diff --git a/Modules/Classification/CLLibSVM/src/svm.cpp b/Modules/Classification/CLLibSVM/src/svm.cpp index 3324e37e38..f20b6e670d 100644 --- a/Modules/Classification/CLLibSVM/src/svm.cpp +++ b/Modules/Classification/CLLibSVM/src/svm.cpp @@ -1,3307 +1,3307 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ /*=================================================================== Copyright (c) 2000-2014 Chih-Chung Chang and Chih-Jen Lin All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither name of copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include "svm.h" int libsvm_version = LIBSVM_VERSION; typedef float Qfloat; typedef signed char schar; #ifndef min template static inline T min(T x,T y) { return (x static inline T max(T x,T y) { return (x>y)?x:y; } #endif template static inline void swap(T& x, T& y) { T t=x; x=y; y=t; } template static inline void clone(T*& dst, S* src, int n) { dst = new T[n]; memcpy((void *)dst,(void *)src,sizeof(T)*n); } static inline double powi(double base, int times) { double tmp = base, ret = 1.0; for(int t=times; t>0; t/=2) { if(t%2==1) ret*=tmp; tmp = tmp * tmp; } return ret; } #define INF HUGE_VAL #define TAU 1e-12 #define Malloc(type,n) (type *)malloc((n)*sizeof(type)) static void print_string_stdout(const char *s) { fputs(s,stdout); fflush(stdout); } static void (*svm_print_string) (const char *) = &print_string_stdout; #if 1 static void info(const char *fmt,...) { char buf[BUFSIZ]; va_list ap; va_start(ap,fmt); vsprintf(buf,fmt,ap); va_end(ap); (*svm_print_string)(buf); } #else static void info(const char *fmt,...) {} #endif // // Kernel Cache // // l is the number of total data items // size is the cache size limit in bytes // class Cache { public: Cache(int l,long int size); ~Cache(); // request data [0,len) // return some position p where [p,len) need to be filled // (p >= len if nothing needs to be filled) int get_data(const int index, Qfloat **data, int len); void swap_index(int i, int j); private: int l; long int size; struct head_t { head_t *prev, *next; // a circular list Qfloat *data; int len; // data[0,len) is cached in this entry }; head_t *head; head_t lru_head; void lru_delete(head_t *h); void lru_insert(head_t *h); }; Cache::Cache(int l_,long int size_):l(l_),size(size_) { head = (head_t *)calloc(l,sizeof(head_t)); // initialized to 0 size /= sizeof(Qfloat); size -= l * sizeof(head_t) / sizeof(Qfloat); size = max(size, 2 * (long int) l); // cache must be large enough for two columns lru_head.next = lru_head.prev = &lru_head; } Cache::~Cache() { for(head_t *h = lru_head.next; h != &lru_head; h=h->next) free(h->data); free(head); } void Cache::lru_delete(head_t *h) { // delete from current location h->prev->next = h->next; h->next->prev = h->prev; } void Cache::lru_insert(head_t *h) { // insert to last position h->next = &lru_head; h->prev = lru_head.prev; h->prev->next = h; h->next->prev = h; } int Cache::get_data(const int index, Qfloat **data, int len) { head_t *h = &head[index]; if(h->len) lru_delete(h); int more = len - h->len; if(more > 0) { // free old space while(size < more) { head_t *old = lru_head.next; lru_delete(old); free(old->data); size += old->len; - old->data = 0; + old->data = nullptr; old->len = 0; } // allocate new space h->data = (Qfloat *)realloc(h->data,sizeof(Qfloat)*len); size -= more; swap(h->len,len); } lru_insert(h); *data = h->data; return len; } void Cache::swap_index(int i, int j) { if(i==j) return; if(head[i].len) lru_delete(&head[i]); if(head[j].len) lru_delete(&head[j]); swap(head[i].data,head[j].data); swap(head[i].len,head[j].len); if(head[i].len) lru_insert(&head[i]); if(head[j].len) lru_insert(&head[j]); if(i>j) swap(i,j); for(head_t *h = lru_head.next; h!=&lru_head; h=h->next) { if(h->len > i) { if(h->len > j) swap(h->data[i],h->data[j]); else { // give up lru_delete(h); free(h->data); size += h->len; - h->data = 0; + h->data = nullptr; h->len = 0; } } } } // // Kernel evaluation // // the static method k_function is for doing single kernel evaluation // the constructor of Kernel prepares to calculate the l*l kernel matrix // the member function get_Q is for getting one column from the Q Matrix // class QMatrix { public: virtual Qfloat *get_Q(int column, int len) const = 0; virtual double *get_QD() const = 0; virtual void swap_index(int i, int j) const = 0; virtual ~QMatrix() {} }; class Kernel: public QMatrix { public: Kernel(int l, svm_node * const * x, const svm_parameter& param); ~Kernel() override; static double k_function(const svm_node *x, const svm_node *y, const svm_parameter& param); Qfloat *get_Q(int column, int len) const override = 0; double *get_QD() const override = 0; void swap_index(int i, int j) const override // no so const... { swap(x[i],x[j]); if(x_square) swap(x_square[i],x_square[j]); } protected: double (Kernel::*kernel_function)(int i, int j) const; private: const svm_node **x; double *x_square; // svm_parameter const int kernel_type; const int degree; const double gamma; const double coef0; static double dot(const svm_node *px, const svm_node *py); double kernel_linear(int i, int j) const { return dot(x[i],x[j]); } double kernel_poly(int i, int j) const { return powi(gamma*dot(x[i],x[j])+coef0,degree); } double kernel_rbf(int i, int j) const { return exp(-gamma*(x_square[i]+x_square[j]-2*dot(x[i],x[j]))); } double kernel_sigmoid(int i, int j) const { return tanh(gamma*dot(x[i],x[j])+coef0); } double kernel_precomputed(int i, int j) const { return x[i][(int)(x[j][0].value)].value; } }; Kernel::Kernel(int l, svm_node * const * x_, const svm_parameter& param) :kernel_type(param.kernel_type), degree(param.degree), gamma(param.gamma), coef0(param.coef0) { switch(kernel_type) { case LINEAR: kernel_function = &Kernel::kernel_linear; break; case POLY: kernel_function = &Kernel::kernel_poly; break; case RBF: kernel_function = &Kernel::kernel_rbf; break; case SIGMOID: kernel_function = &Kernel::kernel_sigmoid; break; case PRECOMPUTED: kernel_function = &Kernel::kernel_precomputed; break; } clone(x,x_,l); if(kernel_type == RBF) { x_square = new double[l]; for(int i=0;iindex != -1 && py->index != -1) { if(px->index == py->index) { sum += px->value * py->value; ++px; ++py; } else { if(px->index > py->index) ++py; else ++px; } } return sum; } double Kernel::k_function(const svm_node *x, const svm_node *y, const svm_parameter& param) { switch(param.kernel_type) { case LINEAR: return dot(x,y); case POLY: return powi(param.gamma*dot(x,y)+param.coef0,param.degree); case RBF: { double sum = 0; while(x->index != -1 && y->index !=-1) { if(x->index == y->index) { double d = x->value - y->value; sum += d*d; ++x; ++y; } else { if(x->index > y->index) { sum += y->value * y->value; ++y; } else { sum += x->value * x->value; ++x; } } } while(x->index != -1) { sum += x->value * x->value; ++x; } while(y->index != -1) { sum += y->value * y->value; ++y; } return exp(-param.gamma*sum); } case SIGMOID: return tanh(param.gamma*dot(x,y)+param.coef0); case PRECOMPUTED: //x: test (validation), y: SV return x[(int)(y->value)].value; default: return 0; // Unreachable } } // An SMO algorithm in Fan et al., JMLR 6(2005), p. 1889--1918 // Solves: // // min 0.5(\alpha^T Q \alpha) + p^T \alpha // // y^T \alpha = \delta // y_i = +1 or -1 // 0 <= alpha_i <= Cp for y_i = 1 // 0 <= alpha_i <= Cn for y_i = -1 // // Given: // // Q, p, y, Cp, Cn, and an initial feasible point \alpha // l is the size of vectors and matrices // eps is the stopping tolerance // // solution will be put in \alpha, objective value will be put in obj // class Solver { public: Solver() {}; virtual ~Solver() {}; struct SolutionInfo { double obj; double rho; double *upper_bound; double r; // for Solver_NU }; void Solve(int l, const QMatrix& Q, const double *p_, const schar *y_, double *alpha_, const double* C_, double eps, SolutionInfo* si, int shrinking); protected: int active_size; schar *y; double *G; // gradient of objective function enum { LOWER_BOUND, UPPER_BOUND, FREE }; char *alpha_status; // LOWER_BOUND, UPPER_BOUND, FREE double *alpha; const QMatrix *Q; const double *QD; double eps; double Cp,Cn; double *C; double *p; int *active_set; double *G_bar; // gradient, if we treat free variables as 0 int l; bool unshrink; // XXX double get_C(int i) { return C[i]; } void update_alpha_status(int i) { if(alpha[i] >= get_C(i)) alpha_status[i] = UPPER_BOUND; else if(alpha[i] <= 0) alpha_status[i] = LOWER_BOUND; else alpha_status[i] = FREE; } bool is_upper_bound(int i) { return alpha_status[i] == UPPER_BOUND; } bool is_lower_bound(int i) { return alpha_status[i] == LOWER_BOUND; } bool is_free(int i) { return alpha_status[i] == FREE; } void swap_index(int i, int j); void reconstruct_gradient(); virtual int select_working_set(int &i, int &j); virtual double calculate_rho(); virtual void do_shrinking(); private: bool be_shrunk(int i, double Gmax1, double Gmax2); }; void Solver::swap_index(int i, int j) { Q->swap_index(i,j); swap(y[i],y[j]); swap(G[i],G[j]); swap(alpha_status[i],alpha_status[j]); swap(alpha[i],alpha[j]); swap(p[i],p[j]); swap(active_set[i],active_set[j]); swap(G_bar[i],G_bar[j]); swap(C[i],C[j]); } void Solver::reconstruct_gradient() { // reconstruct inactive elements of G from G_bar and free variables if(active_size == l) return; int i,j; int nr_free = 0; for(j=active_size;j 2*active_size*(l-active_size)) { for(i=active_size;iget_Q(i,active_size); for(j=0;jget_Q(i,l); double alpha_i = alpha[i]; for(j=active_size;jl = l; this->Q = &Q; QD=Q.get_QD(); clone(p, p_,l); clone(y, y_,l); clone(alpha,alpha_,l); clone(C,C_,l); this->eps = eps; unshrink = false; // initialize alpha_status { alpha_status = new char[l]; for(int i=0;iINT_MAX/100 ? INT_MAX : 100*l); int counter = min(l,1000)+1; while(iter < max_iter) { // show progress and do shrinking if(--counter == 0) { counter = min(l,1000); if(shrinking) do_shrinking(); info("."); } int i,j; if(select_working_set(i,j)!=0) { // reconstruct the whole gradient reconstruct_gradient(); // reset active set size and check active_size = l; info("*"); if(select_working_set(i,j)!=0) break; else counter = 1; // do shrinking next iteration } ++iter; // update alpha[i] and alpha[j], handle bounds carefully const Qfloat *Q_i = Q.get_Q(i,active_size); const Qfloat *Q_j = Q.get_Q(j,active_size); double C_i = get_C(i); double C_j = get_C(j); double old_alpha_i = alpha[i]; double old_alpha_j = alpha[j]; if(y[i]!=y[j]) { double quad_coef = QD[i]+QD[j]+2*Q_i[j]; if (quad_coef <= 0) quad_coef = TAU; double delta = (-G[i]-G[j])/quad_coef; double diff = alpha[i] - alpha[j]; alpha[i] += delta; alpha[j] += delta; if(diff > 0) { if(alpha[j] < 0) { alpha[j] = 0; alpha[i] = diff; } } else { if(alpha[i] < 0) { alpha[i] = 0; alpha[j] = -diff; } } if(diff > C_i - C_j) { if(alpha[i] > C_i) { alpha[i] = C_i; alpha[j] = C_i - diff; } } else { if(alpha[j] > C_j) { alpha[j] = C_j; alpha[i] = C_j + diff; } } } else { double quad_coef = QD[i]+QD[j]-2*Q_i[j]; if (quad_coef <= 0) quad_coef = TAU; double delta = (G[i]-G[j])/quad_coef; double sum = alpha[i] + alpha[j]; alpha[i] -= delta; alpha[j] += delta; if(sum > C_i) { if(alpha[i] > C_i) { alpha[i] = C_i; alpha[j] = sum - C_i; } } else { if(alpha[j] < 0) { alpha[j] = 0; alpha[i] = sum; } } if(sum > C_j) { if(alpha[j] > C_j) { alpha[j] = C_j; alpha[i] = sum - C_j; } } else { if(alpha[i] < 0) { alpha[i] = 0; alpha[j] = sum; } } } // update G double delta_alpha_i = alpha[i] - old_alpha_i; double delta_alpha_j = alpha[j] - old_alpha_j; for(int k=0;k= max_iter) { if(active_size < l) { // reconstruct the whole gradient to calculate objective value reconstruct_gradient(); active_size = l; info("*"); } fprintf(stderr,"\nWARNING: reaching max number of iterations\n"); } // calculate rho si->rho = calculate_rho(); // calculate objective value { double v = 0; int i; for(i=0;iobj = v/2; } // put back the solution { for(int i=0;iupper_bound[i] = C[i]; info("\noptimization finished, #iter = %d\n",iter); delete[] p; delete[] y; delete[] C; delete[] alpha; delete[] alpha_status; delete[] active_set; delete[] G; delete[] G_bar; } // return 1 if already optimal, return 0 otherwise int Solver::select_working_set(int &out_i, int &out_j) { // return i,j such that // i: maximizes -y_i * grad(f)_i, i in I_up(\alpha) // j: minimizes the decrease of obj value // (if quadratic coefficeint <= 0, replace it with tau) // -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha) double Gmax = -INF; double Gmax2 = -INF; int Gmax_idx = -1; int Gmin_idx = -1; double obj_diff_min = INF; for(int t=0;t= Gmax) { Gmax = -G[t]; Gmax_idx = t; } } else { if(!is_lower_bound(t)) if(G[t] >= Gmax) { Gmax = G[t]; Gmax_idx = t; } } int i = Gmax_idx; const Qfloat *Q_i = nullptr; if(i != -1) // nullptr Q_i not accessed: Gmax=-INF if i=-1 Q_i = Q->get_Q(i,active_size); for(int j=0;j= Gmax2) Gmax2 = G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[i]+QD[j]-2.0*y[i]*Q_i[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } else { if (!is_upper_bound(j)) { double grad_diff= Gmax-G[j]; if (-G[j] >= Gmax2) Gmax2 = -G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[i]+QD[j]+2.0*y[i]*Q_i[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } } if(Gmax+Gmax2 < eps) return 1; out_i = Gmax_idx; out_j = Gmin_idx; return 0; } bool Solver::be_shrunk(int i, double Gmax1, double Gmax2) { if(is_upper_bound(i)) { if(y[i]==+1) return(-G[i] > Gmax1); else return(-G[i] > Gmax2); } else if(is_lower_bound(i)) { if(y[i]==+1) return(G[i] > Gmax2); else return(G[i] > Gmax1); } else return(false); } void Solver::do_shrinking() { int i; double Gmax1 = -INF; // max { -y_i * grad(f)_i | i in I_up(\alpha) } double Gmax2 = -INF; // max { y_i * grad(f)_i | i in I_low(\alpha) } // find maximal violating pair first for(i=0;i= Gmax1) Gmax1 = -G[i]; } if(!is_lower_bound(i)) { if(G[i] >= Gmax2) Gmax2 = G[i]; } } else { if(!is_upper_bound(i)) { if(-G[i] >= Gmax2) Gmax2 = -G[i]; } if(!is_lower_bound(i)) { if(G[i] >= Gmax1) Gmax1 = G[i]; } } } if(unshrink == false && Gmax1 + Gmax2 <= eps*10) { unshrink = true; reconstruct_gradient(); active_size = l; info("*"); } for(i=0;i i) { if (!be_shrunk(active_size, Gmax1, Gmax2)) { swap_index(i,active_size); break; } active_size--; } } } double Solver::calculate_rho() { double r; int nr_free = 0; double ub = INF, lb = -INF, sum_free = 0; for(int i=0;i0) r = sum_free/nr_free; else r = (ub+lb)/2; return r; } // // Solver for nu-svm classification and regression // // additional constraint: e^T \alpha = constant // class Solver_NU: public Solver { public: Solver_NU() {} void Solve(int l, const QMatrix& Q, const double *p, const schar *y, double *alpha, double* C_, double eps, SolutionInfo* si, int shrinking) { this->si = si; Solver::Solve(l,Q,p,y,alpha,C_,eps,si,shrinking); } private: SolutionInfo *si; int select_working_set(int &i, int &j) override; double calculate_rho() override; bool be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4); void do_shrinking() override; }; // return 1 if already optimal, return 0 otherwise int Solver_NU::select_working_set(int &out_i, int &out_j) { // return i,j such that y_i = y_j and // i: maximizes -y_i * grad(f)_i, i in I_up(\alpha) // j: minimizes the decrease of obj value // (if quadratic coefficeint <= 0, replace it with tau) // -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha) double Gmaxp = -INF; double Gmaxp2 = -INF; int Gmaxp_idx = -1; double Gmaxn = -INF; double Gmaxn2 = -INF; int Gmaxn_idx = -1; int Gmin_idx = -1; double obj_diff_min = INF; for(int t=0;t= Gmaxp) { Gmaxp = -G[t]; Gmaxp_idx = t; } } else { if(!is_lower_bound(t)) if(G[t] >= Gmaxn) { Gmaxn = G[t]; Gmaxn_idx = t; } } int ip = Gmaxp_idx; int in = Gmaxn_idx; const Qfloat *Q_ip = nullptr; const Qfloat *Q_in = nullptr; if(ip != -1) // nullptr Q_ip not accessed: Gmaxp=-INF if ip=-1 Q_ip = Q->get_Q(ip,active_size); if(in != -1) Q_in = Q->get_Q(in,active_size); for(int j=0;j= Gmaxp2) Gmaxp2 = G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[ip]+QD[j]-2*Q_ip[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } else { if (!is_upper_bound(j)) { double grad_diff=Gmaxn-G[j]; if (-G[j] >= Gmaxn2) Gmaxn2 = -G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[in]+QD[j]-2*Q_in[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } } if(max(Gmaxp+Gmaxp2,Gmaxn+Gmaxn2) < eps) return 1; if (y[Gmin_idx] == +1) out_i = Gmaxp_idx; else out_i = Gmaxn_idx; out_j = Gmin_idx; return 0; } bool Solver_NU::be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4) { if(is_upper_bound(i)) { if(y[i]==+1) return(-G[i] > Gmax1); else return(-G[i] > Gmax4); } else if(is_lower_bound(i)) { if(y[i]==+1) return(G[i] > Gmax2); else return(G[i] > Gmax3); } else return(false); } void Solver_NU::do_shrinking() { double Gmax1 = -INF; // max { -y_i * grad(f)_i | y_i = +1, i in I_up(\alpha) } double Gmax2 = -INF; // max { y_i * grad(f)_i | y_i = +1, i in I_low(\alpha) } double Gmax3 = -INF; // max { -y_i * grad(f)_i | y_i = -1, i in I_up(\alpha) } double Gmax4 = -INF; // max { y_i * grad(f)_i | y_i = -1, i in I_low(\alpha) } // find maximal violating pair first int i; for(i=0;i Gmax1) Gmax1 = -G[i]; } else if(-G[i] > Gmax4) Gmax4 = -G[i]; } if(!is_lower_bound(i)) { if(y[i]==+1) { if(G[i] > Gmax2) Gmax2 = G[i]; } else if(G[i] > Gmax3) Gmax3 = G[i]; } } if(unshrink == false && max(Gmax1+Gmax2,Gmax3+Gmax4) <= eps*10) { unshrink = true; reconstruct_gradient(); active_size = l; } for(i=0;i i) { if (!be_shrunk(active_size, Gmax1, Gmax2, Gmax3, Gmax4)) { swap_index(i,active_size); break; } active_size--; } } } double Solver_NU::calculate_rho() { int nr_free1 = 0,nr_free2 = 0; double ub1 = INF, ub2 = INF; double lb1 = -INF, lb2 = -INF; double sum_free1 = 0, sum_free2 = 0; for(int i=0;i 0) r1 = sum_free1/nr_free1; else r1 = (ub1+lb1)/2; if(nr_free2 > 0) r2 = sum_free2/nr_free2; else r2 = (ub2+lb2)/2; si->r = (r1+r2)/2; return (r1-r2)/2; } // // Q matrices for various formulations // class SVC_Q: public Kernel { public: SVC_Q(const svm_problem& prob, const svm_parameter& param, const schar *y_) :Kernel(prob.l, prob.x, param) { clone(y,y_,prob.l); cache = new Cache(prob.l,(long int)(param.cache_size*(1<<20))); QD = new double[prob.l]; for(int i=0;i*kernel_function)(i,i); } Qfloat *get_Q(int i, int len) const override { Qfloat *data; int start, j; if((start = cache->get_data(i,&data,len)) < len) { for(j=start;j*kernel_function)(i,j)); } return data; } double *get_QD() const override { return QD; } void swap_index(int i, int j) const override { cache->swap_index(i,j); Kernel::swap_index(i,j); swap(y[i],y[j]); swap(QD[i],QD[j]); } ~SVC_Q() override { delete[] y; delete cache; delete[] QD; } private: schar *y; Cache *cache; double *QD; }; class ONE_CLASS_Q: public Kernel { public: ONE_CLASS_Q(const svm_problem& prob, const svm_parameter& param) :Kernel(prob.l, prob.x, param) { cache = new Cache(prob.l,(long int)(param.cache_size*(1<<20))); QD = new double[prob.l]; for(int i=0;i*kernel_function)(i,i); } Qfloat *get_Q(int i, int len) const override { Qfloat *data; int start, j; if((start = cache->get_data(i,&data,len)) < len) { for(j=start;j*kernel_function)(i,j); } return data; } double *get_QD() const override { return QD; } void swap_index(int i, int j) const override { cache->swap_index(i,j); Kernel::swap_index(i,j); swap(QD[i],QD[j]); } ~ONE_CLASS_Q() override { delete cache; delete[] QD; } private: Cache *cache; double *QD; }; class SVR_Q: public Kernel { public: SVR_Q(const svm_problem& prob, const svm_parameter& param) :Kernel(prob.l, prob.x, param) { l = prob.l; cache = new Cache(l,(long int)(param.cache_size*(1<<20))); QD = new double[2*l]; sign = new schar[2*l]; index = new int[2*l]; for(int k=0;k*kernel_function)(k,k); QD[k+l] = QD[k]; } buffer[0] = new Qfloat[2*l]; buffer[1] = new Qfloat[2*l]; next_buffer = 0; } void swap_index(int i, int j) const override { swap(sign[i],sign[j]); swap(index[i],index[j]); swap(QD[i],QD[j]); } Qfloat *get_Q(int i, int len) const override { Qfloat *data; int j, real_i = index[i]; if(cache->get_data(real_i,&data,l) < l) { for(j=0;j*kernel_function)(real_i,j); } // reorder and copy Qfloat *buf = buffer[next_buffer]; next_buffer = 1 - next_buffer; schar si = sign[i]; for(j=0;jl; double *minus_ones = new double[l]; schar *y = new schar[l]; double *C = new double[l]; int i; for(i=0;iy[i] > 0) { y[i] = +1; C[i] = prob->W[i]*Cp; } else { y[i] = -1; C[i] = prob->W[i]*Cn; } } Solver s; s.Solve(l, SVC_Q(*prob,*param,y), minus_ones, y, alpha, C, param->eps, si, param->shrinking); /* double sum_alpha=0; for(i=0;il)); */ for(i=0;il; double nu = param->nu; schar *y = new schar[l]; double *C = new double[l]; for(i=0;iy[i]>0) y[i] = +1; else y[i] = -1; C[i] = prob->W[i]; } double nu_l = 0; for(i=0;ieps, si, param->shrinking); double r = si->r; info("C = %f\n",1/r); for(i=0;iupper_bound[i] /= r; } si->rho /= r; si->obj /= (r*r); delete[] C; delete[] y; delete[] zeros; } static void solve_one_class( const svm_problem *prob, const svm_parameter *param, double *alpha, Solver::SolutionInfo* si) { int l = prob->l; double *zeros = new double[l]; schar *ones = new schar[l]; double *C = new double[l]; int i; double nu_l = 0; for(i=0;iW[i]; nu_l += C[i] * param->nu; } i = 0; while(nu_l > 0) { alpha[i] = min(C[i],nu_l); nu_l -= alpha[i]; ++i; } for(;ieps, si, param->shrinking); delete[] C; delete[] zeros; delete[] ones; } static void solve_epsilon_svr( const svm_problem *prob, const svm_parameter *param, double *alpha, Solver::SolutionInfo* si) { int l = prob->l; double *alpha2 = new double[2*l]; double *linear_term = new double[2*l]; double *C = new double[2*l]; schar *y = new schar[2*l]; int i; for(i=0;ip - prob->y[i]; y[i] = 1; C[i] = prob->W[i]*param->C; alpha2[i+l] = 0; linear_term[i+l] = param->p + prob->y[i]; y[i+l] = -1; C[i+l] = prob->W[i]*param->C; } Solver s; s.Solve(2*l, SVR_Q(*prob,*param), linear_term, y, alpha2, C, param->eps, si, param->shrinking); double sum_alpha = 0; for(i=0;iC*l)); delete[] alpha2; delete[] linear_term; delete[] C; delete[] y; } static void solve_nu_svr( const svm_problem *prob, const svm_parameter *param, double *alpha, Solver::SolutionInfo* si) { int l = prob->l; double *C = new double[2*l]; double *alpha2 = new double[2*l]; double *linear_term = new double[2*l]; schar *y = new schar[2*l]; int i; double sum = 0; for(i=0;iW[i]*param->C; sum += C[i] * param->nu; } sum /= 2; for(i=0;iy[i]; y[i] = 1; linear_term[i+l] = prob->y[i]; y[i+l] = -1; } Solver_NU s; s.Solve(2*l, SVR_Q(*prob,*param), linear_term, y, alpha2, C, param->eps, si, param->shrinking); info("epsilon = %f\n",-si->r); for(i=0;il); Solver::SolutionInfo si; switch(param->svm_type) { case C_SVC: si.upper_bound = Malloc(double,prob->l); solve_c_svc(prob,param,alpha,&si,Cp,Cn); break; case NU_SVC: si.upper_bound = Malloc(double,prob->l); solve_nu_svc(prob,param,alpha,&si); break; case ONE_CLASS: si.upper_bound = Malloc(double,prob->l); solve_one_class(prob,param,alpha,&si); break; case EPSILON_SVR: si.upper_bound = Malloc(double,2*prob->l); solve_epsilon_svr(prob,param,alpha,&si); break; case NU_SVR: si.upper_bound = Malloc(double,2*prob->l); solve_nu_svr(prob,param,alpha,&si); break; } info("obj = %f, rho = %f\n",si.obj,si.rho); // output SVs int nSV = 0; int nBSV = 0; for(int i=0;il;i++) { if(fabs(alpha[i]) > 0) { ++nSV; if(prob->y[i] > 0) { if(fabs(alpha[i]) >= si.upper_bound[i]) ++nBSV; } else { if(fabs(alpha[i]) >= si.upper_bound[i]) ++nBSV; } } } free(si.upper_bound); info("nSV = %d, nBSV = %d\n",nSV,nBSV); decision_function f; f.alpha = alpha; f.rho = si.rho; return f; } // Platt's binary SVM Probablistic Output: an improvement from Lin et al. static void sigmoid_train( int l, const double *dec_values, const double *labels, double& A, double& B) { double prior1=0, prior0 = 0; int i; for (i=0;i 0) prior1+=1; else prior0+=1; int max_iter=100; // Maximal number of iterations double min_step=1e-10; // Minimal step taken in line search double sigma=1e-12; // For numerically strict PD of Hessian double eps=1e-5; double hiTarget=(prior1+1.0)/(prior1+2.0); double loTarget=1/(prior0+2.0); double *t=Malloc(double,l); double fApB,p,q,h11,h22,h21,g1,g2,det,dA,dB,gd,stepsize; double newA,newB,newf,d1,d2; int iter; // Initial Point and Initial Fun Value A=0.0; B=log((prior0+1.0)/(prior1+1.0)); double fval = 0.0; for (i=0;i0) t[i]=hiTarget; else t[i]=loTarget; fApB = dec_values[i]*A+B; if (fApB>=0) fval += t[i]*fApB + log(1+exp(-fApB)); else fval += (t[i] - 1)*fApB +log(1+exp(fApB)); } for (iter=0;iter= 0) { p=exp(-fApB)/(1.0+exp(-fApB)); q=1.0/(1.0+exp(-fApB)); } else { p=1.0/(1.0+exp(fApB)); q=exp(fApB)/(1.0+exp(fApB)); } d2=p*q; h11+=dec_values[i]*dec_values[i]*d2; h22+=d2; h21+=dec_values[i]*d2; d1=t[i]-p; g1+=dec_values[i]*d1; g2+=d1; } // Stopping Criteria if (fabs(g1)= min_step) { newA = A + stepsize * dA; newB = B + stepsize * dB; // New function value newf = 0.0; for (i=0;i= 0) newf += t[i]*fApB + log(1+exp(-fApB)); else newf += (t[i] - 1)*fApB +log(1+exp(fApB)); } // Check sufficient decrease if (newf=max_iter) info("Reaching maximal iterations in two-class probability estimates\n"); free(t); } static double sigmoid_predict(double decision_value, double A, double B) { double fApB = decision_value*A+B; // 1-p used later; avoid catastrophic cancellation if (fApB >= 0) return exp(-fApB)/(1.0+exp(-fApB)); else return 1.0/(1+exp(fApB)) ; } // Method 2 from the multiclass_prob paper by Wu, Lin, and Weng static void multiclass_probability(int k, double **r, double *p) { int t,j; int iter = 0, max_iter=max(100,k); double **Q=Malloc(double *,k); double *Qp=Malloc(double,k); double pQp, eps=0.005/k; for (t=0;tmax_error) max_error=error; } if (max_error=max_iter) info("Exceeds max_iter in multiclass_prob\n"); for(t=0;tl); double *dec_values = Malloc(double,prob->l); // random shuffle for(i=0;il;i++) perm[i]=i; for(i=0;il;i++) { int j = i+rand()%(prob->l-i); swap(perm[i],perm[j]); } for(i=0;il/nr_fold; int end = (i+1)*prob->l/nr_fold; int j,k; struct svm_problem subprob; subprob.l = prob->l-(end-begin); subprob.x = Malloc(struct svm_node*,subprob.l); subprob.y = Malloc(double,subprob.l); subprob.W = Malloc(double,subprob.l); k=0; for(j=0;jx[perm[j]]; subprob.y[k] = prob->y[perm[j]]; subprob.W[k] = prob->W[perm[j]]; ++k; } for(j=end;jl;j++) { subprob.x[k] = prob->x[perm[j]]; subprob.y[k] = prob->y[perm[j]]; subprob.W[k] = prob->W[perm[j]]; ++k; } int p_count=0,n_count=0; for(j=0;j0) p_count++; else n_count++; if(p_count==0 && n_count==0) for(j=begin;j 0 && n_count == 0) for(j=begin;j 0) for(j=begin;jx[perm[j]],&(dec_values[perm[j]])); // ensure +1 -1 order; reason not using CV subroutine dec_values[perm[j]] *= submodel->label[0]; } svm_free_and_destroy_model(&submodel); svm_destroy_param(&subparam); } free(subprob.x); free(subprob.y); free(subprob.W); } sigmoid_train(prob->l,dec_values,prob->y,probA,probB); free(dec_values); free(perm); } // Return parameter of a Laplace distribution static double svm_svr_probability( const svm_problem *prob, const svm_parameter *param) { int i; int nr_fold = 5; double *ymv = Malloc(double,prob->l); double mae = 0; svm_parameter newparam = *param; newparam.probability = 0; svm_cross_validation(prob,&newparam,nr_fold,ymv); for(i=0;il;i++) { ymv[i]=prob->y[i]-ymv[i]; mae += fabs(ymv[i]); } mae /= prob->l; double std=sqrt(2*mae*mae); int count=0; mae=0; for(i=0;il;i++) if (fabs(ymv[i]) > 5*std) count=count+1; else mae+=fabs(ymv[i]); mae /= (prob->l-count); info("Prob. model for test data: target value = predicted value + z,\nz: Laplace distribution e^(-|z|/sigma)/(2sigma),sigma= %g\n",mae); free(ymv); return mae; } // label: label name, start: begin of each class, count: #data of classes, perm: indices to the original data // perm, length l, must be allocated before calling this subroutine static void svm_group_classes(const svm_problem *prob, int *nr_class_ret, int **label_ret, int **start_ret, int **count_ret, int *perm) { int l = prob->l; int max_nr_class = 16; int nr_class = 0; int *label = Malloc(int,max_nr_class); int *count = Malloc(int,max_nr_class); int *data_label = Malloc(int,l); int i; for(i=0;iy[i]; int j; for(j=0;j 0. // static void remove_zero_weight(svm_problem *newprob, const svm_problem *prob) { int i; int l = 0; for(i=0;il;i++) if(prob->W[i] > 0) l++; *newprob = *prob; newprob->l = l; newprob->x = Malloc(svm_node*,l); newprob->y = Malloc(double,l); newprob->W = Malloc(double,l); int j = 0; for(i=0;il;i++) if(prob->W[i] > 0) { newprob->x[j] = prob->x[i]; newprob->y[j] = prob->y[i]; newprob->W[j] = prob->W[i]; j++; } } // // Interface functions // svm_model *svm_train(const svm_problem *prob, const svm_parameter *param) { svm_problem newprob; remove_zero_weight(&newprob, prob); prob = &newprob; svm_model *model = Malloc(svm_model,1); model->param = *param; model->free_sv = 0; // XXX if(param->svm_type == ONE_CLASS || param->svm_type == EPSILON_SVR || param->svm_type == NU_SVR) { // regression or one-class-svm model->nr_class = 2; model->label = nullptr; model->nSV = nullptr; model->probA = nullptr; model->probB = nullptr; model->sv_coef = Malloc(double *,1); if(param->probability && (param->svm_type == EPSILON_SVR || param->svm_type == NU_SVR)) { model->probA = Malloc(double,1); model->probA[0] = svm_svr_probability(prob,param); } decision_function f = svm_train_one(prob,param,0,0); model->rho = Malloc(double,1); model->rho[0] = f.rho; int nSV = 0; int i; for(i=0;il;i++) if(fabs(f.alpha[i]) > 0) ++nSV; model->l = nSV; model->SV = Malloc(svm_node *,nSV); model->sv_coef[0] = Malloc(double,nSV); model->sv_indices = Malloc(int,nSV); int j = 0; for(i=0;il;i++) if(fabs(f.alpha[i]) > 0) { model->SV[j] = prob->x[i]; model->sv_coef[0][j] = f.alpha[i]; model->sv_indices[j] = i+1; ++j; } free(f.alpha); } else { // classification int l = prob->l; int nr_class; int *label = nullptr; int *start = nullptr; int *count = nullptr; int *perm = Malloc(int,l); // group training data of the same class svm_group_classes(prob,&nr_class,&label,&start,&count,perm); if(nr_class == 1) info("WARNING: training data in only one class. See README for details.\n"); svm_node **x = Malloc(svm_node *,l); double *W; W = Malloc(double,l); int i; for(i=0;ix[perm[i]]; W[i] = prob->W[perm[i]]; } // calculate weighted C double *weighted_C = Malloc(double, nr_class); for(i=0;iC; for(i=0;inr_weight;i++) { int j; for(j=0;jweight_label[i] == label[j]) break; if(j == nr_class) fprintf(stderr,"WARNING: class label %d specified in weight is not found\n", param->weight_label[i]); else weighted_C[j] *= param->weight[i]; } // train k*(k-1)/2 models bool *nonzero = Malloc(bool,l); for(i=0;iprobability) { probA=Malloc(double,nr_class*(nr_class-1)/2); probB=Malloc(double,nr_class*(nr_class-1)/2); } int p = 0; for(i=0;iprobability) svm_binary_svc_probability(&sub_prob,param,weighted_C[i],weighted_C[j],probA[p],probB[p]); f[p] = svm_train_one(&sub_prob,param,weighted_C[i],weighted_C[j]); for(k=0;k 0) nonzero[si+k] = true; for(k=0;k 0) nonzero[sj+k] = true; free(sub_prob.x); free(sub_prob.y); free(sub_prob.W); ++p; } // build output model->nr_class = nr_class; model->label = Malloc(int,nr_class); for(i=0;ilabel[i] = label[i]; model->rho = Malloc(double,nr_class*(nr_class-1)/2); for(i=0;irho[i] = f[i].rho; if(param->probability) { model->probA = Malloc(double,nr_class*(nr_class-1)/2); model->probB = Malloc(double,nr_class*(nr_class-1)/2); for(i=0;iprobA[i] = probA[i]; model->probB[i] = probB[i]; } } else { model->probA=nullptr; model->probB=nullptr; } int total_sv = 0; int *nz_count = Malloc(int,nr_class); model->nSV = Malloc(int,nr_class); for(i=0;inSV[i] = nSV; nz_count[i] = nSV; } info("Total nSV = %d\n",total_sv); model->l = total_sv; model->SV = Malloc(svm_node *,total_sv); model->sv_indices = Malloc(int,total_sv); p = 0; for(i=0;iSV[p] = x[i]; model->sv_indices[p++] = perm[i] + 1; } int *nz_start = Malloc(int,nr_class); nz_start[0] = 0; for(i=1;isv_coef = Malloc(double *,nr_class-1); for(i=0;isv_coef[i] = Malloc(double,total_sv); p = 0; for(i=0;isv_coef[j-1][q++] = f[p].alpha[k]; q = nz_start[j]; for(k=0;ksv_coef[i][q++] = f[p].alpha[ci+k]; ++p; } free(label); free(probA); free(probB); free(count); free(perm); free(start); free(W); free(x); free(weighted_C); free(nonzero); for(i=0;il; int *perm = Malloc(int,l); int nr_class; if (nr_fold > l) { nr_fold = l; fprintf(stderr,"WARNING: # folds > # data. Will use # folds = # data instead (i.e., leave-one-out cross validation)\n"); } fold_start = Malloc(int,nr_fold+1); // stratified cv may not give leave-one-out rate // Each class to l folds -> some folds may have zero elements if((param->svm_type == C_SVC || param->svm_type == NU_SVC) && nr_fold < l) { int *start = nullptr; int *label = nullptr; int *count = nullptr; svm_group_classes(prob,&nr_class,&label,&start,&count,perm); // random shuffle and then data grouped by fold using the array perm int *fold_count = Malloc(int,nr_fold); int c; int *index = Malloc(int,l); for(i=0;ix[perm[j]]; subprob.y[k] = prob->y[perm[j]]; subprob.W[k] = prob->W[perm[j]]; ++k; } for(j=end;jx[perm[j]]; subprob.y[k] = prob->y[perm[j]]; subprob.W[k] = prob->W[perm[j]]; ++k; } struct svm_model *submodel = svm_train(&subprob,param); if(param->probability && (param->svm_type == C_SVC || param->svm_type == NU_SVC)) { double *prob_estimates=Malloc(double,svm_get_nr_class(submodel)); for(j=begin;jx[perm[j]],prob_estimates); free(prob_estimates); } else for(j=begin;jx[perm[j]]); svm_free_and_destroy_model(&submodel); free(subprob.x); free(subprob.y); free(subprob.W); } free(fold_start); free(perm); } int svm_get_svm_type(const svm_model *model) { return model->param.svm_type; } int svm_get_nr_class(const svm_model *model) { return model->nr_class; } void svm_get_labels(const svm_model *model, int* label) { if (model->label != nullptr) for(int i=0;inr_class;i++) label[i] = model->label[i]; } void svm_get_sv_indices(const svm_model *model, int* indices) { if (model->sv_indices != nullptr) for(int i=0;il;i++) indices[i] = model->sv_indices[i]; } int svm_get_nr_sv(const svm_model *model) { return model->l; } double svm_get_svr_probability(const svm_model *model) { if ((model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) && model->probA!=nullptr) return model->probA[0]; else { fprintf(stderr,"Model doesn't contain information for SVR probability inference\n"); return 0; } } double svm_predict_values(const svm_model *model, const svm_node *x, double* dec_values) { int i; if(model->param.svm_type == ONE_CLASS || model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) { double *sv_coef = model->sv_coef[0]; double sum = 0; for(i=0;il;i++) sum += sv_coef[i] * Kernel::k_function(x,model->SV[i],model->param); sum -= model->rho[0]; *dec_values = sum; if(model->param.svm_type == ONE_CLASS) return (sum>0)?1:-1; else return sum; } else { int nr_class = model->nr_class; int l = model->l; double *kvalue = Malloc(double,l); for(i=0;iSV[i],model->param); int *start = Malloc(int,nr_class); start[0] = 0; for(i=1;inSV[i-1]; int *vote = Malloc(int,nr_class); for(i=0;inSV[i]; int cj = model->nSV[j]; int k; double *coef1 = model->sv_coef[j-1]; double *coef2 = model->sv_coef[i]; for(k=0;krho[p]; dec_values[p] = sum; if(dec_values[p] > 0) ++vote[i]; else ++vote[j]; p++; } int vote_max_idx = 0; for(i=1;i vote[vote_max_idx]) vote_max_idx = i; free(kvalue); free(start); free(vote); return model->label[vote_max_idx]; } } double svm_predict(const svm_model *model, const svm_node *x) { int nr_class = model->nr_class; double *dec_values; if(model->param.svm_type == ONE_CLASS || model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) dec_values = Malloc(double, 1); else dec_values = Malloc(double, nr_class*(nr_class-1)/2); double pred_result = svm_predict_values(model, x, dec_values); free(dec_values); return pred_result; } double svm_predict_probability( const svm_model *model, const svm_node *x, double *prob_estimates) { if ((model->param.svm_type == C_SVC || model->param.svm_type == NU_SVC) && model->probA!=nullptr && model->probB!=nullptr) { int i; int nr_class = model->nr_class; double *dec_values = Malloc(double, nr_class*(nr_class-1)/2); svm_predict_values(model, x, dec_values); double min_prob=1e-7; double **pairwise_prob=Malloc(double *,nr_class); for(i=0;iprobA[k],model->probB[k]),min_prob),1-min_prob); pairwise_prob[j][i]=1-pairwise_prob[i][j]; k++; } multiclass_probability(nr_class,pairwise_prob,prob_estimates); int prob_max_idx = 0; for(i=1;i prob_estimates[prob_max_idx]) prob_max_idx = i; for(i=0;ilabel[prob_max_idx]; } else return svm_predict(model, x); } static const char *svm_type_table[] = { "c_svc","nu_svc","one_class","epsilon_svr","nu_svr",nullptr }; static const char *kernel_type_table[]= { "linear","polynomial","rbf","sigmoid","precomputed",nullptr }; int svm_save_model(const char *model_file_name, const svm_model *model) { FILE *fp = fopen(model_file_name,"w"); if(fp==nullptr) return -1; mitk::LocaleSwitch localeSwitch("C"); const svm_parameter& param = model->param; fprintf(fp,"svm_type %s\n", svm_type_table[param.svm_type]); fprintf(fp,"kernel_type %s\n", kernel_type_table[param.kernel_type]); if(param.kernel_type == POLY) fprintf(fp,"degree %d\n", param.degree); if(param.kernel_type == POLY || param.kernel_type == RBF || param.kernel_type == SIGMOID) fprintf(fp,"gamma %g\n", param.gamma); if(param.kernel_type == POLY || param.kernel_type == SIGMOID) fprintf(fp,"coef0 %g\n", param.coef0); int nr_class = model->nr_class; int l = model->l; fprintf(fp, "nr_class %d\n", nr_class); fprintf(fp, "total_sv %d\n",l); { fprintf(fp, "rho"); for(int i=0;irho[i]); fprintf(fp, "\n"); } if(model->label) { fprintf(fp, "label"); for(int i=0;ilabel[i]); fprintf(fp, "\n"); } if(model->probA) // regression has probA only { fprintf(fp, "probA"); for(int i=0;iprobA[i]); fprintf(fp, "\n"); } if(model->probB) { fprintf(fp, "probB"); for(int i=0;iprobB[i]); fprintf(fp, "\n"); } if(model->nSV) { fprintf(fp, "nr_sv"); for(int i=0;inSV[i]); fprintf(fp, "\n"); } fprintf(fp, "SV\n"); const double * const *sv_coef = model->sv_coef; const svm_node * const *SV = model->SV; for(int i=0;ivalue)); else while(p->index != -1) { fprintf(fp,"%d:%.8g ",p->index,p->value); p++; } fprintf(fp, "\n"); } if (ferror(fp) != 0 || fclose(fp) != 0) return -1; else return 0; } static char *line = nullptr; static int max_line_len; static char* readline(FILE *input) { int len; if(fgets(line,max_line_len,input) == nullptr) return nullptr; while(strrchr(line,'\n') == nullptr) { max_line_len *= 2; line = (char *) realloc(line,max_line_len); len = (int) strlen(line); if(fgets(line+len,max_line_len-len,input) == nullptr) break; } return line; } // // FSCANF helps to handle fscanf failures. // Its do-while block avoids the ambiguity when // if (...) // FSCANF(); // is used // #define FSCANF(_stream, _format, _var) do{ if (fscanf(_stream, _format, _var) != 1) return false; }while(0) bool read_model_header(FILE *fp, svm_model* model) { svm_parameter& param = model->param; char cmd[81]; while(1) { FSCANF(fp,"%80s",cmd); if(strcmp(cmd,"svm_type")==0) { FSCANF(fp,"%80s",cmd); int i; for(i=0;svm_type_table[i];i++) { if(strcmp(svm_type_table[i],cmd)==0) { param.svm_type=i; break; } } if(svm_type_table[i] == nullptr) { fprintf(stderr,"unknown svm type.\n"); return false; } } else if(strcmp(cmd,"kernel_type")==0) { FSCANF(fp,"%80s",cmd); int i; for(i=0;kernel_type_table[i];i++) { if(strcmp(kernel_type_table[i],cmd)==0) { param.kernel_type=i; break; } } if(kernel_type_table[i] == nullptr) { fprintf(stderr,"unknown kernel function.\n"); return false; } } else if(strcmp(cmd,"degree")==0) FSCANF(fp,"%d",¶m.degree); else if(strcmp(cmd,"gamma")==0) FSCANF(fp,"%lf",¶m.gamma); else if(strcmp(cmd,"coef0")==0) FSCANF(fp,"%lf",¶m.coef0); else if(strcmp(cmd,"nr_class")==0) FSCANF(fp,"%d",&model->nr_class); else if(strcmp(cmd,"total_sv")==0) FSCANF(fp,"%d",&model->l); else if(strcmp(cmd,"rho")==0) { int n = model->nr_class * (model->nr_class-1)/2; model->rho = Malloc(double,n); for(int i=0;irho[i]); } else if(strcmp(cmd,"label")==0) { int n = model->nr_class; model->label = Malloc(int,n); for(int i=0;ilabel[i]); } else if(strcmp(cmd,"probA")==0) { int n = model->nr_class * (model->nr_class-1)/2; model->probA = Malloc(double,n); for(int i=0;iprobA[i]); } else if(strcmp(cmd,"probB")==0) { int n = model->nr_class * (model->nr_class-1)/2; model->probB = Malloc(double,n); for(int i=0;iprobB[i]); } else if(strcmp(cmd,"nr_sv")==0) { int n = model->nr_class; model->nSV = Malloc(int,n); for(int i=0;inSV[i]); } else if(strcmp(cmd,"SV")==0) { while(1) { int c = getc(fp); if(c==EOF || c=='\n') break; } break; } else { fprintf(stderr,"unknown text in model file: [%s]\n",cmd); return false; } } return true; } svm_model *svm_load_model(const char *model_file_name) { FILE *fp = fopen(model_file_name,"rb"); if(fp==nullptr) return nullptr; mitk::LocaleSwitch localeSwitch("C"); // read parameters svm_model *model = Malloc(svm_model,1); model->rho = nullptr; model->probA = nullptr; model->probB = nullptr; model->sv_indices = nullptr; model->label = nullptr; model->nSV = nullptr; // read header if (!read_model_header(fp, model)) { fprintf(stderr, "ERROR: fscanf failed to read model\n"); free(model->rho); free(model->label); free(model->nSV); free(model); return nullptr; } // read sv_coef and SV int elements = 0; long pos = ftell(fp); max_line_len = 1024; line = Malloc(char,max_line_len); char *p,*endptr,*idx,*val; while(readline(fp)!=nullptr) { p = strtok(line,":"); while(1) { p = strtok(nullptr,":"); if(p == nullptr) break; ++elements; } } elements += model->l; fseek(fp,pos,SEEK_SET); int m = model->nr_class - 1; int l = model->l; model->sv_coef = Malloc(double *,m); int i; for(i=0;isv_coef[i] = Malloc(double,l); model->SV = Malloc(svm_node*,l); svm_node *x_space = nullptr; if(l>0) x_space = Malloc(svm_node,elements); int j=0; for(i=0;iSV[i] = &x_space[j]; p = strtok(line, " \t"); model->sv_coef[0][i] = strtod(p,&endptr); for(int k=1;ksv_coef[k][i] = strtod(p,&endptr); } while(1) { idx = strtok(nullptr, ":"); val = strtok(nullptr, " \t"); if(val == nullptr) break; x_space[j].index = (int) strtol(idx,&endptr,10); x_space[j].value = strtod(val,&endptr); ++j; } x_space[j++].index = -1; } free(line); if (ferror(fp) != 0 || fclose(fp) != 0) return nullptr; model->free_sv = 1; // XXX return model; } void svm_free_model_content(svm_model* model_ptr) { if(model_ptr->free_sv && model_ptr->l > 0 && model_ptr->SV != nullptr) free((void *)(model_ptr->SV[0])); if(model_ptr->sv_coef) { for(int i=0;inr_class-1;i++) free(model_ptr->sv_coef[i]); } free(model_ptr->SV); model_ptr->SV = nullptr; free(model_ptr->sv_coef); model_ptr->sv_coef = nullptr; free(model_ptr->rho); model_ptr->rho = nullptr; free(model_ptr->label); model_ptr->label= nullptr; free(model_ptr->probA); model_ptr->probA = nullptr; free(model_ptr->probB); model_ptr->probB= nullptr; free(model_ptr->sv_indices); model_ptr->sv_indices = nullptr; free(model_ptr->nSV); model_ptr->nSV = nullptr; } void svm_free_and_destroy_model(svm_model** model_ptr_ptr) { if(model_ptr_ptr != nullptr && *model_ptr_ptr != nullptr) { svm_free_model_content(*model_ptr_ptr); free(*model_ptr_ptr); *model_ptr_ptr = nullptr; } } void svm_destroy_param(svm_parameter* param) { free(param->weight_label); free(param->weight); } const char *svm_check_parameter(const svm_problem *prob, const svm_parameter *param) { // svm_type int svm_type = param->svm_type; if(svm_type != C_SVC && svm_type != NU_SVC && svm_type != ONE_CLASS && svm_type != EPSILON_SVR && svm_type != NU_SVR) return "unknown svm type"; // kernel_type, degree int kernel_type = param->kernel_type; if(kernel_type != LINEAR && kernel_type != POLY && kernel_type != RBF && kernel_type != SIGMOID && kernel_type != PRECOMPUTED) return "unknown kernel type"; if(param->gamma < 0) return "gamma < 0"; if(param->degree < 0) return "degree of polynomial kernel < 0"; // cache_size,eps,C,nu,p,shrinking if(param->cache_size <= 0) return "cache_size <= 0"; if(param->eps <= 0) return "eps <= 0"; if(svm_type == C_SVC || svm_type == EPSILON_SVR || svm_type == NU_SVR) if(param->C <= 0) return "C <= 0"; if(svm_type == NU_SVC || svm_type == ONE_CLASS || svm_type == NU_SVR) if(param->nu <= 0 || param->nu > 1) return "nu <= 0 or nu > 1"; if(svm_type == EPSILON_SVR) if(param->p < 0) return "p < 0"; if(param->shrinking != 0 && param->shrinking != 1) return "shrinking != 0 and shrinking != 1"; if(param->probability != 0 && param->probability != 1) return "probability != 0 and probability != 1"; if(param->probability == 1 && svm_type == ONE_CLASS) return "one-class SVM probability output not supported yet"; // check whether nu-svc is feasible if(svm_type == NU_SVC) { int l = prob->l; int max_nr_class = 16; int nr_class = 0; int *label = Malloc(int,max_nr_class); double *count = Malloc(double,max_nr_class); int i; for(i=0;iy[i]; int j; for(j=0;jW[i]; break; } if(j == nr_class) { if(nr_class == max_nr_class) { max_nr_class *= 2; label = (int *)realloc(label,max_nr_class*sizeof(int)); count = (double *)realloc(count,max_nr_class*sizeof(double)); } label[nr_class] = this_label; count[nr_class] = prob->W[i]; ++nr_class; } } for(i=0;inu*(n1+n2)/2 > min(n1,n2)) { free(label); free(count); return "specified nu is infeasible"; } } } free(label); free(count); } return nullptr; } int svm_check_probability_model(const svm_model *model) { return ((model->param.svm_type == C_SVC || model->param.svm_type == NU_SVC) && model->probA!=nullptr && model->probB!=nullptr) || ((model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) && model->probA!=nullptr); } void svm_set_print_string_function(void (*print_func)(const char *)) { if(print_func == nullptr) svm_print_string = &print_string_stdout; else svm_print_string = print_func; } diff --git a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp index 5342b1c9cd..180bb38318 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp +++ b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp @@ -1,400 +1,400 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkContourModelSetGLMapper2D.h" #include "mitkColorProperty.h" #include "mitkContourModelSet.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include #include "vtkPen.h" #include "vtkContext2D.h" #include "vtkContextDevice2D.h" #include "vtkOpenGLContextDevice2D.h" #include "mitkManualPlacementAnnotationRenderer.h" #include "mitkBaseRenderer.h" #include "mitkContourModel.h" #include "mitkTextAnnotation2D.h" mitk::ContourModelGLMapper2DBase::ContourModelGLMapper2DBase(): m_Initialized(false) { m_PointNumbersAnnotation = mitk::TextAnnotation2D::New(); m_ControlPointNumbersAnnotation = mitk::TextAnnotation2D::New(); } mitk::ContourModelGLMapper2DBase::~ContourModelGLMapper2DBase() { } void mitk::ContourModelGLMapper2DBase::Initialize(mitk::BaseRenderer *) { - vtkOpenGLContextDevice2D *device = NULL; + vtkOpenGLContextDevice2D *device = nullptr; device = vtkOpenGLContextDevice2D::New(); if (device) { this->m_Context->Begin(device); device->Delete(); this->m_Initialized = true; } else { } } void mitk::ContourModelGLMapper2DBase::ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * /*actor*/) { float rgba[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; // check for color prop and use it for rendering if it exists GetDataNode()->GetColor(rgba, renderer, "color"); // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(rgba[3], renderer, "opacity"); if (this->m_Context->GetPen() == nullptr) { return; } this->m_Context->GetPen()->SetColorF((double)rgba[0], (double)rgba[1], (double)rgba[2], (double)rgba[3]); } void mitk::ContourModelGLMapper2DBase::DrawContour(mitk::ContourModel *renderingContour, mitk::BaseRenderer *renderer) { if (std::find(m_RendererList.begin(), m_RendererList.end(), renderer) == m_RendererList.end()) { m_RendererList.push_back(renderer); } mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_PointNumbersAnnotation.GetPointer(), renderer); m_PointNumbersAnnotation->SetVisibility(false); mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_ControlPointNumbersAnnotation.GetPointer(), renderer); m_ControlPointNumbersAnnotation->SetVisibility(false); InternalDrawContour(renderingContour, renderer); } void mitk::ContourModelGLMapper2DBase::InternalDrawContour(mitk::ContourModel *renderingContour, mitk::BaseRenderer *renderer) { if (!renderingContour) return; if (!this->m_Initialized) { this->Initialize(renderer); } vtkOpenGLContextDevice2D::SafeDownCast( this->m_Context->GetDevice())->Begin(renderer->GetVtkRenderer()); mitk::DataNode *dataNode = this->GetDataNode(); renderingContour->UpdateOutputInformation(); unsigned int timestep = renderer->GetTimeStep(); if (!renderingContour->IsEmptyTimeStep(timestep)) { // apply color and opacity read from the PropertyList ApplyColorAndOpacityProperties(renderer); mitk::ColorProperty::Pointer colorprop = dynamic_cast(dataNode->GetProperty("contour.color", renderer)); float opacity = 0.5; dataNode->GetFloatProperty("opacity", opacity, renderer); if (colorprop) { // set the color of the contour double red = colorprop->GetColor().GetRed(); double green = colorprop->GetColor().GetGreen(); double blue = colorprop->GetColor().GetBlue(); this->m_Context->GetPen()->SetColorF(red, green, blue, opacity); } mitk::ColorProperty::Pointer selectedcolor = dynamic_cast(dataNode->GetProperty("contour.points.color", renderer)); if (!selectedcolor) { selectedcolor = mitk::ColorProperty::New(1.0, 0.0, 0.1); } vtkLinearTransform *transform = dataNode->GetVtkTransform(); // ContourModel::OutputType point; mitk::Point3D point; mitk::Point3D p; float vtkp[3]; float lineWidth = 3.0; bool drawit = false; bool isHovering = false; dataNode->GetBoolProperty("contour.hovering", isHovering); if (isHovering) dataNode->GetFloatProperty("contour.hovering.width", lineWidth); else dataNode->GetFloatProperty("contour.width", lineWidth); bool showSegments = false; dataNode->GetBoolProperty("contour.segments.show", showSegments); bool showControlPoints = false; dataNode->GetBoolProperty("contour.controlpoints.show", showControlPoints); bool showPoints = false; dataNode->GetBoolProperty("contour.points.show", showPoints); bool showPointsNumbers = false; dataNode->GetBoolProperty("contour.points.text", showPointsNumbers); bool showControlPointsNumbers = false; dataNode->GetBoolProperty("contour.controlpoints.text", showControlPointsNumbers); bool projectmode = false; dataNode->GetVisibility(projectmode, renderer, "contour.project-onto-plane"); mitk::ContourModel::VertexIterator pointsIt = renderingContour->IteratorBegin(timestep); Point2D pt2d; // projected_p in display coordinates Point2D lastPt2d; int index = 0; mitk::ScalarType maxDiff = 0.25; while (pointsIt != renderingContour->IteratorEnd(timestep)) { lastPt2d = pt2d; point = (*pointsIt)->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->WorldToView(p, pt2d); ScalarType scalardiff = fabs(renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(p)); // project to plane if (projectmode) { drawit = true; } else if (scalardiff < maxDiff) // point is close enough to be drawn { drawit = true; } else { drawit = false; } // draw line if (drawit) { if (showSegments) { // lastPt2d is not valid in first step if (!(pointsIt == renderingContour->IteratorBegin(timestep))) { this->m_Context->GetPen()->SetWidth(lineWidth); this->m_Context->DrawLine(pt2d[0], pt2d[1], lastPt2d[0], lastPt2d[1]); this->m_Context->GetPen()->SetWidth(1); } } if (showControlPoints) { // draw ontrol points if ((*pointsIt)->IsControlPoint) { float pointsize = 4; Point2D tmp; Vector2D horz, vert; horz[1] = 0; vert[0] = 0; horz[0] = pointsize; vert[1] = pointsize; this->m_Context->GetPen()->SetColorF(selectedcolor->GetColor().GetRed(), selectedcolor->GetColor().GetBlue(), selectedcolor->GetColor().GetGreen()); this->m_Context->GetPen()->SetWidth(1); // a rectangle around the point with the selected color float* rectPts = new float[8]; tmp = pt2d - horz; rectPts[0] = tmp[0]; rectPts[1] = tmp[1]; tmp = pt2d + vert; rectPts[2] = tmp[0]; rectPts[3] = tmp[1]; tmp = pt2d + horz; rectPts[4] = tmp[0]; rectPts[5] = tmp[1]; tmp = pt2d - vert; rectPts[6] = tmp[0]; rectPts[7] = tmp[1]; this->m_Context->DrawPolygon(rectPts,4); // the actual point in the specified color to see the usual color of the point this->m_Context->GetPen()->SetColorF( colorprop->GetColor().GetRed(), colorprop->GetColor().GetGreen(), colorprop->GetColor().GetBlue()); this->m_Context->DrawPoint(pt2d[0], pt2d[1]); } } if (showPoints) { float pointsize = 3; Point2D tmp; Vector2D horz, vert; horz[1] = 0; vert[0] = 0; horz[0] = pointsize; vert[1] = pointsize; this->m_Context->GetPen()->SetColorF(0.0, 0.0, 0.0); this->m_Context->GetPen()->SetWidth(1); // a rectangle around the point with the selected color float* rectPts = new float[8]; tmp = pt2d - horz; rectPts[0] = tmp[0]; rectPts[1] = tmp[1]; tmp = pt2d + vert; rectPts[2] = tmp[0]; rectPts[3] = tmp[1]; tmp = pt2d + horz; rectPts[4] = tmp[0]; rectPts[5] = tmp[1]; tmp = pt2d - vert; rectPts[6] = tmp[0]; rectPts[7] = tmp[1]; this->m_Context->DrawPolygon(rectPts, 4); // the actual point in the specified color to see the usual color of the point this->m_Context->GetPen()->SetColorF( colorprop->GetColor().GetRed(), colorprop->GetColor().GetGreen(), colorprop->GetColor().GetBlue()); this->m_Context->DrawPoint(pt2d[0], pt2d[1]); } if (showPointsNumbers) { std::string l; std::stringstream ss; ss << index; l.append(ss.str()); float rgb[3]; rgb[0] = 0.0; rgb[1] = 0.0; rgb[2] = 0.0; WriteTextWithAnnotation(m_PointNumbersAnnotation, l.c_str(), rgb, pt2d, renderer); } if (showControlPointsNumbers && (*pointsIt)->IsControlPoint) { std::string l; std::stringstream ss; ss << index; l.append(ss.str()); float rgb[3]; rgb[0] = 1.0; rgb[1] = 1.0; rgb[2] = 0.0; WriteTextWithAnnotation(m_ControlPointNumbersAnnotation, l.c_str(), rgb, pt2d, renderer); } index++; } pointsIt++; } // end while iterate over controlpoints // close contour if necessary if (renderingContour->IsClosed(timestep) && drawit && showSegments) { lastPt2d = pt2d; point = renderingContour->GetVertexAt(0, timestep)->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->WorldToDisplay(p, pt2d); this->m_Context->GetPen()->SetWidth(lineWidth); this->m_Context->DrawLine(lastPt2d[0], lastPt2d[1], pt2d[0], pt2d[1]); this->m_Context->GetPen()->SetWidth(1); } // draw selected vertex if exists if (renderingContour->GetSelectedVertex()) { // transform selected vertex point = renderingContour->GetSelectedVertex()->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->WorldToDisplay(p, pt2d); ScalarType scalardiff = fabs(renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(p)); //---------------------------------- // draw point if close to plane if (scalardiff < maxDiff) { float pointsize = 5; Point2D tmp; this->m_Context->GetPen()->SetColorF(0.0, 1.0, 0.0); this->m_Context->GetPen()->SetWidth(1); // a rectangle around the point with the selected color float* rectPts = new float[8]; // a diamond around the point // begin from upper left corner and paint clockwise rectPts[0] = pt2d[0] - pointsize; rectPts[1] = pt2d[1] + pointsize; rectPts[2] = pt2d[0] + pointsize; rectPts[3] = pt2d[1] + pointsize; rectPts[4] = pt2d[0] + pointsize; rectPts[5] = pt2d[1] - pointsize; rectPts[6] = pt2d[0] - pointsize; rectPts[7] = pt2d[1] - pointsize; this->m_Context->DrawPolygon(rectPts, 4); } //------------------------------------ } } this->m_Context->GetDevice()->End(); } void mitk::ContourModelGLMapper2DBase::WriteTextWithAnnotation(TextAnnotationPointerType textAnnotation, const char *text, float rgb[3], Point2D /*pt2d*/, mitk::BaseRenderer * /*renderer*/) { textAnnotation->SetText(text); textAnnotation->SetColor(rgb); textAnnotation->SetOpacity(1); textAnnotation->SetFontSize(16); textAnnotation->SetBoolProperty("drawShadow", false); textAnnotation->SetVisibility(true); } diff --git a/Modules/Core/src/Controllers/mitkPlanePositionManager.cpp b/Modules/Core/src/Controllers/mitkPlanePositionManager.cpp index 1f0233c3cb..b24eb22c98 100644 --- a/Modules/Core/src/Controllers/mitkPlanePositionManager.cpp +++ b/Modules/Core/src/Controllers/mitkPlanePositionManager.cpp @@ -1,112 +1,112 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlanePositionManager.h" #include "mitkInteractionConst.h" mitk::PlanePositionManagerService::PlanePositionManagerService() { } mitk::PlanePositionManagerService::~PlanePositionManagerService() { for (unsigned int i = 0; i < m_PositionList.size(); ++i) delete m_PositionList[i]; } unsigned int mitk::PlanePositionManagerService::AddNewPlanePosition(const PlaneGeometry *plane, unsigned int sliceIndex) { for (unsigned int i = 0; i < m_PositionList.size(); ++i) { - if (m_PositionList[i] != 0) + if (m_PositionList[i] != nullptr) { bool isSameMatrix(true); bool isSameOffset(true); isSameOffset = mitk::Equal(m_PositionList[i]->GetTransform()->GetOffset(), plane->GetIndexToWorldTransform()->GetOffset()); if (!isSameOffset || sliceIndex != m_PositionList[i]->GetPos()) continue; isSameMatrix = mitk::MatrixEqualElementWise(m_PositionList[i]->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix()); if (isSameMatrix) return i; } } AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); matrix.GetVnlMatrix().set_column(1, plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); matrix.GetVnlMatrix().set_column(2, plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); transform->SetMatrix(matrix); transform->SetOffset(plane->GetIndexToWorldTransform()->GetOffset()); mitk::Vector3D direction; direction[0] = plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)[0]; direction[1] = plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)[1]; direction[2] = plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)[2]; direction.Normalize(); mitk::RestorePlanePositionOperation *newOp = new mitk::RestorePlanePositionOperation(OpRESTOREPLANEPOSITION, plane->GetExtent(0), plane->GetExtent(1), plane->GetSpacing(), sliceIndex, direction, transform); m_PositionList.push_back(newOp); return GetNumberOfPlanePositions() - 1; } bool mitk::PlanePositionManagerService::RemovePlanePosition(unsigned int ID) { if (m_PositionList.size() > ID) { delete m_PositionList[ID]; m_PositionList.erase(m_PositionList.begin() + ID); return true; } else { return false; } } mitk::RestorePlanePositionOperation *mitk::PlanePositionManagerService::GetPlanePosition(unsigned int ID) { if (ID < m_PositionList.size()) { return m_PositionList[ID]; } else { MITK_WARN << "GetPlanePosition returned nullptr!"; - return 0; + return nullptr; } } unsigned int mitk::PlanePositionManagerService::GetNumberOfPlanePositions() { return m_PositionList.size(); } void mitk::PlanePositionManagerService::RemoveAllPlanePositions() { for (unsigned int i = 0; i < m_PositionList.size(); ++i) delete m_PositionList[i]; m_PositionList.clear(); } diff --git a/Modules/Core/src/Controllers/mitkRenderingManager.cpp b/Modules/Core/src/Controllers/mitkRenderingManager.cpp index 8e1162931a..e74f03dea2 100644 --- a/Modules/Core/src/Controllers/mitkRenderingManager.cpp +++ b/Modules/Core/src/Controllers/mitkRenderingManager.cpp @@ -1,744 +1,744 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkRenderingManager.h" #include "mitkBaseRenderer.h" #include "mitkCameraController.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateProperty.h" #include "mitkProportionalTimeGeometry.h" #include "mitkRenderingManagerFactory.h" #include #include "mitkNumericTypes.h" #include #include #include #include #include namespace mitk { itkEventMacroDefinition(FocusChangedEvent, itk::AnyEvent) - RenderingManager::Pointer RenderingManager::s_Instance = 0; - RenderingManagerFactory *RenderingManager::s_RenderingManagerFactory = 0; + RenderingManager::Pointer RenderingManager::s_Instance = nullptr; + RenderingManagerFactory *RenderingManager::s_RenderingManagerFactory = nullptr; RenderingManager::RenderingManager() : m_UpdatePending(false), m_MaxLOD(1), m_LODIncreaseBlocked(false), m_LODAbortMechanismEnabled(false), m_ClippingPlaneEnabled(false), m_TimeNavigationController(SliceNavigationController::New()), m_DataStorage(nullptr), m_ConstrainedPanningZooming(true), m_FocusedRenderWindow(nullptr) { m_ShadingEnabled.assign(3, false); m_ShadingValues.assign(4, 0.0); InitializePropertyList(); } RenderingManager::~RenderingManager() { // Decrease reference counts of all registered vtkRenderWindows for // proper destruction RenderWindowVector::iterator it; for (it = m_AllRenderWindows.begin(); it != m_AllRenderWindows.end(); ++it) { (*it)->UnRegister(nullptr); RenderWindowCallbacksList::iterator callbacks_it = this->m_RenderWindowCallbacksList.find(*it); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { (*it)->RemoveObserver(callbacks_it->second.commands[0u]); (*it)->RemoveObserver(callbacks_it->second.commands[1u]); (*it)->RemoveObserver(callbacks_it->second.commands[2u]); } } } void RenderingManager::SetFactory(RenderingManagerFactory *factory) { s_RenderingManagerFactory = factory; } const RenderingManagerFactory *RenderingManager::GetFactory() { return s_RenderingManagerFactory; } bool RenderingManager::HasFactory() { if (RenderingManager::s_RenderingManagerFactory) { return true; } else { return false; } } RenderingManager::Pointer RenderingManager::New() { const RenderingManagerFactory *factory = GetFactory(); if (factory == nullptr) return nullptr; return factory->CreateRenderingManager(); } RenderingManager *RenderingManager::GetInstance() { if (!RenderingManager::s_Instance) { if (s_RenderingManagerFactory) { s_Instance = s_RenderingManagerFactory->CreateRenderingManager(); } } return s_Instance; } bool RenderingManager::IsInstantiated() { if (RenderingManager::s_Instance) return true; else return false; } void RenderingManager::AddRenderWindow(vtkRenderWindow *renderWindow) { if (renderWindow && (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.end())) { m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_AllRenderWindows.push_back(renderWindow); if (m_DataStorage.IsNotNull()) mitk::BaseRenderer::GetInstance(renderWindow)->SetDataStorage(m_DataStorage.GetPointer()); // Register vtkRenderWindow instance renderWindow->Register(nullptr); // Add callbacks for rendering abort mechanism // BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); vtkCallbackCommand *startCallbackCommand = vtkCallbackCommand::New(); startCallbackCommand->SetCallback(RenderingManager::RenderingStartCallback); renderWindow->AddObserver(vtkCommand::StartEvent, startCallbackCommand); vtkCallbackCommand *progressCallbackCommand = vtkCallbackCommand::New(); progressCallbackCommand->SetCallback(RenderingManager::RenderingProgressCallback); renderWindow->AddObserver(vtkCommand::AbortCheckEvent, progressCallbackCommand); vtkCallbackCommand *endCallbackCommand = vtkCallbackCommand::New(); endCallbackCommand->SetCallback(RenderingManager::RenderingEndCallback); renderWindow->AddObserver(vtkCommand::EndEvent, endCallbackCommand); RenderWindowCallbacks callbacks; callbacks.commands[0u] = startCallbackCommand; callbacks.commands[1u] = progressCallbackCommand; callbacks.commands[2u] = endCallbackCommand; this->m_RenderWindowCallbacksList[renderWindow] = callbacks; // Delete vtk variables correctly startCallbackCommand->Delete(); progressCallbackCommand->Delete(); endCallbackCommand->Delete(); } } void RenderingManager::RemoveRenderWindow(vtkRenderWindow *renderWindow) { if (m_RenderWindowList.erase(renderWindow)) { RenderWindowCallbacksList::iterator callbacks_it = this->m_RenderWindowCallbacksList.find(renderWindow); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { renderWindow->RemoveObserver(callbacks_it->second.commands[0u]); renderWindow->RemoveObserver(callbacks_it->second.commands[1u]); renderWindow->RemoveObserver(callbacks_it->second.commands[2u]); this->m_RenderWindowCallbacksList.erase(callbacks_it); } RenderWindowVector::iterator rw_it = std::find(m_AllRenderWindows.begin(), m_AllRenderWindows.end(), renderWindow); if (rw_it != m_AllRenderWindows.cend()) { // Decrease reference count for proper destruction (*rw_it)->UnRegister(nullptr); m_AllRenderWindows.erase(rw_it); } } } const RenderingManager::RenderWindowVector &RenderingManager::GetAllRegisteredRenderWindows() { return m_AllRenderWindows; } void RenderingManager::RequestUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } m_RenderWindowList[renderWindow] = RENDERING_REQUESTED; if (!m_UpdatePending) { m_UpdatePending = true; this->GenerateRenderingRequestEvent(); } } void RenderingManager::ForceImmediateUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } // Erase potentially pending requests for this window m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_UpdatePending = false; // Immediately repaint this window (implementation platform specific) // If the size is 0 it crashes int *size = renderWindow->GetSize(); if (0 != size[0] && 0 != size[1]) { // prepare the camera etc. before rendering // Note: this is a very important step which should be called before the VTK render! // If you modify the camera anywhere else or after the render call, the scene cannot be seen. mitk::VtkPropRenderer *vPR = dynamic_cast(mitk::BaseRenderer::GetInstance(renderWindow)); if (vPR) vPR->PrepareRender(); // Execute rendering renderWindow->Render(); } } void RenderingManager::RequestUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->RequestUpdate(it->first); } } } void RenderingManager::ForceImmediateUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { // Immediately repaint this window (implementation platform specific) // If the size is 0, it crashes this->ForceImmediateUpdate(it->first); } } } void RenderingManager::InitializeViewsByBoundingObjects(const DataStorage *ds) { if (!ds) return; // get all nodes that have not set "includeInBoundingBox" to false mitk::NodePredicateNot::Pointer pred = mitk::NodePredicateNot::New( mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false))); mitk::DataStorage::SetOfObjects::ConstPointer rs = ds->GetSubset(pred); // calculate bounding geometry of these nodes mitk::TimeGeometry::Pointer bounds = ds->ComputeBoundingGeometry3D(rs, "visible"); // initialize the views to the bounding geometry this->InitializeViews(bounds); } // TODO_GOETZ // Remove old function, so only this one is working. bool RenderingManager::InitializeViews(const BaseGeometry *dataGeometry, RequestType type, bool preserveRoughOrientationInWorldSpace) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(dataGeometry->Clone().GetPointer()), 1); return InitializeViews(propTimeGeometry, type, preserveRoughOrientationInWorldSpace); } bool RenderingManager::InitializeViews(const TimeGeometry *dataGeometry, RequestType type, bool /*preserveRoughOrientationInWorldSpace*/) { MITK_DEBUG << "initializing views"; bool boundingBoxInitialized = false; TimeGeometry::ConstPointer timeGeometry = dataGeometry; TimeGeometry::Pointer modifiedGeometry = nullptr; if (dataGeometry != nullptr) { modifiedGeometry = dataGeometry->Clone(); } int warningLevel = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); if ((timeGeometry.IsNotNull()) && (const_cast(timeGeometry->GetBoundingBoxInWorld())->GetDiagonalLength2() > mitk::eps)) { boundingBoxInitialized = true; } if (timeGeometry.IsNotNull()) { // make sure bounding box has an extent bigger than zero in any direction // clone the input geometry // Old Geometry3D::Pointer modifiedGeometry = dynamic_cast( dataGeometry->Clone().GetPointer() ); assert(modifiedGeometry.IsNotNull()); for (TimeStepType step = 0; step < modifiedGeometry->CountTimeSteps(); ++step) { BaseGeometry::BoundsArrayType newBounds = modifiedGeometry->GetGeometryForTimeStep(step)->GetBounds(); for (unsigned int dimension = 0; (2 * dimension) < newBounds.Size(); dimension++) { // check for equality but for an epsilon if (Equal(newBounds[2 * dimension], newBounds[2 * dimension + 1])) { newBounds[2 * dimension + 1] += 1; if (Equal( newBounds[2 * dimension], newBounds[2 * dimension + 1])) // newBounds will still be equal if values are beyond double precision { mitkThrow() << "One dimension of object data has zero length, please make sure you're not using numbers " "beyond double precision as coordinates."; } } } modifiedGeometry->GetGeometryForTimeStep(step)->SetBounds(newBounds); } } timeGeometry = modifiedGeometry; RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(it->first); baseRenderer->SetConstrainZoomingAndPanning(m_ConstrainedPanningZooming); int id = baseRenderer->GetMapperID(); if (((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)))) { this->InternalViewInitialization(baseRenderer, timeGeometry, boundingBoxInitialized, id); } } if (boundingBoxInitialized) { m_TimeNavigationController->SetInputWorldTimeGeometry(timeGeometry); } m_TimeNavigationController->Update(); this->RequestUpdateAll(type); vtkObject::SetGlobalWarningDisplay(warningLevel); // Inform listeners that views have been initialized this->InvokeEvent(mitk::RenderingManagerViewsInitializedEvent()); return boundingBoxInitialized; } bool RenderingManager::InitializeViews(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(it->first); int id = baseRenderer->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); // Update the SNC nc->Update(); } } this->RequestUpdateAll(type); return true; } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow, const BaseGeometry *geometry, bool initializeGlobalTimeSNC) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); return InitializeView(renderWindow, propTimeGeometry, initializeGlobalTimeSNC); } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow, const TimeGeometry *geometry, bool initializeGlobalTimeSNC) { bool boundingBoxInitialized = false; int warningLevel = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); if ((geometry != nullptr) && (const_cast(geometry->GetBoundingBoxInWorld())->GetDiagonalLength2() > mitk::eps)) { boundingBoxInitialized = true; } mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); int id = baseRenderer->GetMapperID(); this->InternalViewInitialization(baseRenderer, geometry, boundingBoxInitialized, id); if (boundingBoxInitialized && initializeGlobalTimeSNC) { m_TimeNavigationController->SetInputWorldTimeGeometry(geometry); } m_TimeNavigationController->Update(); this->RequestUpdate(renderWindow); vtkObject::SetGlobalWarningDisplay(warningLevel); return boundingBoxInitialized; } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); // Update the SNC nc->Update(); this->RequestUpdate(renderWindow); return true; } void RenderingManager::InternalViewInitialization(mitk::BaseRenderer *baseRenderer, const mitk::TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID) { mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); if (boundingBoxInitialized) { // Set geometry for NC nc->SetInputWorldTimeGeometry(geometry); nc->Update(); if (mapperID == BaseRenderer::Standard2D) { // For 2D SNCs, steppers are set so that the cross is centered // in the image nc->GetSlice()->SetPos(nc->GetSlice()->GetSteps() / 2); } baseRenderer->GetCameraController()->SetViewToAnterior(); baseRenderer->GetCameraController()->Fit(); } else { nc->Update(); } } const SliceNavigationController *RenderingManager::GetTimeNavigationController() const { return m_TimeNavigationController.GetPointer(); } SliceNavigationController *RenderingManager::GetTimeNavigationController() { return m_TimeNavigationController.GetPointer(); } void RenderingManager::ExecutePendingRequests() { m_UpdatePending = false; // Satisfy all pending update requests RenderWindowList::const_iterator it; int i = 0; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it, ++i) { if (it->second == RENDERING_REQUESTED) { this->ForceImmediateUpdate(it->first); } } } void RenderingManager::RenderingStartCallback(vtkObject *caller, unsigned long, void *, void *) { vtkRenderWindow *renderWindow = dynamic_cast(caller); mitk::RenderingManager *renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); RenderWindowList &renderWindowList = renman->m_RenderWindowList; if (renderWindow) { renderWindowList[renderWindow] = RENDERING_INPROGRESS; } renman->m_UpdatePending = false; } void RenderingManager::RenderingProgressCallback(vtkObject *caller, unsigned long, void *, void *) { vtkRenderWindow *renderWindow = dynamic_cast(caller); mitk::RenderingManager *renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); if (renman->m_LODAbortMechanismEnabled) { vtkRenderWindow *renderWindow = dynamic_cast(caller); if (renderWindow) { BaseRenderer *renderer = BaseRenderer::GetInstance(renderWindow); if (renderer && (renderer->GetNumberOfVisibleLODEnabledMappers() > 0)) { renman->DoMonitorRendering(); } } } } void RenderingManager::RenderingEndCallback(vtkObject *caller, unsigned long, void *, void *) { vtkRenderWindow *renderWindow = dynamic_cast(caller); mitk::RenderingManager *renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); RenderWindowList &renderWindowList = renman->m_RenderWindowList; RendererIntMap &nextLODMap = renman->m_NextLODMap; if (renderWindow) { BaseRenderer *renderer = BaseRenderer::GetInstance(renderWindow); if (renderer) { renderWindowList[renderer->GetRenderWindow()] = RENDERING_INACTIVE; // Level-of-Detail handling if (renderer->GetNumberOfVisibleLODEnabledMappers() > 0) { if (nextLODMap[renderer] == 0) renman->StartOrResetTimer(); else nextLODMap[renderer] = 0; } } } } bool RenderingManager::IsRendering() const { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { return true; } } return false; } void RenderingManager::AbortRendering() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { it->first->SetAbortRender(true); m_RenderingAbortedMap[BaseRenderer::GetInstance(it->first)] = true; } } } int RenderingManager::GetNextLOD(BaseRenderer *renderer) { if (renderer != nullptr) { return m_NextLODMap[renderer]; } else { return 0; } } void RenderingManager::ExecutePendingHighResRenderingRequest() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { BaseRenderer *renderer = BaseRenderer::GetInstance(it->first); if (renderer->GetNumberOfVisibleLODEnabledMappers() > 0) { if (m_NextLODMap[renderer] == 0) { m_NextLODMap[renderer] = 1; RequestUpdate(it->first); } } } } void RenderingManager::SetMaximumLOD(unsigned int max) { m_MaxLOD = max; } // enable/disable shading void RenderingManager::SetShading(bool state, unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return; } m_ShadingEnabled[lod] = state; } bool RenderingManager::GetShading(unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return false; } return m_ShadingEnabled[lod]; } // enable/disable the clipping plane void RenderingManager::SetClippingPlaneStatus(bool status) { m_ClippingPlaneEnabled = status; } bool RenderingManager::GetClippingPlaneStatus() { return m_ClippingPlaneEnabled; } void RenderingManager::SetShadingValues(float ambient, float diffuse, float specular, float specpower) { m_ShadingValues[0] = ambient; m_ShadingValues[1] = diffuse; m_ShadingValues[2] = specular; m_ShadingValues[3] = specpower; } RenderingManager::FloatVector &RenderingManager::GetShadingValues() { return m_ShadingValues; } void RenderingManager::InitializePropertyList() { if (m_PropertyList.IsNull()) { m_PropertyList = PropertyList::New(); } this->SetProperty("coupled-zoom", BoolProperty::New(false)); this->SetProperty("coupled-plane-rotation", BoolProperty::New(false)); this->SetProperty("MIP-slice-rendering", BoolProperty::New(false)); } PropertyList::Pointer RenderingManager::GetPropertyList() const { return m_PropertyList; } BaseProperty *RenderingManager::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void RenderingManager::SetProperty(const char *propertyKey, BaseProperty *propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void RenderingManager::SetDataStorage(DataStorage *storage) { if (storage != nullptr) { m_DataStorage = storage; RenderingManager::RenderWindowVector::const_iterator iter; for (iter = m_AllRenderWindows.cbegin(); iter < m_AllRenderWindows.cend(); ++iter) { mitk::BaseRenderer::GetInstance((*iter))->SetDataStorage(m_DataStorage.GetPointer()); } } } mitk::DataStorage *RenderingManager::GetDataStorage() { return m_DataStorage; } void RenderingManager::SetRenderWindowFocus(vtkRenderWindow *focusWindow) { if (focusWindow != m_FocusedRenderWindow) { if (!focusWindow || (m_RenderWindowList.find(focusWindow) != m_RenderWindowList.cend())) { m_FocusedRenderWindow = focusWindow; this->InvokeEvent(FocusChangedEvent()); return; } MITK_ERROR << "Tried to set a RenderWindow that does not exist."; } } // Create and register generic RenderingManagerFactory. TestingRenderingManagerFactory renderingManagerFactory; } // namespace diff --git a/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp b/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp index c5969f0505..9939a7ee28 100644 --- a/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp @@ -1,293 +1,293 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include mitk::ArbitraryTimeGeometry::ArbitraryTimeGeometry() = default; mitk::ArbitraryTimeGeometry::~ArbitraryTimeGeometry() = default; void mitk::ArbitraryTimeGeometry::Initialize() { this->ClearAllGeometries(); Geometry3D::Pointer geo = Geometry3D::New(); this->AppendNewTimeStep(geo, 0, 1); } mitk::TimeStepType mitk::ArbitraryTimeGeometry::CountTimeSteps() const { return static_cast(m_GeometryVector.size()); } mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMinimumTimePoint() const { return m_MinimumTimePoints.empty() ? 0.0 : m_MinimumTimePoints.front(); } mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMaximumTimePoint() const { TimePointType result = 0; if ( !m_MaximumTimePoints.empty() ) { result = m_MaximumTimePoints.back(); } return result; } mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMinimumTimePoint( TimeStepType step ) const { TimePointType result = GetMinimumTimePoint(); if (step > 0 && step <= m_MaximumTimePoints.size()) { result = m_MaximumTimePoints[step - 1]; } return result; }; mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMaximumTimePoint( TimeStepType step ) const { TimePointType result = 0; if (step < m_MaximumTimePoints.size()) { result = m_MaximumTimePoints[step]; } return result; }; mitk::TimeBounds mitk::ArbitraryTimeGeometry::GetTimeBounds() const { TimeBounds bounds; bounds[0] = this->GetMinimumTimePoint(); bounds[1] = this->GetMaximumTimePoint(); return bounds; } mitk::TimeBounds mitk::ArbitraryTimeGeometry::GetTimeBounds(TimeStepType step) const { TimeBounds bounds; bounds[0] = this->GetMinimumTimePoint( step ); bounds[1] = this->GetMaximumTimePoint( step ); return bounds; } bool mitk::ArbitraryTimeGeometry::IsValidTimePoint(TimePointType timePoint) const { return this->GetMinimumTimePoint() <= timePoint && timePoint < this->GetMaximumTimePoint(); } bool mitk::ArbitraryTimeGeometry::IsValidTimeStep(TimeStepType timeStep) const { return timeStep < this->CountTimeSteps(); } mitk::TimePointType mitk::ArbitraryTimeGeometry::TimeStepToTimePoint( TimeStepType timeStep ) const { TimePointType result = 0.0; if (timeStep < m_MinimumTimePoints.size() ) { result = m_MinimumTimePoints[timeStep]; } return result; } mitk::TimeStepType mitk::ArbitraryTimeGeometry::TimePointToTimeStep(TimePointType timePoint) const { mitk::TimeStepType result = 0; if ( timePoint >= GetMinimumTimePoint() ) { for ( auto pos = m_MaximumTimePoints.cbegin(); pos != m_MaximumTimePoints.cend(); ++pos ) { if (timePoint < *pos) { result = pos - m_MaximumTimePoints.begin(); break; } } } return result; } mitk::BaseGeometry::Pointer mitk::ArbitraryTimeGeometry::GetGeometryForTimeStep(TimeStepType timeStep) const { if ( IsValidTimeStep( timeStep ) ) { return m_GeometryVector[timeStep]; } else { - return 0; + return nullptr; } } mitk::BaseGeometry::Pointer mitk::ArbitraryTimeGeometry::GetGeometryForTimePoint( TimePointType timePoint ) const { if ( this->IsValidTimePoint( timePoint ) ) { const TimeStepType timeStep = this->TimePointToTimeStep( timePoint ); return this->GetGeometryForTimeStep( timeStep ); } else { - return 0; + return nullptr; } } mitk::BaseGeometry::Pointer mitk::ArbitraryTimeGeometry::GetGeometryCloneForTimeStep( TimeStepType timeStep ) const { if ( timeStep >= m_GeometryVector.size() ) - return 0; + return nullptr; return m_GeometryVector[timeStep]->Clone(); } bool mitk::ArbitraryTimeGeometry::IsValid() const { bool isValid = true; isValid &= m_GeometryVector.size() > 0; return isValid; } void mitk::ArbitraryTimeGeometry::ClearAllGeometries() { m_GeometryVector.clear(); m_MinimumTimePoints.clear(); m_MaximumTimePoints.clear(); } void mitk::ArbitraryTimeGeometry::ReserveSpaceForGeometries( TimeStepType numberOfGeometries ) { m_GeometryVector.reserve( numberOfGeometries ); m_MinimumTimePoints.reserve( numberOfGeometries ); m_MaximumTimePoints.reserve( numberOfGeometries ); } void mitk::ArbitraryTimeGeometry::Expand( mitk::TimeStepType size ) { m_GeometryVector.reserve( size ); const mitk::TimeStepType lastIndex = this->CountTimeSteps() - 1; const TimePointType minTP = this->GetMinimumTimePoint( lastIndex ); TimePointType maxTP = this->GetMaximumTimePoint( lastIndex ); const TimePointType duration = maxTP - minTP; while (m_GeometryVector.size() < size) { m_GeometryVector.push_back( Geometry3D::New().GetPointer() ); m_MinimumTimePoints.push_back( maxTP ); maxTP += duration; m_MaximumTimePoints.push_back( maxTP ); } } void mitk::ArbitraryTimeGeometry::ReplaceTimeStepGeometries(const BaseGeometry *geometry) { for ( auto pos = m_GeometryVector.begin(); pos != m_GeometryVector.end(); ++pos ) { *pos = geometry->Clone(); } } void mitk::ArbitraryTimeGeometry::SetTimeStepGeometry(BaseGeometry *geometry, TimeStepType timeStep) { assert( timeStep <= m_GeometryVector.size() ); if ( timeStep == m_GeometryVector.size() ) { m_GeometryVector.push_back( geometry ); } m_GeometryVector[timeStep] = geometry; } itk::LightObject::Pointer mitk::ArbitraryTimeGeometry::InternalClone() const { itk::LightObject::Pointer parent = Superclass::InternalClone(); ArbitraryTimeGeometry::Pointer newTimeGeometry = dynamic_cast(parent.GetPointer()); newTimeGeometry->m_MinimumTimePoints = this->m_MinimumTimePoints; newTimeGeometry->m_MaximumTimePoints = this->m_MaximumTimePoints; newTimeGeometry->m_GeometryVector.clear(); for (TimeStepType i = 0; i < CountTimeSteps(); ++i) { newTimeGeometry->m_GeometryVector.push_back( this->m_GeometryVector[i]->Clone() ); } return parent; } void mitk::ArbitraryTimeGeometry::AppendNewTimeStep(BaseGeometry *geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint) { if ( !geometry ) { mitkThrow() << "Cannot append geometry to time geometry. Invalid geometry passed (nullptr pointer)."; } if (maximumTimePoint < minimumTimePoint) { mitkThrow() << "Cannot append geometry to time geometry. Time bound conflict. Maxmimum time point ("< minimumTimePoint ) { mitkThrow() << "Cannot append geometry to time geometry. Time bound conflict new time point and currently last time point overlapp."; } } m_GeometryVector.push_back( geometry ); m_MinimumTimePoints.push_back( minimumTimePoint ); m_MaximumTimePoints.push_back( maximumTimePoint ); } void mitk::ArbitraryTimeGeometry::AppendNewTimeStepClone(const BaseGeometry *geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint) { BaseGeometry::Pointer clone = geometry->Clone(); this->AppendNewTimeStep(clone, minimumTimePoint, maximumTimePoint); }; void mitk::ArbitraryTimeGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << " MinimumTimePoint: " << this->GetMinimumTimePoint() << " ms" << std::endl; os << indent << " MaximumTimePoint: " << this->GetMaximumTimePoint() << " ms" << std::endl; os << std::endl; os << indent << " min TimeBounds: " << std::endl; for (TimeStepType i = 0; i < m_MinimumTimePoints.size(); ++i) { os << indent.GetNextIndent() << "Step " << i << ": " << m_MinimumTimePoints[i] << " ms" << std::endl; } os << std::endl; os << indent << " max TimeBounds: " << std::endl; for (TimeStepType i = 0; i < m_MaximumTimePoints.size(); ++i) { os << indent.GetNextIndent() << "Step " << i << ": " << m_MaximumTimePoints[i] << " ms" << std::endl; } } diff --git a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp index 53103c711f..75fd6cc88e 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp @@ -1,622 +1,622 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLevelWindowManager.h" #include "mitkDataStorage.h" #include "mitkImage.h" #include "mitkMessage.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include "mitkRenderingModeProperty.h" #include mitk::LevelWindowManager::LevelWindowManager() : m_DataStorage(nullptr), m_LevelWindowProperty(nullptr), m_AutoTopMost(true), m_IsObserverTagSet(false), m_CurrentImage(nullptr), m_IsPropertyModifiedTagSet(false), m_SettingImgForLvlWinProp(false) { } mitk::LevelWindowManager::~LevelWindowManager() { if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); m_DataStorage = nullptr; } if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } // clear both observer maps this->ClearPropObserverLists(); } void mitk::LevelWindowManager::SetDataStorage(mitk::DataStorage *ds) { if (ds == nullptr) return; /* remove listeners of old DataStorage */ if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); } /* register listener for new DataStorage */ m_DataStorage = ds; // register m_DataStorage->AddNodeEvent.AddListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.AddListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); this->DataStorageAddedNode(); // update us with new DataStorage } void mitk::LevelWindowManager::OnPropertyModified(const itk::EventObject &) { Modified(); } void mitk::LevelWindowManager::SetAutoTopMostImage(bool autoTopMost, const mitk::DataNode *removedNode) { m_AutoTopMost = autoTopMost; if (m_AutoTopMost == false) return; if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } /* search topmost image */ if (m_DataStorage.IsNull()) { itkExceptionMacro("DataStorage not set"); } int maxLayer = itk::NumericTraits::min(); m_LevelWindowProperty = nullptr; mitk::DataNode::Pointer topLevelNode; mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull() || (removedNode != nullptr && node == removedNode)) continue; m_SettingImgForLvlWinProp = true; node->SetBoolProperty("imageForLevelWindow", false); m_SettingImgForLvlWinProp = false; if (node->IsVisible(nullptr) == false) continue; int layer = 0; node->GetIntProperty("layer", layer); if (layer < maxLayer) continue; mitk::LevelWindowProperty::Pointer levelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); if (levelWindowProperty.IsNull()) continue; int nonLvlWinMode1 = mitk::RenderingModeProperty::LOOKUPTABLE_COLOR; int nonLvlWinMode2 = mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(node->GetProperty("Image Rendering.Mode")); if (mode.IsNotNull()) { int currMode = mode->GetRenderingMode(); if (currMode == nonLvlWinMode1 || currMode == nonLvlWinMode2) { continue; } } else continue; m_LevelWindowProperty = levelWindowProperty; m_CurrentImage = dynamic_cast(node->GetData()); topLevelNode = node; maxLayer = layer; } if (topLevelNode.IsNotNull()) { m_SettingImgForLvlWinProp = true; topLevelNode->SetBoolProperty("imageForLevelWindow", true); m_SettingImgForLvlWinProp = false; } this->SetLevelWindowProperty(m_LevelWindowProperty); if (m_LevelWindowProperty.IsNull()) { Modified(); } // else SetLevelWindowProperty will call Modified(); } // sets an specific LevelWindowProperty, all changes will affect the image belonging to this property. void mitk::LevelWindowManager::SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty) { if (levelWindowProperty.IsNull()) return; /* search image than belongs to the property */ typedef mitk::DataStorage::SetOfObjects NodeSetType; NodeSetType::ConstPointer nodes = m_DataStorage->GetAll(); NodeSetType::ConstIterator it = nodes->Begin(); - mitk::DataNode::Pointer propNode = 0; + mitk::DataNode::Pointer propNode = nullptr; while (it != nodes->End()) { mitk::DataNode::Pointer node = it.Value(); mitk::LevelWindowProperty::Pointer prop = dynamic_cast(node->GetProperty("levelwindow")); if (prop == levelWindowProperty) { propNode = node; } else { m_SettingImgForLvlWinProp = true; node->SetBoolProperty("imageForLevelWindow", false); m_SettingImgForLvlWinProp = false; } ++it; } if (propNode.IsNull()) { mitkThrow() << "No Image in DataStorage that belongs to LevelWindow property" << m_LevelWindowProperty; } if (m_IsPropertyModifiedTagSet) // remove listener for old property { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } m_LevelWindowProperty = levelWindowProperty; itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); // register listener for new property command->SetCallbackFunction(this, &LevelWindowManager::OnPropertyModified); m_PropertyModifiedTag = m_LevelWindowProperty->AddObserver(itk::ModifiedEvent(), command); m_IsPropertyModifiedTagSet = true; m_CurrentImage = dynamic_cast(propNode->GetData()); m_SettingImgForLvlWinProp = true; propNode->SetBoolProperty("imageForLevelWindow", true); m_SettingImgForLvlWinProp = false; this->Modified(); } // returns the current mitkLevelWindowProperty object from the image that is affected by changes mitk::LevelWindowProperty::Pointer mitk::LevelWindowManager::GetLevelWindowProperty() { return m_LevelWindowProperty; } // returns Level/Window values for the current image const mitk::LevelWindow &mitk::LevelWindowManager::GetLevelWindow() { if (m_LevelWindowProperty.IsNotNull()) { return m_LevelWindowProperty->GetLevelWindow(); } else { itkExceptionMacro("No LevelWindow available!"); } } // sets new Level/Window values and informs all listeners about changes void mitk::LevelWindowManager::SetLevelWindow(const mitk::LevelWindow &levelWindow) { if (m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->SetLevelWindow(levelWindow); } this->Modified(); } void mitk::LevelWindowManager::DataStorageAddedNode(const mitk::DataNode *) { // update observers with new data storage UpdateObservers(); // Initialize LevelWindowsManager to new image SetAutoTopMostImage(true); // check if everything is still ok if ((m_PropObserverToNode.size() != m_PropObserverToNode2.size()) || (m_PropObserverToNode2.size() != this->GetRelevantNodes()->size())) { mitkThrow() << "Wrong number of observers in Level Window Manager!"; } } void mitk::LevelWindowManager::DataStorageRemovedNode(const mitk::DataNode *removedNode) { // first: check if deleted node is part of relevant nodes. If not, abort method because there is no need change // anything. if ((this->GetRelevantNodes()->size() == 0)) return; bool removedNodeIsRelevant = false; /* Iterator code: is crashing, don't know why... so using for loop for (mitk::DataStorage::SetOfObjects::ConstIterator it = this->GetRelevantNodes()->Begin(); it != this->GetRelevantNodes()->End(); ++it) {if (it->Value() == removedNode) {removedNodeIsRelevant=true;}}*/ for (unsigned int i = 0; i < this->GetRelevantNodes()->size(); i++) { if (this->GetRelevantNodes()->at(i) == removedNode) { removedNodeIsRelevant = true; } } if (!removedNodeIsRelevant) return; // remember node which will be removed m_NodeMarkedToDelete = removedNode; // update observers UpdateObservers(); /* search image than belongs to the property */ if (m_LevelWindowProperty.IsNull()) { SetAutoTopMostImage(true, removedNode); } else { mitk::NodePredicateProperty::Pointer p2 = mitk::NodePredicateProperty::New("levelwindow", m_LevelWindowProperty); mitk::DataNode *n = m_DataStorage->GetNode(p2); if (n == nullptr || m_AutoTopMost) // if node was deleted, change our behaviour to AutoTopMost, if AutoTopMost is true // change level window to topmost node { SetAutoTopMostImage(true, removedNode); } } // reset variable m_NodeMarkedToDelete = nullptr; // check if everything is still ok if ((m_PropObserverToNode.size() != m_PropObserverToNode2.size()) || (m_PropObserverToNode2.size() != (this->GetRelevantNodes()->size() - 1))) { mitkThrow() << "Wrong number of observers in Level Window Manager!"; } } void mitk::LevelWindowManager::UpdateObservers() { this->ClearPropObserverLists(); // remove old observers CreatePropObserverLists(); // create new observer lists } int mitk::LevelWindowManager::GetNumberOfObservers() { return m_PropObserverToNode.size(); } mitk::DataStorage *mitk::LevelWindowManager::GetDataStorage() { return m_DataStorage.GetPointer(); } // true if changes on slider or line-edits will affect always the topmost layer image bool mitk::LevelWindowManager::isAutoTopMost() { return m_AutoTopMost; } void mitk::LevelWindowManager::RecaluclateLevelWindowForSelectedComponent(const itk::EventObject &event) { mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; bool isSelected = false; node->GetBoolProperty("selected", isSelected); if (isSelected) { mitk::LevelWindow selectedLevelWindow; node->GetLevelWindow(selectedLevelWindow); // node is an image node because of predicates mitk::Image *image = dynamic_cast(node->GetData()); int displayedComponent = 0; if (image && (node->GetIntProperty("Image.Displayed Component", displayedComponent))) { // we found a selected image with a displayed component // let's recalculate the levelwindow for this. selectedLevelWindow.SetAuto(image, true, true, static_cast(displayedComponent)); node->SetLevelWindow(selectedLevelWindow); } } mitk::LevelWindow levelWindow; node->GetLevelWindow(levelWindow); } this->Update(event); } void mitk::LevelWindowManager::Update(const itk::EventObject &) // visible property of a image has changed { if (m_SettingImgForLvlWinProp) // no mutex, should still help { return; } if (m_AutoTopMost) { SetAutoTopMostImage(true); return; } int maxVisibleLayer = itk::NumericTraits::min(); mitk::DataNode::Pointer highestVisible = nullptr; std::vector visProbNodes; mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull()) { continue; } bool visible = node->IsVisible(nullptr); if (node->IsVisible(nullptr)) { int layer = -1; node->GetIntProperty("layer", layer); if (layer > maxVisibleLayer) { maxVisibleLayer = layer; highestVisible = node; } } bool prop = false; node->GetBoolProperty("imageForLevelWindow", prop); if (prop && visible) { visProbNodes.push_back(node); } } int numVisProbNodes = visProbNodes.size(); if (numVisProbNodes > 2) { MITK_ERROR << "Error: not more than two visible nodes are expected to have the imageForLevelWindow property set at " "any point."; } else if (numVisProbNodes == 2) { for (std::vector::const_iterator it = visProbNodes.begin(); it != visProbNodes.end(); ++it) { mitk::LevelWindowProperty::Pointer newProp = dynamic_cast((*it)->GetProperty("levelwindow")); if (newProp != m_LevelWindowProperty) { this->SetLevelWindowProperty(newProp); return; } } } else if (numVisProbNodes == 1) { mitk::LevelWindowProperty::Pointer newProp = dynamic_cast(visProbNodes[0]->GetProperty("levelwindow")); if (newProp != m_LevelWindowProperty) { this->SetLevelWindowProperty(newProp); return; } } else if (highestVisible) { mitk::LevelWindowProperty::Pointer lvlProp = dynamic_cast(highestVisible->GetProperty("levelwindow")); this->SetLevelWindowProperty(lvlProp); } else { Modified(); } } mitk::DataStorage::SetOfObjects::ConstPointer mitk::LevelWindowManager::GetRelevantNodes() { if (m_DataStorage.IsNull()) return mitk::DataStorage::SetOfObjects::ConstPointer(mitk::DataStorage::SetOfObjects::New()); // return empty set mitk::NodePredicateProperty::Pointer notBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(false)); mitk::NodePredicateProperty::Pointer hasLevelWindow = mitk::NodePredicateProperty::New("levelwindow", nullptr); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateDataType::Pointer isDImage = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isTImage = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdfImage = mitk::NodePredicateDataType::New("OdfImage"); mitk::NodePredicateOr::Pointer predicateTypes = mitk::NodePredicateOr::New(); predicateTypes->AddPredicate(isImage); predicateTypes->AddPredicate(isDImage); predicateTypes->AddPredicate(isTImage); predicateTypes->AddPredicate(isOdfImage); mitk::NodePredicateAnd::Pointer predicate = mitk::NodePredicateAnd::New(); predicate->AddPredicate(notBinary); predicate->AddPredicate(hasLevelWindow); predicate->AddPredicate(predicateTypes); mitk::DataStorage::SetOfObjects::ConstPointer relevantNodes = m_DataStorage->GetSubset(predicate); return relevantNodes; } mitk::Image *mitk::LevelWindowManager::GetCurrentImage() { return m_CurrentImage; } void mitk::LevelWindowManager::ClearPropObserverLists() { for (ObserverToPropertyMap::iterator iter = m_PropObserverToNode.begin(); iter != m_PropObserverToNode.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); - (*iter).second = 0; + (*iter).second = nullptr; } m_PropObserverToNode.clear(); for (ObserverToPropertyMap::iterator iter = m_PropObserverToNode2.begin(); iter != m_PropObserverToNode2.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); - (*iter).second = 0; + (*iter).second = nullptr; } m_PropObserverToNode2.clear(); for (ObserverToPropertyMap::iterator iter = m_PropObserverToNode3.begin(); iter != m_PropObserverToNode3.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); - (*iter).second = 0; + (*iter).second = nullptr; } m_PropObserverToNode3.clear(); for (ObserverToPropertyMap::iterator iter = m_PropObserverToNode4.begin(); iter != m_PropObserverToNode4.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); - (*iter).second = 0; + (*iter).second = nullptr; } m_PropObserverToNode4.clear(); for (ObserverToPropertyMap::iterator iter = m_PropObserverToNode5.begin(); iter != m_PropObserverToNode5.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); - (*iter).second = 0; + (*iter).second = nullptr; } m_PropObserverToNode5.clear(); } void mitk::LevelWindowManager::CreatePropObserverLists() { if (m_DataStorage.IsNull()) // check if data storage is set { itkExceptionMacro("DataStorage not set"); } /* add observers for all relevant nodes */ mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { if ((it->Value().IsNull()) || (it->Value() == m_NodeMarkedToDelete)) { continue; } /* register listener for changes in visible property */ itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long visIdx = it->Value()->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command); m_PropObserverToNode[PropDataPair(visIdx, it->Value())] = it->Value()->GetProperty("visible"); /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long layerIdx = it->Value()->GetProperty("layer")->AddObserver(itk::ModifiedEvent(), command2); m_PropObserverToNode2[PropDataPair(layerIdx, it->Value())] = it->Value()->GetProperty("layer"); /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command3 = itk::ReceptorMemberCommand::New(); command3->SetCallbackFunction(this, &LevelWindowManager::Update); mitk::BaseProperty::Pointer imageRenderingMode = it->Value()->GetProperty("Image Rendering.Mode"); if (imageRenderingMode.IsNotNull()) { unsigned long rendIdx = imageRenderingMode->AddObserver(itk::ModifiedEvent(), command3); m_PropObserverToNode3[PropDataPair(rendIdx, it->Value())] = imageRenderingMode.GetPointer(); } itk::ReceptorMemberCommand::Pointer command4 = itk::ReceptorMemberCommand::New(); command4->SetCallbackFunction(this, &LevelWindowManager::RecaluclateLevelWindowForSelectedComponent); mitk::BaseProperty::Pointer displayedImageComponent = it->Value()->GetProperty("Image.Displayed Component"); if (displayedImageComponent.IsNotNull()) { unsigned long dispIdx = displayedImageComponent->AddObserver(itk::ModifiedEvent(), command4); m_PropObserverToNode4[PropDataPair(dispIdx, it->Value())] = displayedImageComponent.GetPointer(); } itk::ReceptorMemberCommand::Pointer command5 = itk::ReceptorMemberCommand::New(); command5->SetCallbackFunction(this, &LevelWindowManager::Update); mitk::BaseProperty::Pointer imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); if (imgForLvlWin.IsNull()) { it->Value()->SetBoolProperty("imageForLevelWindow", false); imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); } unsigned long lvlWinIdx = imgForLvlWin->AddObserver(itk::ModifiedEvent(), command5); m_PropObserverToNode5[PropDataPair(lvlWinIdx, it->Value())] = it->Value()->GetProperty("imageForLevelWindow"); } } diff --git a/Modules/Core/src/Interactions/mitkVtkInteractorStyle.cxx b/Modules/Core/src/Interactions/mitkVtkInteractorStyle.cxx index c90f82c6f6..43cd32dda0 100644 --- a/Modules/Core/src/Interactions/mitkVtkInteractorStyle.cxx +++ b/Modules/Core/src/Interactions/mitkVtkInteractorStyle.cxx @@ -1,46 +1,46 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkVtkInteractorStyle.h" #include #include vtkStandardNewMacro(mitkVtkInteractorStyle); mitkVtkInteractorStyle::mitkVtkInteractorStyle() : vtkInteractorStyleUser() { } mitkVtkInteractorStyle::~mitkVtkInteractorStyle() { } void mitkVtkInteractorStyle::OnMouseWheelForward() { if (this->HasObserver(vtkCommand::MouseWheelForwardEvent)) { - this->InvokeEvent(vtkCommand::MouseWheelForwardEvent, NULL); + this->InvokeEvent(vtkCommand::MouseWheelForwardEvent, nullptr); } } void mitkVtkInteractorStyle::OnMouseWheelBackward() { if (this->HasObserver(vtkCommand::MouseWheelBackwardEvent)) { - this->InvokeEvent(vtkCommand::MouseWheelBackwardEvent, NULL); + this->InvokeEvent(vtkCommand::MouseWheelBackwardEvent, nullptr); } } diff --git a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp index f40dd2bcdd..7d10db6d7c 100644 --- a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1130 +1,1130 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // MITK #include #include #include #include #include #include #include #include #include #include #include //#include #include "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include // MITK Rendering #include "mitkImageVtkMapper2D.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkNeverTranslucentTexture.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include #include mitk::ImageVtkMapper2D::ImageVtkMapper2D() { } mitk::ImageVtkMapper2D::~ImageVtkMapper2D() { // The 3D RW Mapper (PlaneGeometryDataVtkMapper3D) is listening to this event, // in order to delete the images from the 3D RW. this->InvokeEvent(itk::DeleteEvent()); } // set the two points defining the textured plane according to the dimension and spacing void mitk::ImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float depth = this->CalculateLayerDepth(renderer); // Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct // plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth); // These two points define the axes of the plane in combination with the origin. // Point 1 is the x-axis and point 2 the y-axis. // Each plane is transformed according to the view (axial, coronal and saggital) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], depth); // P1: (xMax, yMin, depth) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); // P2: (xMin, yMax, depth) } float mitk::ImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer *renderer) { // get the clipping range to check how deep into z direction we can render images double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1]; // Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined float depth = -maxRange * 0.01; // divide by 100 int layer = 0; GetDataNode()->GetIntProperty("layer", layer, renderer); // add the layer property for each image to render images with a higher layer on top of the others depth += layer * 10; //*10: keep some room for each image (e.g. for ODFs in between) if (depth > 0.0f) { depth = 0.0f; MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead."; } return depth; } const mitk::Image *mitk::ImageVtkMapper2D::GetInput(void) { return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::ImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::ImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); SetVtkMapperImmediateModeRendering(localStorage->m_Mapper); mitk::Image *image = const_cast(this->GetInput()); mitk::DataNode *datanode = this->GetDataNode(); if (nullptr == image || !image->IsInitialized()) { return; } // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (nullptr == worldGeometry || !worldGeometry->IsValid() || !worldGeometry->HasReferenceGeometry()) { return; } image->Update(); // early out if there is no intersection of the current rendering geometry // and the geometry of the image that is to be rendered. if (!RenderingGeometryIntersectsImage(worldGeometry, image->GetSlicedGeometry())) { // set image to nullptr, to clear the texture in 3D, because // the latest image is used there if the plane is out of the geometry // see bug-13275 localStorage->m_ReslicedImage = nullptr; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); return; } // set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(image); localStorage->m_Reslicer->SetWorldGeometry(worldGeometry); localStorage->m_Reslicer->SetTimeStep(this->GetTimestep()); // set the transformation of the image to adapt reslice axis localStorage->m_Reslicer->SetResliceTransformByGeometry( image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())); // is the geometry of the slice based on the input image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ((image->GetDimension() >= 3) && (image->GetDimension(2) > 1)) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation", renderer); int interpolationMode = VTK_RESLICE_NEAREST; if (resliceInterpolationProperty != nullptr) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch (interpolationMode) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC); break; } } else { localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); } // set the vtk output property to true, makes sure that no unneeded mitk image convertion // is done. localStorage->m_Reslicer->SetVtkOutputRequest(true); // Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if (image->GetPixelType().GetNumberOfComponents() == 1) // for now only single component are allowed { DataNode *dn = renderer->GetCurrentWorldPlaneGeometryNode(); if (dn) { - ResliceMethodProperty *resliceMethodEnumProperty = 0; + ResliceMethodProperty *resliceMethodEnumProperty = nullptr; if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices", renderer) && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); - IntProperty *intProperty = 0; + IntProperty *intProperty = nullptr; if (dn->GetProperty(intProperty, "reslice.thickslices.num", renderer) && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 1; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const PlaneGeometry *planeGeometry = dynamic_cast(worldGeometry); if (thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; const mitk::AbstractTransformGeometry *abstractGeometry = dynamic_cast(worldGeometry); if (abstractGeometry != nullptr) normal = abstractGeometry->GetPlane()->GetNormal(); else { if (planeGeometry != nullptr) { normal = planeGeometry->GetNormal(); } else return; // no fitting geometry set } normal.Normalize(); image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())->WorldToIndex(normal, normInIndex); dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality(3); localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing); localStorage->m_Reslicer->SetOutputExtentZDirection(-thickSlicesNum, 0 + thickSlicesNum); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. localStorage->m_TSFilter->SetThickSliceMode(thickSlicesMode - 1); localStorage->m_TSFilter->SetInputData(localStorage->m_Reslicer->GetVtkOutput()); // vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput(); } else { // this is needed when thick mode was enable bevore. These variable have to be reset to default values localStorage->m_Reslicer->SetOutputDimensionality(2); localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0); localStorage->m_Reslicer->SetOutputExtentZDirection(0, 0); localStorage->m_Reslicer->Modified(); // start the pipeline with updating the largest possible, needed if the geometry of the input has changed localStorage->m_Reslicer->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput(); } // Bounds information for reslicing (only reuqired if reference geometry // is present) // this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; for (auto &sliceBound : sliceBounds) { sliceBound = 0.0; } localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds); // get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing(); // calculate minimum bounding rect of IMAGE in texture { double textureClippingBounds[6]; for (auto &textureClippingBound : textureClippingBounds) { textureClippingBound = 0.0; } // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. mitk::PlaneClipping::CalculateClippedPlaneBounds(image->GetGeometry(), planeGeometry, textureClippingBounds); textureClippingBounds[0] = static_cast(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[1] = static_cast(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[2] = static_cast(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5); textureClippingBounds[3] = static_cast(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5); // clipping bounds for cutting the image localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds); } // get the number of scalar components to distinguish between different image types int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents(); // get the binary property bool binary = false; bool binaryOutline = false; datanode->GetBoolProperty("binary", binary, renderer); if (binary) // binary image { datanode->GetBoolProperty("outline binary", binaryOutline, renderer); if (binaryOutline) // contour rendering { // get pixel type of vtk image itk::ImageIOBase::IOComponentType componentType = static_cast(image->GetPixelType().GetComponentType()); switch (componentType) { case itk::ImageIOBase::UCHAR: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; case itk::ImageIOBase::USHORT: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; default: binaryOutline = false; this->ApplyLookuptable(renderer); MITK_WARN << "Type of all binary images should be unsigned char or unsigned short. Outline does not work on other pixel types!"; } if (binaryOutline) // binary outline is still true --> add outline { float binaryOutlineWidth = 1.0; if (datanode->GetFloatProperty("outline width", binaryOutlineWidth, renderer)) { if (localStorage->m_Actors->GetNumberOfPaths() > 1) { float binaryOutlineShadowWidth = 1.5; datanode->GetFloatProperty("outline shadow width", binaryOutlineShadowWidth, renderer); dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty() ->SetLineWidth(binaryOutlineWidth * binaryOutlineShadowWidth); } localStorage->m_Actor->GetProperty()->SetLineWidth(binaryOutlineWidth); } } } else // standard binary image { if (numberOfComponents != 1) { MITK_ERROR << "Rendering Error: Binary Images with more then 1 component are not supported!"; } } } this->ApplyOpacity(renderer); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->MapColorScalarsThroughLookupTableOff(); int displayedComponent = 0; if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1) { localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent); localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage); localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0)); } else { // connect the input with the levelwindow filter localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage); } // check for texture interpolation property bool textureInterpolation = false; GetDataNode()->GetBoolProperty("texture interpolation", textureInterpolation, renderer); // set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); // connect the texture with the output of the levelwindow filter localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); this->TransformActor(renderer); vtkActor *contourShadowActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); if (binary && binaryOutline) // connect the mapper with the polyData which contains the lines { // We need the contour for the binary outline property as actor localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); localStorage->m_Actor->SetTexture(nullptr); // no texture for contours bool binaryOutlineShadow = false; datanode->GetBoolProperty("outline binary shadow", binaryOutlineShadow, renderer); if (binaryOutlineShadow) { contourShadowActor->SetVisibility(true); } else { contourShadowActor->SetVisibility(false); } } else { // Connect the mapper with the input texture. This is the standard case. // setup the textured plane this->GeneratePlane(renderer, sliceBounds); // set the plane as input for the mapper localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); // set the texture for the actor localStorage->m_Actor->SetTexture(localStorage->m_Texture); contourShadowActor->SetVisibility(false); } // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); LevelWindow levelWindow; this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelwindow"); localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); mitk::LevelWindow opacLevelWindow; if (this->GetDataNode()->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow")) { // pass the opaque level window to the filter localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { // no opaque level window localStorage->m_LevelWindowFilter->SetMinOpacity(0.0); localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0); } } void mitk::ImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float rgb[3] = {1.0f, 1.0f, 1.0f}; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; bool binary = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary && hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.hoveringcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (binary && selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.selectedcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (!binary || (!hover && !selected)) { GetDataNode()->GetColor(rgb, renderer, "color"); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); localStorage->m_Actor->GetProperty()->SetColor(rgbConv); if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1) { float rgb[3] = {1.0f, 1.0f, 1.0f}; mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("outline binary shadow color", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); } } void mitk::ImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float opacity = 1.0f; // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(opacity, renderer, "opacity"); // set the opacity according to the properties localStorage->m_Actor->GetProperty()->SetOpacity(opacity); if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1) { dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty() ->SetOpacity(opacity); } } void mitk::ImageVtkMapper2D::ApplyRenderingMode(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); bool binary = false; this->GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary) // is it a binary image? { // for binary images, we always use our default LuT and map every value to (0,1) // the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window. localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable); } else { // all other image types can make use of the rendering mode int renderingMode = mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(this->GetDataNode()->GetProperty("Image Rendering.Mode", renderer)); if (mode.IsNotNull()) { renderingMode = mode->GetRenderingMode(); } switch (renderingMode) { case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color"; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color"; this->ApplyLookuptable(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); break; default: MITK_ERROR << "No valid 'Image Rendering.Mode' set. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead."; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; } } // we apply color for all images (including binaries). this->ApplyColor(renderer); } void mitk::ImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); vtkLookupTable *usedLookupTable = localStorage->m_ColorLookupTable; // If lookup table or transferfunction use is requested... mitk::LookupTableProperty::Pointer lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable")); if (lookupTableProp.IsNotNull()) // is a lookuptable set? { usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { //"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'. // A default (rainbow) lookup table will be used. // Here have to do nothing. Warning for the user has been removed, due to unwanted console output // in every interation of the rendering. } localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable); } void mitk::ImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer) { mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast( this->GetDataNode()->GetProperty("Image Rendering.Transfer Function", renderer)); if (transferFunctionProp.IsNull()) { MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image " "Rendering.Transfer Function'. Nothing will be done."; return; } LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // pass the transfer function to our level window filter localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction()); localStorage->m_LevelWindowFilter->SetOpacityPiecewiseFunction( transferFunctionProp->GetValue()->GetScalarOpacityFunction()); } void mitk::ImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } mitk::Image *data = const_cast(this->GetInput()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) { return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) // was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty("depthOffset", mitk::FloatProperty::New(0.0), renderer, overwrite); node->AddProperty("outline binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline width", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("outline binary shadow", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline binary shadow color", ColorProperty::New(0.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("outline shadow width", mitk::FloatProperty::New(1.5), renderer, overwrite); if (image->IsRotated()) node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC)); else node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New()); node->AddProperty("texture interpolation", mitk::BoolProperty::New(false)); node->AddProperty("in plane resample extent by geometry", mitk::BoolProperty::New(false)); node->AddProperty("bounding box", mitk::BoolProperty::New(false)); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(); node->AddProperty("Image Rendering.Mode", renderingModeProperty); // Set default grayscale look-up table mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); mitkLut->SetType(mitk::LookupTable::GRAYSCALE); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed if (node->GetStringProperty("dicom.pixel.PhotometricInterpretation", photometricInterpretation)) { // modality provided by DICOM or other reader if (photometricInterpretation.find("MONOCHROME1") != std::string::npos) // meaning: display MINIMUM pixels as WHITE { // Set inverse grayscale look-up table mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); renderingModeProperty->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); // USE lookuptable } // Otherwise do nothing - the default grayscale look-up table has already been set } bool isBinaryImage(false); if (!node->GetBoolProperty("binary", isBinaryImage) && image->GetPixelType().GetNumberOfComponents() == 1) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); if (centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized()) { minValue = centralSliceImage->GetStatistics()->GetScalarValueMin(); maxValue = centralSliceImage->GetStatistics()->GetScalarValueMax(); min2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMin(); max2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMax(); } if ((maxValue == min2ndValue && minValue == max2ndValue) || minValue == maxValue) { // centralSlice is strange, lets look at all data minValue = image->GetStatistics()->GetScalarValueMin(); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } isBinaryImage = (maxValue == min2ndValue && minValue == max2ndValue); } std::string className = image->GetNameOfClass(); if (className != "TensorImage" && className != "OdfImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); if ((pixelType.GetPixelType() == itk::ImageIOBase::VECTOR && numComponents > 1) || numComponents == 2 || numComponents > 4) { node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite); } } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty("opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 1.0, 1.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); } if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) { /* initialize level/window from DICOM tags */ std::string sLevel = ""; std::string sWindow = ""; if (GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1050, "dicom.voilut.WindowCenter", image->GetPropertyList(), sLevel) && GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1051, "dicom.voilut.WindowWidth", image->GetPropertyList(), sWindow)) { float level = atof(sLevel.c_str()); float window = atof(sWindow.c_str()); mitk::LevelWindow contrast; std::string sSmallestPixelValueInSeries; std::string sLargestPixelValueInSeries; if (GetBackwardsCompatibleDICOMProperty(0x0028, 0x0108, "dicom.series.SmallestPixelValueInSeries", image->GetPropertyList(), sSmallestPixelValueInSeries) && GetBackwardsCompatibleDICOMProperty(0x0028, 0x0109, "dicom.series.LargestPixelValueInSeries", image->GetPropertyList(), sLargestPixelValueInSeries)) { float smallestPixelValueInSeries = atof(sSmallestPixelValueInSeries.c_str()); float largestPixelValueInSeries = atof(sLargestPixelValueInSeries.c_str()); contrast.SetRangeMinMax(smallestPixelValueInSeries - 1, largestPixelValueInSeries + 1); // why not a little buffer? // might remedy some l/w widget challenges } else { contrast.SetAuto(static_cast(node->GetData()), false, true); // we need this as a fallback } contrast.SetLevelWindow(level, window, true); node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer); } } if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == nullptr)) && (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) && (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR)) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0, 255); opaclevwin.SetWindowBounds(0, 255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty("opaclevelwindow", prop, renderer); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::ImageVtkMapper2D::LocalStorage *mitk::ImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } template vtkSmartPointer mitk::ImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); // get the min and max index values of each direction int *extent = localStorage->m_ReslicedImage->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int *dims = localStorage->m_ReslicedImage->GetDimensions(); // dimensions of the image int line = dims[0]; // how many pixels per line? int x = xMin; // pixel index x int y = yMin; // pixel index y // get the depth for each contour float depth = CalculateLayerDepth(renderer); vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points // We take the pointer to the first pixel of the image TPixel* currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer()); while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel != 0)) { // check in which direction a line is necessary // a line is added if the neighbor of the current pixel has the value 0 // and if the pixel is located at the edge of the image // if vvvvv not the first line vvvvv if (y > yMin && *(currentPixel - line) == 0) { // x direction - bottom edge of the pixel // add the 2 points vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); // add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel + line) == 0) { // x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the first pixel vvvvv if ((x > xMin || y > yMin) && *(currentPixel - 1) == 0) { // y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last pixel vvvvv if ((y < yMax || (x < xMax)) && *(currentPixel + 1) == 0) { // y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } /* now consider pixels at the edge of the image */ // if vvvvv left edge of image vvvvv if (x == xMin) { // draw left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv right edge of image vvvvv if (x == xMax) { // draw right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv bottom edge of image vvvvv if (y == yMin) { // draw bottom edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv top edge of image vvvvv if (y == yMax) { // draw top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } } // end if currentpixel is set x++; if (x > xMax) { // reached end of line x = xMin; y++; } // Increase the pointer-position to the next pixel. // This is safe, as the while-loop and the x-reset logic above makes // sure we do not exceed the bounds of the image currentPixel++; } // end of while // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); return polyData; } void mitk::ImageVtkMapper2D::TransformActor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); // transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_Actor->SetUserTransform(trans); // transform the origin to center based coordinates, because MITK is center based. localStorage->m_Actor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); if (localStorage->m_Actors->GetNumberOfPaths() > 1) { vtkActor *secondaryActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); secondaryActor->SetUserTransform(trans); secondaryActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } } bool mitk::ImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, SlicedGeometry3D *imageGeometry) { // if either one of the two geometries is nullptr we return true // for safety reasons if (renderingGeometry == nullptr || imageGeometry == nullptr) return true; // get the distance for the first cornerpoint ScalarType initialDistance = renderingGeometry->SignedDistance(imageGeometry->GetCornerPoint(0)); for (int i = 1; i < 8; i++) { mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint(i); // get the distance to the other cornerpoints ScalarType distance = renderingGeometry->SignedDistance(cornerPoint); // if it has not the same signing as the distance of the first point if (initialDistance * distance < 0) { // we have an intersection and return true return true; } } // all distances have the same sign, no intersection and we return false return false; } mitk::ImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::ImageVtkMapper2D::LocalStorage::LocalStorage() : m_VectorComponentExtractor(vtkSmartPointer::New()) { m_LevelWindowFilter = vtkSmartPointer::New(); // Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_BinaryLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_TSFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_ReslicedImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); // the following actions are always the same and thus can be performed // in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); // built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY); m_BinaryLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR); m_ColorLookupTable = mitkLUT->GetVtkLookupTable(); // do not repeat the texture (the image) m_Texture->RepeatOff(); // set the mapper for the actor m_Actor->SetMapper(m_Mapper); vtkSmartPointer outlineShadowActor = vtkSmartPointer::New(); outlineShadowActor->SetMapper(m_Mapper); m_Actors->AddPart(outlineShadowActor); m_Actors->AddPart(m_Actor); } diff --git a/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp b/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp index 9cb4eb1213..64b3135288 100644 --- a/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp @@ -1,710 +1,710 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlaneGeometryDataMapper2D.h" // mitk includes #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include // vtk includes #include #include #include #include #include #include #include #include #include #include /// #include #include #include #include namespace { /// Some simple interval arithmetic template class SimpleInterval { public: SimpleInterval(T start = T(), T end = T()) : m_LowerBoundary(std::min(start, end)), m_UpperBoundary(std::max(start, end)) { } T GetLowerBoundary() const { return m_LowerBoundary; } T GetUpperBoundary() const { return m_UpperBoundary; } bool empty() const { return m_LowerBoundary == m_UpperBoundary; } bool operator<(const SimpleInterval &otherInterval) const { return this->m_UpperBoundary < otherInterval.GetLowerBoundary(); } private: T m_LowerBoundary; T m_UpperBoundary; }; template class IntervalSet { public: typedef SimpleInterval IntervalType; IntervalSet(IntervalType startingInterval) { m_IntervalsContainer.insert(std::move(startingInterval)); } void operator-=(const IntervalType &interval) { // equal_range will find all the intervals in the interval set which intersect with the input interval // due to the nature of operator< of SimpleInterval auto range = m_IntervalsContainer.equal_range(interval); for (auto iter = range.first; iter != range.second;) { auto subtractionResult = SubtractIntervals(*iter, interval); // Remove the old interval from the set iter = m_IntervalsContainer.erase(iter); for (auto &&interval : subtractionResult) { if (!interval.empty()) { // Add the new interval to the set // emplace_hint adds the element at the closest valid place before the hint iterator, // which is exactly where the new interval should be iter = m_IntervalsContainer.insert(iter, std::move(interval)); ++iter; } } } } IntervalSet operator-(const IntervalType &interval) { IntervalSet result = *this; result -= interval; return result; } typedef std::set IntervalsContainer; const IntervalsContainer &getIntervals() const { return m_IntervalsContainer; } private: IntervalsContainer m_IntervalsContainer; std::array SubtractIntervals(const IntervalType &firstInterval, const IntervalType &secondInterval) { assert(secondInterval.GetUpperBoundary() >= firstInterval.GetLowerBoundary() && firstInterval.GetUpperBoundary() >= secondInterval.GetLowerBoundary()); // Non-intersecting intervals should never reach here if (secondInterval.GetLowerBoundary() < firstInterval.GetLowerBoundary()) { if (firstInterval.GetUpperBoundary() < secondInterval.GetUpperBoundary()) { std::array result = {{IntervalType(), IntervalType()}}; return result; // firstInterval completely enclosed } std::array result = { {IntervalType(firstInterval.GetUpperBoundary(), secondInterval.GetUpperBoundary()), IntervalType()}}; return result; // secondInterval removes the beginning of firstInterval } if (firstInterval.GetUpperBoundary() < secondInterval.GetUpperBoundary()) { std::array result = { {IntervalType(firstInterval.GetLowerBoundary(), secondInterval.GetLowerBoundary()), IntervalType()}}; return result; // secondInterval removes the end of firstInterval } std::array result = { {IntervalType(firstInterval.GetLowerBoundary(), secondInterval.GetLowerBoundary()), IntervalType(secondInterval.GetUpperBoundary(), firstInterval.GetUpperBoundary())}}; return result; // secondInterval is completely enclosed in firstInterval and removes the middle } }; } mitk::PlaneGeometryDataMapper2D::AllInstancesContainer mitk::PlaneGeometryDataMapper2D::s_AllInstances; // input for this mapper ( = PlaneGeometryData) const mitk::PlaneGeometryData *mitk::PlaneGeometryDataMapper2D::GetInput() const { return static_cast(GetDataNode()->GetData()); } mitk::PlaneGeometryDataMapper2D::PlaneGeometryDataMapper2D() : m_RenderOrientationArrows(false), m_ArrowOrientationPositive(true), m_DepthValue(1.0f) { s_AllInstances.insert(this); } mitk::PlaneGeometryDataMapper2D::~PlaneGeometryDataMapper2D() { s_AllInstances.erase(this); } vtkProp *mitk::PlaneGeometryDataMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_CrosshairAssembly; } void mitk::PlaneGeometryDataMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); // The PlaneGeometryDataMapper2D mapper is special in that the rendering of // OTHER PlaneGeometryDatas affects how we render THIS PlaneGeometryData // (for the gap at the point where they intersect). A change in any of the // other PlaneGeometryData nodes could mean that we render ourself // differently, so we check for that here. for (AllInstancesContainer::iterator it = s_AllInstances.begin(); it != s_AllInstances.end(); ++it) { bool generateDataRequired = ls->IsGenerateDataRequired(renderer, this, (*it)->GetDataNode()); if (generateDataRequired) break; } ls->UpdateGenerateDataTime(); // Collect all other PlaneGeometryDatas that are being mapped by this mapper m_OtherPlaneGeometries.clear(); for (AllInstancesContainer::iterator it = s_AllInstances.begin(); it != s_AllInstances.end(); ++it) { Self *otherInstance = *it; // Skip ourself if (otherInstance == this) continue; mitk::DataNode *otherNode = otherInstance->GetDataNode(); if (!otherNode) continue; // Skip other PlaneGeometryData nodes that are not visible on this renderer if (!otherNode->IsVisible(renderer)) continue; PlaneGeometryData *otherData = dynamic_cast(otherNode->GetData()); if (!otherData) continue; PlaneGeometry *otherGeometry = dynamic_cast(otherData->GetPlaneGeometry()); if (otherGeometry && !dynamic_cast(otherData->GetPlaneGeometry())) { m_OtherPlaneGeometries.push_back(otherNode); } } CreateVtkCrosshair(renderer); ApplyAllProperties(renderer); } void mitk::PlaneGeometryDataMapper2D::CreateVtkCrosshair(mitk::BaseRenderer *renderer) { bool visible = true; LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_CrosshairActor->SetVisibility(0); ls->m_ArrowActor->SetVisibility(0); ls->m_CrosshairHelperLineActor->SetVisibility(0); GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } PlaneGeometryData::Pointer input = const_cast(this->GetInput()); mitk::DataNode *geometryDataNode = renderer->GetCurrentWorldPlaneGeometryNode(); const PlaneGeometryData *rendererWorldPlaneGeometryData = dynamic_cast(geometryDataNode->GetData()); // intersecting with ourself? if (input.IsNull() || input.GetPointer() == rendererWorldPlaneGeometryData) { return; // nothing to do in this case } const PlaneGeometry *inputPlaneGeometry = dynamic_cast(input->GetPlaneGeometry()); const PlaneGeometry *worldPlaneGeometry = dynamic_cast(rendererWorldPlaneGeometryData->GetPlaneGeometry()); if (worldPlaneGeometry && dynamic_cast(worldPlaneGeometry) == nullptr && inputPlaneGeometry && dynamic_cast(input->GetPlaneGeometry()) == nullptr) { const BaseGeometry *referenceGeometry = inputPlaneGeometry->GetReferenceGeometry(); // calculate intersection of the plane data with the border of the // world geometry rectangle Point3D point1, point2; Line3D crossLine; // Calculate the intersection line of the input plane with the world plane if (worldPlaneGeometry->IntersectionLine(inputPlaneGeometry, crossLine)) { bool hasIntersection = referenceGeometry ? CutCrossLineWithReferenceGeometry(referenceGeometry, crossLine) : CutCrossLineWithPlaneGeometry(inputPlaneGeometry, crossLine); if (!hasIntersection) { return; } point1 = crossLine.GetPoint1(); point2 = crossLine.GetPoint2(); vtkSmartPointer lines = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer linesPolyData = vtkSmartPointer::New(); // Now iterate through all other lines displayed in this window and // calculate the positions of intersection with the line to be // rendered; these positions will be stored in lineParams to form a // gap afterwards. NodesVectorType::iterator otherPlanesIt = m_OtherPlaneGeometries.begin(); NodesVectorType::iterator otherPlanesEnd = m_OtherPlaneGeometries.end(); int gapSize = 32; this->GetDataNode()->GetPropertyValue("Crosshair.Gap Size", gapSize, nullptr); auto intervals = IntervalSet(SimpleInterval(0, 1)); ScalarType lineLength = point1.EuclideanDistanceTo(point2); ScalarType gapInMM = gapSize * renderer->GetScaleFactorMMPerDisplayUnit(); float gapSizeParam = gapInMM / lineLength; if (gapSize != 0) { while (otherPlanesIt != otherPlanesEnd) { bool ignorePlane = false; (*otherPlanesIt)->GetPropertyValue("Crosshair.Ignore", ignorePlane); if (ignorePlane) { ++otherPlanesIt; continue; } PlaneGeometry *otherPlaneGeometry = static_cast( static_cast((*otherPlanesIt)->GetData())->GetPlaneGeometry()); if (otherPlaneGeometry != inputPlaneGeometry && otherPlaneGeometry != worldPlaneGeometry) { double intersectionParam; if (otherPlaneGeometry->IntersectionPointParam(crossLine, intersectionParam) && intersectionParam > 0 && intersectionParam < 1) { Point3D point = crossLine.GetPoint() + intersectionParam * crossLine.GetDirection(); bool intersectionPointInsideOtherPlane = otherPlaneGeometry->HasReferenceGeometry() ? TestPointInReferenceGeometry(otherPlaneGeometry->GetReferenceGeometry(), point) : TestPointInPlaneGeometry(otherPlaneGeometry, point); if (intersectionPointInsideOtherPlane) { intervals -= SimpleInterval(intersectionParam - gapSizeParam, intersectionParam + gapSizeParam); } } } ++otherPlanesIt; } } for (const auto &interval : intervals.getIntervals()) { this->DrawLine(crossLine.GetPoint(interval.GetLowerBoundary()), crossLine.GetPoint(interval.GetUpperBoundary()), lines, points); } // Add the points to the dataset linesPolyData->SetPoints(points); // Add the lines to the dataset linesPolyData->SetLines(lines); Vector3D orthogonalVector; orthogonalVector = inputPlaneGeometry->GetNormal(); worldPlaneGeometry->Project(orthogonalVector, orthogonalVector); orthogonalVector.Normalize(); // Visualize ls->m_Mapper->SetInputData(linesPolyData); ls->m_CrosshairActor->SetMapper(ls->m_Mapper); // Determine if we should draw the area covered by the thick slicing, default is false. // This will also show the area of slices that do not have thick slice mode enabled bool showAreaOfThickSlicing = false; GetDataNode()->GetBoolProperty("reslice.thickslices.showarea", showAreaOfThickSlicing); // determine the pixelSpacing in that direction double thickSliceDistance = SlicedGeometry3D::CalculateSpacing( referenceGeometry ? referenceGeometry->GetSpacing() : inputPlaneGeometry->GetSpacing(), orthogonalVector); - IntProperty *intProperty = 0; + IntProperty *intProperty = nullptr; if (GetDataNode()->GetProperty(intProperty, "reslice.thickslices.num") && intProperty) thickSliceDistance *= intProperty->GetValue() + 0.5; else showAreaOfThickSlicing = false; // not the nicest place to do it, but we have the width of the visible bloc in MM here // so we store it in this fancy property GetDataNode()->SetFloatProperty("reslice.thickslices.sizeinmm", thickSliceDistance * 2); ls->m_CrosshairActor->SetVisibility(1); vtkSmartPointer arrowPolyData = vtkSmartPointer::New(); ls->m_Arrowmapper->SetInputData(arrowPolyData); if (this->m_RenderOrientationArrows) { ScalarType triangleSizeMM = 7.0 * renderer->GetScaleFactorMMPerDisplayUnit(); vtkSmartPointer triangles = vtkSmartPointer::New(); vtkSmartPointer triPoints = vtkSmartPointer::New(); DrawOrientationArrow(triangles, triPoints, triangleSizeMM, orthogonalVector, point1, point2); DrawOrientationArrow(triangles, triPoints, triangleSizeMM, orthogonalVector, point2, point1); arrowPolyData->SetPoints(triPoints); arrowPolyData->SetPolys(triangles); ls->m_ArrowActor->SetVisibility(1); } // Visualize vtkSmartPointer helperlinesPolyData = vtkSmartPointer::New(); ls->m_HelperLinesmapper->SetInputData(helperlinesPolyData); if (showAreaOfThickSlicing) { vtkSmartPointer helperlines = vtkSmartPointer::New(); // vectorToHelperLine defines how to reach the helperLine from the mainLine // got the right direction, so we multiply the width Vector3D vecToHelperLine = orthogonalVector * thickSliceDistance; this->DrawLine(point1 - vecToHelperLine, point2 - vecToHelperLine, helperlines, points); this->DrawLine(point1 + vecToHelperLine, point2 + vecToHelperLine, helperlines, points); // Add the points to the dataset helperlinesPolyData->SetPoints(points); // Add the lines to the dataset helperlinesPolyData->SetLines(helperlines); ls->m_CrosshairActor->GetProperty()->SetLineStipplePattern(0xf0f0); ls->m_CrosshairActor->GetProperty()->SetLineStippleRepeatFactor(1); ls->m_CrosshairHelperLineActor->SetVisibility(1); } } } } bool mitk::PlaneGeometryDataMapper2D::TestPointInPlaneGeometry(const PlaneGeometry *planeGeometry, const Point3D &point) { Point2D mappedPoint; planeGeometry->Map(point, mappedPoint); planeGeometry->WorldToIndex(mappedPoint, mappedPoint); return (planeGeometry->GetBounds()[0] < mappedPoint[0] && mappedPoint[0] < planeGeometry->GetBounds()[1] && planeGeometry->GetBounds()[2] < mappedPoint[1] && mappedPoint[1] < planeGeometry->GetBounds()[3]); } bool mitk::PlaneGeometryDataMapper2D::TestPointInReferenceGeometry(const BaseGeometry *referenceGeometry, const Point3D &point) { return referenceGeometry->IsInside(point); } bool mitk::PlaneGeometryDataMapper2D::CutCrossLineWithPlaneGeometry(const PlaneGeometry *planeGeometry, Line3D &crossLine) { Point2D indexLinePoint; Vector2D indexLineDirection; planeGeometry->Map(crossLine.GetPoint(), indexLinePoint); planeGeometry->Map(crossLine.GetPoint(), crossLine.GetDirection(), indexLineDirection); planeGeometry->WorldToIndex(indexLinePoint, indexLinePoint); planeGeometry->WorldToIndex(indexLineDirection, indexLineDirection); mitk::Point2D intersectionPoints[2]; // Then, clip this line with the (transformed) bounding box of the // reference geometry. int nIntersections = Line3D::RectangleLineIntersection(planeGeometry->GetBounds()[0], planeGeometry->GetBounds()[2], planeGeometry->GetBounds()[1], planeGeometry->GetBounds()[3], indexLinePoint, indexLineDirection, intersectionPoints[0], intersectionPoints[1]); if (nIntersections < 2) { return false; } planeGeometry->IndexToWorld(intersectionPoints[0], intersectionPoints[0]); planeGeometry->IndexToWorld(intersectionPoints[1], intersectionPoints[1]); Point3D point1, point2; planeGeometry->Map(intersectionPoints[0], point1); planeGeometry->Map(intersectionPoints[1], point2); crossLine.SetPoints(point1, point2); return true; } bool mitk::PlaneGeometryDataMapper2D::CutCrossLineWithReferenceGeometry(const BaseGeometry *referenceGeometry, Line3D &crossLine) { Point3D boundingBoxMin, boundingBoxMax; boundingBoxMin = referenceGeometry->GetCornerPoint(0); boundingBoxMax = referenceGeometry->GetCornerPoint(7); Point3D indexLinePoint; Vector3D indexLineDirection; referenceGeometry->WorldToIndex(crossLine.GetPoint(), indexLinePoint); referenceGeometry->WorldToIndex(crossLine.GetDirection(), indexLineDirection); referenceGeometry->WorldToIndex(boundingBoxMin, boundingBoxMin); referenceGeometry->WorldToIndex(boundingBoxMax, boundingBoxMax); Point3D point1, point2; // Then, clip this line with the (transformed) bounding box of the // reference geometry. int nIntersections = Line3D::BoxLineIntersection(boundingBoxMin[0], boundingBoxMin[1], boundingBoxMin[2], boundingBoxMax[0], boundingBoxMax[1], boundingBoxMax[2], indexLinePoint, indexLineDirection, point1, point2); if (nIntersections < 2) { return false; } referenceGeometry->IndexToWorld(point1, point1); referenceGeometry->IndexToWorld(point2, point2); crossLine.SetPoints(point1, point2); return true; } void mitk::PlaneGeometryDataMapper2D::DrawLine(mitk::Point3D p0, mitk::Point3D p1, vtkCellArray *lines, vtkPoints *points) { vtkIdType pidStart = points->InsertNextPoint(p0[0], p0[1], p0[2]); vtkIdType pidEnd = points->InsertNextPoint(p1[0], p1[1], p1[2]); vtkSmartPointer lineVtk = vtkSmartPointer::New(); lineVtk->GetPointIds()->SetId(0, pidStart); lineVtk->GetPointIds()->SetId(1, pidEnd); lines->InsertNextCell(lineVtk); } void mitk::PlaneGeometryDataMapper2D::DrawOrientationArrow(vtkSmartPointer triangles, vtkSmartPointer triPoints, double triangleSizeMM, Vector3D &orthogonalVector, Point3D &point1, Point3D &point2) { // Draw arrows to indicate plane orientation // Vector along line Vector3D v1 = point2 - point1; v1.Normalize(); v1 *= triangleSizeMM; // Orthogonal vector Vector3D v2 = orthogonalVector; v2 *= triangleSizeMM; if (!this->m_ArrowOrientationPositive) v2 *= -1.0; // Initialize remaining triangle coordinates accordingly Point3D p1 = point1 + v1 * 2.0; Point3D p2 = point1 + v1 + v2; vtkIdType t0 = triPoints->InsertNextPoint(point1[0], point1[1], point1[2]); // start of the line vtkIdType t1 = triPoints->InsertNextPoint(p1[0], p1[1], p1[2]); // point on line vtkIdType t2 = triPoints->InsertNextPoint(p2[0], p2[1], p2[2]); // direction point vtkSmartPointer triangle = vtkSmartPointer::New(); triangle->GetPointIds()->SetId(0, t0); triangle->GetPointIds()->SetId(1, t1); triangle->GetPointIds()->SetId(2, t2); triangles->InsertNextCell(triangle); } int mitk::PlaneGeometryDataMapper2D::DetermineThickSliceMode(DataNode *dn, int &thickSlicesNum) { int thickSlicesMode = 0; // determine the state and the extend of the thick-slice mode - mitk::ResliceMethodProperty *resliceMethodEnumProperty = 0; + mitk::ResliceMethodProperty *resliceMethodEnumProperty = nullptr; if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices") && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); - IntProperty *intProperty = 0; + IntProperty *intProperty = nullptr; if (dn->GetProperty(intProperty, "reslice.thickslices.num") && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 0; if (thickSlicesNum > 10) thickSlicesNum = 10; } if (thickSlicesMode == 0) thickSlicesNum = 0; return thickSlicesMode; } void mitk::PlaneGeometryDataMapper2D::ApplyAllProperties(BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ApplyColorAndOpacityProperties2D(renderer, ls->m_CrosshairActor); ApplyColorAndOpacityProperties2D(renderer, ls->m_CrosshairHelperLineActor); ApplyColorAndOpacityProperties2D(renderer, ls->m_ArrowActor); float thickness; this->GetDataNode()->GetFloatProperty("Line width", thickness, renderer); ls->m_CrosshairActor->GetProperty()->SetLineWidth(thickness); ls->m_CrosshairHelperLineActor->GetProperty()->SetLineWidth(thickness); PlaneOrientationProperty *decorationProperty; this->GetDataNode()->GetProperty(decorationProperty, "decoration", renderer); if (decorationProperty != nullptr) { if (decorationProperty->GetPlaneDecoration() == PlaneOrientationProperty::PLANE_DECORATION_POSITIVE_ORIENTATION) { m_RenderOrientationArrows = true; m_ArrowOrientationPositive = true; } else if (decorationProperty->GetPlaneDecoration() == PlaneOrientationProperty::PLANE_DECORATION_NEGATIVE_ORIENTATION) { m_RenderOrientationArrows = true; m_ArrowOrientationPositive = false; } else { m_RenderOrientationArrows = false; } } } void mitk::PlaneGeometryDataMapper2D::ApplyColorAndOpacityProperties2D(BaseRenderer *renderer, vtkActor2D *actor) { float rgba[4] = {1.0f, 1.0f, 1.0f, 1.0f}; DataNode *node = GetDataNode(); // check for color prop and use it for rendering if it exists node->GetColor(rgba, renderer, "color"); // check for opacity prop and use it for rendering if it exists node->GetOpacity(rgba[3], renderer, "opacity"); double drgba[4] = {rgba[0], rgba[1], rgba[2], rgba[3]}; actor->GetProperty()->SetColor(drgba); actor->GetProperty()->SetOpacity(drgba[3]); } void mitk::PlaneGeometryDataMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::IPropertyAliases *aliases = mitk::CoreServices::GetPropertyAliases(); node->AddProperty("Line width", mitk::FloatProperty::New(1), renderer, overwrite); aliases->AddAlias("line width", "Crosshair.Line Width", ""); node->AddProperty("Crosshair.Gap Size", mitk::IntProperty::New(32), renderer, overwrite); node->AddProperty("decoration", mitk::PlaneOrientationProperty::New(PlaneOrientationProperty::PLANE_DECORATION_NONE), renderer, overwrite); aliases->AddAlias("decoration", "Crosshair.Orientation Decoration", ""); Superclass::SetDefaultProperties(node, renderer, overwrite); } void mitk::PlaneGeometryDataMapper2D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { } mitk::PlaneGeometryDataMapper2D::LocalStorage::LocalStorage() { m_CrosshairAssembly = vtkSmartPointer::New(); m_CrosshairActor = vtkSmartPointer::New(); m_ArrowActor = vtkSmartPointer::New(); m_CrosshairHelperLineActor = vtkSmartPointer::New(); m_HelperLinesmapper = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Arrowmapper = vtkSmartPointer::New(); m_CrosshairActor->SetMapper(m_Mapper); m_ArrowActor->SetMapper(m_Arrowmapper); m_CrosshairHelperLineActor->SetMapper(m_HelperLinesmapper); m_CrosshairActor->SetVisibility(0); m_ArrowActor->SetVisibility(0); m_CrosshairHelperLineActor->SetVisibility(0); m_CrosshairAssembly->AddPart(m_CrosshairActor); m_CrosshairAssembly->AddPart(m_ArrowActor); m_CrosshairAssembly->AddPart(m_CrosshairHelperLineActor); vtkCoordinate *tcoord = vtkCoordinate::New(); tcoord->SetCoordinateSystemToWorld(); m_HelperLinesmapper->SetTransformCoordinate(tcoord); m_Mapper->SetTransformCoordinate(tcoord); // tcoord->SetCoordinateSystemToNormalizedDisplay(); m_Arrowmapper->SetTransformCoordinate(tcoord); tcoord->Delete(); } mitk::PlaneGeometryDataMapper2D::LocalStorage::~LocalStorage() { } diff --git a/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp b/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp index ac25920ae4..79f1d68759 100644 --- a/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp +++ b/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp @@ -1,589 +1,589 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlaneGeometryDataVtkMapper3D.h" #include "mitkImageVtkMapper2D.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateOr.h" #include "mitkSmartPointerProperty.h" #include "mitkSurface.h" #include "mitkVtkRepresentationProperty.h" #include "mitkWeakPointerProperty.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkNeverTranslucentTexture.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { PlaneGeometryDataVtkMapper3D::PlaneGeometryDataVtkMapper3D() : m_NormalsActorAdded(false), m_DataStorage(nullptr) { m_EdgeTuber = vtkTubeFilter::New(); m_EdgeMapper = vtkPolyDataMapper::New(); m_SurfaceCreator = PlaneGeometryDataToSurfaceFilter::New(); m_SurfaceCreatorBoundingBox = BoundingBox::New(); m_SurfaceCreatorPointsContainer = BoundingBox::PointsContainer::New(); m_Edges = vtkFeatureEdges::New(); m_Edges->BoundaryEdgesOn(); m_Edges->FeatureEdgesOff(); m_Edges->NonManifoldEdgesOff(); m_Edges->ManifoldEdgesOff(); m_EdgeTransformer = vtkTransformPolyDataFilter::New(); m_NormalsTransformer = vtkTransformPolyDataFilter::New(); m_EdgeActor = vtkActor::New(); m_BackgroundMapper = vtkPolyDataMapper::New(); m_BackgroundActor = vtkActor::New(); m_Prop3DAssembly = vtkAssembly::New(); m_ImageAssembly = vtkAssembly::New(); m_SurfaceCreatorBoundingBox->SetPoints(m_SurfaceCreatorPointsContainer); m_Cleaner = vtkCleanPolyData::New(); m_Cleaner->PieceInvariantOn(); m_Cleaner->ConvertLinesToPointsOn(); m_Cleaner->ConvertPolysToLinesOn(); m_Cleaner->ConvertStripsToPolysOn(); m_Cleaner->PointMergingOn(); // Make sure that the FeatureEdge algorithm is initialized with a "valid" // (though empty) input vtkPolyData *emptyPolyData = vtkPolyData::New(); m_Cleaner->SetInputData(emptyPolyData); emptyPolyData->Delete(); m_Edges->SetInputConnection(m_Cleaner->GetOutputPort()); m_EdgeTransformer->SetInputConnection(m_Edges->GetOutputPort()); m_EdgeTuber->SetInputConnection(m_EdgeTransformer->GetOutputPort()); m_EdgeTuber->SetVaryRadiusToVaryRadiusOff(); m_EdgeTuber->SetNumberOfSides(12); m_EdgeTuber->CappingOn(); m_EdgeMapper->SetInputConnection(m_EdgeTuber->GetOutputPort()); m_EdgeMapper->ScalarVisibilityOff(); m_BackgroundMapper->SetInputData(emptyPolyData); m_BackgroundMapper->Update(); m_EdgeActor->SetMapper(m_EdgeMapper); m_BackgroundActor->GetProperty()->SetAmbient(0.5); m_BackgroundActor->GetProperty()->SetColor(0.0, 0.0, 0.0); m_BackgroundActor->GetProperty()->SetOpacity(0.0); m_BackgroundActor->SetMapper(m_BackgroundMapper); vtkProperty *backfaceProperty = m_BackgroundActor->MakeProperty(); backfaceProperty->SetColor(0.0, 0.0, 0.0); m_BackgroundActor->SetBackfaceProperty(backfaceProperty); backfaceProperty->Delete(); m_FrontHedgeHog = vtkHedgeHog::New(); m_BackHedgeHog = vtkHedgeHog::New(); m_FrontNormalsMapper = vtkPolyDataMapper::New(); m_FrontNormalsMapper->SetInputConnection(m_FrontHedgeHog->GetOutputPort()); m_BackNormalsMapper = vtkPolyDataMapper::New(); m_Prop3DAssembly->AddPart(m_EdgeActor); m_Prop3DAssembly->AddPart(m_ImageAssembly); m_FrontNormalsActor = vtkActor::New(); m_FrontNormalsActor->SetMapper(m_FrontNormalsMapper); m_BackNormalsActor = vtkActor::New(); m_BackNormalsActor->SetMapper(m_BackNormalsMapper); m_ImageMapperDeletedCommand = MemberCommandType::New(); m_ImageMapperDeletedCommand->SetCallbackFunction(this, &PlaneGeometryDataVtkMapper3D::ImageMapperDeletedCallback); } PlaneGeometryDataVtkMapper3D::~PlaneGeometryDataVtkMapper3D() { m_ImageAssembly->Delete(); m_Prop3DAssembly->Delete(); m_EdgeTuber->Delete(); m_EdgeMapper->Delete(); m_EdgeTransformer->Delete(); m_Cleaner->Delete(); m_Edges->Delete(); m_NormalsTransformer->Delete(); m_EdgeActor->Delete(); m_BackgroundMapper->Delete(); m_BackgroundActor->Delete(); m_FrontNormalsMapper->Delete(); m_FrontNormalsActor->Delete(); m_FrontHedgeHog->Delete(); m_BackNormalsMapper->Delete(); m_BackNormalsActor->Delete(); m_BackHedgeHog->Delete(); for (ActorList::iterator it = m_ImageActors.begin(); it != m_ImageActors.end(); ++it) - it->second.m_Actor->ReleaseGraphicsResources(0); + it->second.m_Actor->ReleaseGraphicsResources(nullptr); // Delete entries in m_ImageActors list one by one m_ImageActors.clear(); m_DataStorage = nullptr; } vtkProp *PlaneGeometryDataVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { if ((this->GetDataNode() != nullptr) && (m_ImageAssembly != nullptr)) { // Do not transform the entire Prop3D assembly, but only the image part // here. The colored frame is transformed elsewhere (via m_EdgeTransformer), // since only vertices should be transformed there, not the poly data // itself, to avoid distortion for anisotropic datasets. m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform()); } return m_Prop3DAssembly; } void PlaneGeometryDataVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep())); } const PlaneGeometryData *PlaneGeometryDataVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } void PlaneGeometryDataVtkMapper3D::SetDataStorageForTexture(mitk::DataStorage *storage) { if (storage != nullptr && m_DataStorage != storage) { m_DataStorage = storage; this->Modified(); } } void PlaneGeometryDataVtkMapper3D::ImageMapperDeletedCallback(itk::Object *caller, const itk::EventObject & /*event*/) { ImageVtkMapper2D *imageMapper = dynamic_cast(caller); if ((imageMapper != nullptr)) { if (m_ImageActors.count(imageMapper) > 0) { m_ImageActors[imageMapper].m_Sender = nullptr; // sender is already destroying itself m_ImageActors.erase(imageMapper); } } } void PlaneGeometryDataVtkMapper3D::GenerateDataForRenderer(BaseRenderer *renderer) { SetVtkMapperImmediateModeRendering(m_EdgeMapper); SetVtkMapperImmediateModeRendering(m_BackgroundMapper); // Remove all actors from the assembly, and re-initialize it with the // edge actor m_ImageAssembly->GetParts()->RemoveAllItems(); bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. m_ImageAssembly->VisibilityOff(); m_EdgeActor->VisibilityOff(); return; } // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. m_ImageAssembly->VisibilityOn(); bool drawEdges = true; this->GetDataNode()->GetBoolProperty("draw edges", drawEdges, renderer); m_EdgeActor->SetVisibility(drawEdges); PlaneGeometryData::Pointer input = const_cast(this->GetInput()); if (input.IsNotNull() && (input->GetPlaneGeometry() != nullptr)) { SmartPointerProperty::Pointer surfacecreatorprop; surfacecreatorprop = dynamic_cast(GetDataNode()->GetProperty("surfacegeometry", renderer)); if ((surfacecreatorprop.IsNull()) || (surfacecreatorprop->GetSmartPointer().IsNull()) || ((m_SurfaceCreator = dynamic_cast(surfacecreatorprop->GetSmartPointer().GetPointer())) .IsNull())) { m_SurfaceCreator->PlaceByGeometryOn(); surfacecreatorprop = SmartPointerProperty::New(m_SurfaceCreator); GetDataNode()->SetProperty("surfacegeometry", surfacecreatorprop); } m_SurfaceCreator->SetInput(input); int res; if (GetDataNode()->GetIntProperty("xresolution", res, renderer)) { m_SurfaceCreator->SetXResolution(res); } if (GetDataNode()->GetIntProperty("yresolution", res, renderer)) { m_SurfaceCreator->SetYResolution(res); } double tubeRadius = 1.0; // Radius of tubular edge surrounding plane // Clip the PlaneGeometry with the reference geometry bounds (if available) if (input->GetPlaneGeometry()->HasReferenceGeometry()) { const BaseGeometry *referenceGeometry = input->GetPlaneGeometry()->GetReferenceGeometry(); BoundingBox::PointType boundingBoxMin, boundingBoxMax; boundingBoxMin = referenceGeometry->GetBoundingBox()->GetMinimum(); boundingBoxMax = referenceGeometry->GetBoundingBox()->GetMaximum(); if (referenceGeometry->GetImageGeometry()) { for (unsigned int i = 0; i < 3; ++i) { boundingBoxMin[i] -= 0.5; boundingBoxMax[i] -= 0.5; } } m_SurfaceCreatorPointsContainer->CreateElementAt(0) = boundingBoxMin; m_SurfaceCreatorPointsContainer->CreateElementAt(1) = boundingBoxMax; m_SurfaceCreatorBoundingBox->ComputeBoundingBox(); m_SurfaceCreator->SetBoundingBox(m_SurfaceCreatorBoundingBox); tubeRadius = referenceGeometry->GetDiagonalLength() / 450.0; } // If no reference geometry is available, clip with the current global // bounds else if (m_DataStorage.IsNotNull()) { m_SurfaceCreator->SetBoundingBox(m_DataStorage->ComputeVisibleBoundingBox(nullptr, "includeInBoundingBox")); tubeRadius = sqrt(m_SurfaceCreator->GetBoundingBox()->GetDiagonalLength2()) / 450.0; } // Calculate the surface of the PlaneGeometry m_SurfaceCreator->Update(); Surface *surface = m_SurfaceCreator->GetOutput(); // Check if there's something to display, otherwise return - if ((surface->GetVtkPolyData() == 0) || (surface->GetVtkPolyData()->GetNumberOfCells() == 0)) + if ((surface->GetVtkPolyData() == nullptr) || (surface->GetVtkPolyData()->GetNumberOfCells() == 0)) { m_ImageAssembly->VisibilityOff(); return; } // add a graphical representation of the surface normals if requested DataNode *node = this->GetDataNode(); bool displayNormals = false; bool colorTwoSides = false; bool invertNormals = false; node->GetBoolProperty("draw normals 3D", displayNormals, renderer); node->GetBoolProperty("color two sides", colorTwoSides, renderer); node->GetBoolProperty("invert normals", invertNormals, renderer); // if we want to draw the display normals or render two sides we have to get the colors if (displayNormals || colorTwoSides) { // get colors float frontColor[3] = {0.0, 0.0, 1.0}; node->GetColor(frontColor, renderer, "front color"); float backColor[3] = {1.0, 0.0, 0.0}; node->GetColor(backColor, renderer, "back color"); if (displayNormals) { m_NormalsTransformer->SetInputData(surface->GetVtkPolyData()); m_NormalsTransformer->SetTransform(node->GetVtkTransform(this->GetTimestep())); m_FrontHedgeHog->SetInputConnection(m_NormalsTransformer->GetOutputPort()); m_FrontHedgeHog->SetVectorModeToUseNormal(); m_FrontHedgeHog->SetScaleFactor(invertNormals ? 1.0 : -1.0); m_FrontHedgeHog->Update(); m_FrontNormalsActor->GetProperty()->SetColor(frontColor[0], frontColor[1], frontColor[2]); m_BackHedgeHog->SetInputConnection(m_NormalsTransformer->GetOutputPort()); m_BackHedgeHog->SetVectorModeToUseNormal(); m_BackHedgeHog->SetScaleFactor(invertNormals ? -1.0 : 1.0); m_BackHedgeHog->Update(); m_BackNormalsActor->GetProperty()->SetColor(backColor[0], backColor[1], backColor[2]); // if there is no actor added yet, add one if (!m_NormalsActorAdded) { m_Prop3DAssembly->AddPart(m_FrontNormalsActor); m_Prop3DAssembly->AddPart(m_BackNormalsActor); m_NormalsActorAdded = true; } } // if we don't want to display normals AND there is an actor added remove the actor else if (m_NormalsActorAdded) { m_Prop3DAssembly->RemovePart(m_FrontNormalsActor); m_Prop3DAssembly->RemovePart(m_BackNormalsActor); m_NormalsActorAdded = false; } if (colorTwoSides) { if (!invertNormals) { m_BackgroundActor->GetProperty()->SetColor(backColor[0], backColor[1], backColor[2]); m_BackgroundActor->GetBackfaceProperty()->SetColor(frontColor[0], frontColor[1], frontColor[2]); } else { m_BackgroundActor->GetProperty()->SetColor(frontColor[0], frontColor[1], frontColor[2]); m_BackgroundActor->GetBackfaceProperty()->SetColor(backColor[0], backColor[1], backColor[2]); } } } // Add black background for all images (which may be transparent) m_BackgroundMapper->SetInputData(surface->GetVtkPolyData()); // m_ImageAssembly->AddPart(m_BackgroundActor); LayerSortedActorList layerSortedActors; // Traverse the data tree to find nodes resliced by ImageMapperGL2D // use a predicate to get all data nodes which are "images" or inherit from mitk::Image mitk::TNodePredicateDataType::Pointer predicateAllImages = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetSubset(predicateAllImages); // process all found images for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode *node = it->Value(); if (node != nullptr) this->ProcessNode(node, renderer, surface, layerSortedActors); } // Add all image actors to the assembly, sorted according to // layer property LayerSortedActorList::iterator actorIt; for (actorIt = layerSortedActors.begin(); actorIt != layerSortedActors.end(); ++actorIt) { m_ImageAssembly->AddPart(actorIt->second); } // Configurate the tube-shaped frame: size according to the surface // bounds, color as specified in the plane's properties vtkPolyData *surfacePolyData = surface->GetVtkPolyData(); m_Cleaner->SetInputData(surfacePolyData); m_EdgeTransformer->SetTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep())); // Adjust the radius according to extent m_EdgeTuber->SetRadius(tubeRadius); // Get the plane's color and set the tube properties accordingly ColorProperty::Pointer colorProperty; colorProperty = dynamic_cast(this->GetDataNode()->GetProperty("color")); if (colorProperty.IsNotNull()) { const Color &color = colorProperty->GetColor(); m_EdgeActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue()); } else { m_EdgeActor->GetProperty()->SetColor(1.0, 1.0, 1.0); } m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep())); } VtkRepresentationProperty *representationProperty; this->GetDataNode()->GetProperty(representationProperty, "material.representation", renderer); if (representationProperty != nullptr) m_BackgroundActor->GetProperty()->SetRepresentation(representationProperty->GetVtkRepresentation()); } void PlaneGeometryDataVtkMapper3D::ProcessNode(DataNode *node, BaseRenderer *renderer, Surface *surface, LayerSortedActorList &layerSortedActors) { if (node != nullptr) { // we need to get the information from the 2D mapper to render the texture on the 3D plane ImageVtkMapper2D *imageMapper = dynamic_cast(node->GetMapper(1)); // GetMapper(1) provides the 2D mapper for the data node // if there is a 2D mapper, which is not the standard image mapper... if (!imageMapper && node->GetMapper(1)) { //... check if it is the composite mapper std::string cname(node->GetMapper(1)->GetNameOfClass()); if (!cname.compare("CompositeMapper")) // string.compare returns 0 if the two strings are equal. { // get the standard image mapper. // This is a special case in MITK and does only work for the CompositeMapper. imageMapper = dynamic_cast(node->GetMapper(3)); } } if ((node->IsVisible(renderer)) && imageMapper) { WeakPointerProperty::Pointer rendererProp = dynamic_cast(GetDataNode()->GetPropertyList()->GetProperty("renderer")); if (rendererProp.IsNotNull()) { BaseRenderer::Pointer planeRenderer = dynamic_cast(rendererProp->GetWeakPointer().GetPointer()); // Retrieve and update image to be mapped const ImageVtkMapper2D::LocalStorage *localStorage = imageMapper->GetLocalStorage(planeRenderer); if (planeRenderer.IsNotNull()) { // perform update of imagemapper if needed (maybe the respective 2D renderwindow is not rendered/update // before) imageMapper->Update(planeRenderer); // If it has not been initialized already in a previous pass, // generate an actor and a texture object to // render the image associated with the ImageVtkMapper2D. vtkActor *imageActor; vtkDataSetMapper *dataSetMapper = nullptr; vtkTexture *texture; if (m_ImageActors.count(imageMapper) == 0) { dataSetMapper = vtkDataSetMapper::New(); // Enable rendering without copying the image. dataSetMapper->ImmediateModeRenderingOn(); texture = vtkNeverTranslucentTexture::New(); texture->RepeatOff(); imageActor = vtkActor::New(); imageActor->SetMapper(dataSetMapper); imageActor->SetTexture(texture); imageActor->GetProperty()->SetOpacity( 0.999); // HACK! otherwise VTK wouldn't recognize this as translucent // surface (if LUT values map to alpha < 255 // improvement: apply "opacity" property onle HERE and also in 2D image mapper. DO NOT change LUT to // achieve // translucent images (see method ChangeOpacity in image mapper 2D) // Make imageActor the sole owner of the mapper and texture // objects dataSetMapper->UnRegister(nullptr); texture->UnRegister(nullptr); // Store the actor so that it may be accessed in following // passes. m_ImageActors[imageMapper].Initialize(imageActor, imageMapper, m_ImageMapperDeletedCommand); } else { // Else, retrieve the actor and associated objects from the // previous pass. imageActor = m_ImageActors[imageMapper].m_Actor; dataSetMapper = (vtkDataSetMapper *)imageActor->GetMapper(); texture = imageActor->GetTexture(); } // Set poly data new each time its object changes (e.g. when // switching between planar and curved geometries) if ((dataSetMapper != nullptr) && (dataSetMapper->GetInput() != surface->GetVtkPolyData())) { dataSetMapper->SetInputData(surface->GetVtkPolyData()); } dataSetMapper->Update(); // Check if the m_ReslicedImage is nullptr. // This is the case when no image geometry is met by // the reslicer. In that case, the texture has to be // empty (black) and we don't have to do anything. // See fixed bug #13275 if (localStorage->m_ReslicedImage != nullptr) { texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) texture->MapColorScalarsThroughLookupTableOff(); // re-use properties from the 2D image mapper imageActor->SetProperty(localStorage->m_Actor->GetProperty()); imageActor->GetProperty()->SetAmbient(0.5); // Set texture interpolation on/off bool textureInterpolation = node->IsOn("texture interpolation", renderer); texture->SetInterpolate(textureInterpolation); // Store this actor to be added to the actor assembly, sort // by layer int layer = 1; node->GetIntProperty("layer", layer); layerSortedActors.insert(std::pair(layer, imageActor)); } } } } } } void PlaneGeometryDataVtkMapper3D::ActorInfo::Initialize(vtkActor *actor, itk::Object *sender, itk::Command *command) { m_Actor = actor; m_Sender = sender; // Get informed when ImageMapper object is deleted, so that // the data structures built here can be deleted as well m_ObserverID = sender->AddObserver(itk::DeleteEvent(), command); } PlaneGeometryDataVtkMapper3D::ActorInfo::ActorInfo() : m_Actor(nullptr), m_Sender(nullptr), m_ObserverID(0) {} PlaneGeometryDataVtkMapper3D::ActorInfo::~ActorInfo() { if (m_Sender != nullptr) { m_Sender->RemoveObserver(m_ObserverID); } if (m_Actor != nullptr) { - m_Actor->ReleaseGraphicsResources(0); + m_Actor->ReleaseGraphicsResources(nullptr); m_Actor->Delete(); } } } // namespace mitk diff --git a/Modules/Core/src/Rendering/mitkSurfaceVtkMapper3D.cpp b/Modules/Core/src/Rendering/mitkSurfaceVtkMapper3D.cpp index bc15a7a8ed..f8031d0a11 100644 --- a/Modules/Core/src/Rendering/mitkSurfaceVtkMapper3D.cpp +++ b/Modules/Core/src/Rendering/mitkSurfaceVtkMapper3D.cpp @@ -1,522 +1,522 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurfaceVtkMapper3D.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // VTK #include #include #include #include #include #include #include #include const mitk::Surface *mitk::SurfaceVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } mitk::SurfaceVtkMapper3D::SurfaceVtkMapper3D() { m_GenerateNormals = false; } mitk::SurfaceVtkMapper3D::~SurfaceVtkMapper3D() { } void mitk::SurfaceVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { ls->m_Actor->VisibilityOff(); return; } // // set the input-object at time t for the mapper // mitk::Surface::Pointer input = const_cast(this->GetInput()); vtkSmartPointer polydata = input->GetVtkPolyData(this->GetTimestep()); if (polydata == nullptr) { ls->m_Actor->VisibilityOff(); return; } if (m_GenerateNormals) { ls->m_VtkPolyDataNormals->SetInputData(polydata); ls->m_VtkPolyDataMapper->SetInputConnection(ls->m_VtkPolyDataNormals->GetOutputPort()); } else { bool depthsorting = false; GetDataNode()->GetBoolProperty("Depth Sorting", depthsorting); if (depthsorting) { ls->m_DepthSort->SetInputData(polydata); ls->m_DepthSort->SetCamera(renderer->GetVtkRenderer()->GetActiveCamera()); ls->m_DepthSort->SetDirectionToBackToFront(); ls->m_DepthSort->Update(); ls->m_VtkPolyDataMapper->SetInputConnection(ls->m_DepthSort->GetOutputPort()); } else { ls->m_VtkPolyDataMapper->SetInputData(polydata); } } // // apply properties read from the PropertyList // ApplyAllProperties(renderer, ls->m_Actor); if (visible) ls->m_Actor->VisibilityOn(); } void mitk::SurfaceVtkMapper3D::ResetMapper(BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_Actor->VisibilityOff(); } void mitk::SurfaceVtkMapper3D::ApplyMitkPropertiesToVtkProperty(mitk::DataNode *node, vtkProperty *property, mitk::BaseRenderer *renderer) { // Backface culling { mitk::BoolProperty::Pointer p; node->GetProperty(p, "Backface Culling", renderer); bool useCulling = false; if (p.IsNotNull()) useCulling = p->GetValue(); property->SetBackfaceCulling(useCulling); } // Colors { double ambient[3] = {0.5, 0.5, 0.0}; double diffuse[3] = {0.5, 0.5, 0.0}; double specular[3] = {1.0, 1.0, 1.0}; float coeff_ambient = 0.5f; float coeff_diffuse = 0.5f; float coeff_specular = 0.5f; float power_specular = 10.0f; // Color { mitk::ColorProperty::Pointer p; node->GetProperty(p, "color", renderer); if (p.IsNotNull()) { mitk::Color c = p->GetColor(); ambient[0] = c.GetRed(); ambient[1] = c.GetGreen(); ambient[2] = c.GetBlue(); diffuse[0] = c.GetRed(); diffuse[1] = c.GetGreen(); diffuse[2] = c.GetBlue(); // Setting specular color to the same, make physically no real sense, however vtk rendering slows down, if these // colors are different. specular[0] = c.GetRed(); specular[1] = c.GetGreen(); specular[2] = c.GetBlue(); } } // Ambient { mitk::ColorProperty::Pointer p; node->GetProperty(p, "material.ambientColor", renderer); if (p.IsNotNull()) { mitk::Color c = p->GetColor(); ambient[0] = c.GetRed(); ambient[1] = c.GetGreen(); ambient[2] = c.GetBlue(); } } // Diffuse { mitk::ColorProperty::Pointer p; node->GetProperty(p, "material.diffuseColor", renderer); if (p.IsNotNull()) { mitk::Color c = p->GetColor(); diffuse[0] = c.GetRed(); diffuse[1] = c.GetGreen(); diffuse[2] = c.GetBlue(); } } // Specular { mitk::ColorProperty::Pointer p; node->GetProperty(p, "material.specularColor", renderer); if (p.IsNotNull()) { mitk::Color c = p->GetColor(); specular[0] = c.GetRed(); specular[1] = c.GetGreen(); specular[2] = c.GetBlue(); } } // Ambient coeff { node->GetFloatProperty("material.ambientCoefficient", coeff_ambient, renderer); } // Diffuse coeff { node->GetFloatProperty("material.diffuseCoefficient", coeff_diffuse, renderer); } // Specular coeff { node->GetFloatProperty("material.specularCoefficient", coeff_specular, renderer); } // Specular power { node->GetFloatProperty("material.specularPower", power_specular, renderer); } property->SetAmbient(coeff_ambient); property->SetDiffuse(coeff_diffuse); property->SetSpecular(coeff_specular); property->SetSpecularPower(power_specular); property->SetAmbientColor(ambient); property->SetDiffuseColor(diffuse); property->SetSpecularColor(specular); } // Render mode { // Opacity { float opacity = 1.0f; if (node->GetOpacity(opacity, renderer)) property->SetOpacity(opacity); } // Wireframe line width { float lineWidth = 1; node->GetFloatProperty("material.wireframeLineWidth", lineWidth, renderer); property->SetLineWidth(lineWidth); } // Point size { float pointSize = 1.0f; node->GetFloatProperty("material.pointSize", pointSize, renderer); property->SetPointSize(pointSize); } // Representation { mitk::VtkRepresentationProperty::Pointer p; node->GetProperty(p, "material.representation", renderer); if (p.IsNotNull()) property->SetRepresentation(p->GetVtkRepresentation()); } // Interpolation { mitk::VtkInterpolationProperty::Pointer p; node->GetProperty(p, "material.interpolation", renderer); if (p.IsNotNull()) property->SetInterpolation(p->GetVtkInterpolation()); } } } void mitk::SurfaceVtkMapper3D::ApplyAllProperties(mitk::BaseRenderer *renderer, vtkActor * /*actor*/) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); Superclass::ApplyColorAndOpacityProperties(renderer, ls->m_Actor); // VTK Properties ApplyMitkPropertiesToVtkProperty(this->GetDataNode(), ls->m_Actor->GetProperty(), renderer); mitk::TransferFunctionProperty::Pointer transferFuncProp; this->GetDataNode()->GetProperty(transferFuncProp, "Surface.TransferFunction", renderer); if (transferFuncProp.IsNotNull()) { ls->m_VtkPolyDataMapper->SetLookupTable(transferFuncProp->GetValue()->GetColorTransferFunction()); } mitk::LookupTableProperty::Pointer lookupTableProp; this->GetDataNode()->GetProperty(lookupTableProp, "LookupTable", renderer); if (lookupTableProp.IsNotNull()) { ls->m_VtkPolyDataMapper->SetLookupTable(lookupTableProp->GetLookupTable()->GetVtkLookupTable()); } mitk::LevelWindow levelWindow; if (this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelWindow")) { ls->m_VtkPolyDataMapper->SetScalarRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); } else if (this->GetDataNode()->GetLevelWindow(levelWindow, renderer)) { ls->m_VtkPolyDataMapper->SetScalarRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); } bool scalarVisibility = false; this->GetDataNode()->GetBoolProperty("scalar visibility", scalarVisibility); ls->m_VtkPolyDataMapper->SetScalarVisibility((scalarVisibility ? 1 : 0)); if (scalarVisibility) { mitk::VtkScalarModeProperty *scalarMode; if (this->GetDataNode()->GetProperty(scalarMode, "scalar mode", renderer)) ls->m_VtkPolyDataMapper->SetScalarMode(scalarMode->GetVtkScalarMode()); else ls->m_VtkPolyDataMapper->SetScalarModeToDefault(); bool colorMode = false; this->GetDataNode()->GetBoolProperty("color mode", colorMode); ls->m_VtkPolyDataMapper->SetColorMode((colorMode ? 1 : 0)); double scalarsMin = 0; this->GetDataNode()->GetDoubleProperty("ScalarsRangeMinimum", scalarsMin, renderer); double scalarsMax = 1.0; this->GetDataNode()->GetDoubleProperty("ScalarsRangeMaximum", scalarsMax, renderer); ls->m_VtkPolyDataMapper->SetScalarRange(scalarsMin, scalarsMax); } mitk::SmartPointerProperty::Pointer imagetextureProp = dynamic_cast(GetDataNode()->GetProperty("Surface.Texture", renderer)); if (imagetextureProp.IsNotNull()) { mitk::Image *miktTexture = dynamic_cast(imagetextureProp->GetSmartPointer().GetPointer()); vtkSmartPointer vtkTxture = vtkSmartPointer::New(); // Either select the first slice of a volume if (miktTexture->GetDimension(2) > 1) { MITK_WARN << "3D Textures are not supported by VTK and MITK. The first slice of the volume will be used instead!"; mitk::ImageSliceSelector::Pointer sliceselector = mitk::ImageSliceSelector::New(); sliceselector->SetSliceNr(0); sliceselector->SetChannelNr(0); sliceselector->SetTimeNr(0); sliceselector->SetInput(miktTexture); sliceselector->Update(); vtkTxture->SetInputData(sliceselector->GetOutput()->GetVtkImageData()); } else // or just use the 2D image { vtkTxture->SetInputData(miktTexture->GetVtkImageData()); } // pass the texture to the actor ls->m_Actor->SetTexture(vtkTxture); if (ls->m_VtkPolyDataMapper->GetInput()->GetPointData()->GetTCoords() == nullptr) { MITK_ERROR << "Surface.Texture property was set, but there are no texture coordinates. Please provide texture " "coordinates for the vtkPolyData via vtkPolyData->GetPointData()->SetTCoords()."; } // if no texture is set, this will also remove a previously used texture // and reset the actor to it's default behaviour } else { - ls->m_Actor->SetTexture(0); + ls->m_Actor->SetTexture(nullptr); } // deprecated settings bool deprecatedUseCellData = false; this->GetDataNode()->GetBoolProperty("deprecated useCellDataForColouring", deprecatedUseCellData); bool deprecatedUsePointData = false; this->GetDataNode()->GetBoolProperty("deprecated usePointDataForColouring", deprecatedUsePointData); if (deprecatedUseCellData) { ls->m_VtkPolyDataMapper->SetColorModeToDefault(); ls->m_VtkPolyDataMapper->SetScalarRange(0, 255); ls->m_VtkPolyDataMapper->ScalarVisibilityOn(); ls->m_VtkPolyDataMapper->SetScalarModeToUseCellData(); ls->m_Actor->GetProperty()->SetSpecular(1); ls->m_Actor->GetProperty()->SetSpecularPower(50); ls->m_Actor->GetProperty()->SetInterpolationToPhong(); } else if (deprecatedUsePointData) { float scalarsMin = 0; if (dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMinimum")) != nullptr) scalarsMin = dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMinimum"))->GetValue(); float scalarsMax = 0.1; if (dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMaximum")) != nullptr) scalarsMax = dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMaximum"))->GetValue(); ls->m_VtkPolyDataMapper->SetScalarRange(scalarsMin, scalarsMax); ls->m_VtkPolyDataMapper->SetColorModeToMapScalars(); ls->m_VtkPolyDataMapper->ScalarVisibilityOn(); ls->m_Actor->GetProperty()->SetSpecular(1); ls->m_Actor->GetProperty()->SetSpecularPower(50); ls->m_Actor->GetProperty()->SetInterpolationToPhong(); } int deprecatedScalarMode = VTK_COLOR_MODE_DEFAULT; if (this->GetDataNode()->GetIntProperty("deprecated scalar mode", deprecatedScalarMode, renderer)) { ls->m_VtkPolyDataMapper->SetScalarMode(deprecatedScalarMode); ls->m_VtkPolyDataMapper->ScalarVisibilityOn(); ls->m_Actor->GetProperty()->SetSpecular(1); ls->m_Actor->GetProperty()->SetSpecularPower(50); } // Check whether one or more ClippingProperty objects have been defined for // this node. Check both renderer specific and global property lists, since // properties in both should be considered. const PropertyList::PropertyMap *rendererProperties = this->GetDataNode()->GetPropertyList(renderer)->GetMap(); const PropertyList::PropertyMap *globalProperties = this->GetDataNode()->GetPropertyList(nullptr)->GetMap(); // Add clipping planes (if any) ls->m_ClippingPlaneCollection->RemoveAllItems(); PropertyList::PropertyMap::const_iterator it; for (it = rendererProperties->begin(); it != rendererProperties->end(); ++it) { this->CheckForClippingProperty(renderer, (*it).second.GetPointer()); } for (it = globalProperties->begin(); it != globalProperties->end(); ++it) { this->CheckForClippingProperty(renderer, (*it).second.GetPointer()); } if (ls->m_ClippingPlaneCollection->GetNumberOfItems() > 0) { ls->m_VtkPolyDataMapper->SetClippingPlanes(ls->m_ClippingPlaneCollection); } else { ls->m_VtkPolyDataMapper->RemoveAllClippingPlanes(); } } vtkProp *mitk::SurfaceVtkMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_Actor; } void mitk::SurfaceVtkMapper3D::CheckForClippingProperty(mitk::BaseRenderer *renderer, mitk::BaseProperty *property) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ClippingProperty *clippingProperty = dynamic_cast(property); if ((clippingProperty != nullptr) && (clippingProperty->GetClippingEnabled())) { const Point3D &origin = clippingProperty->GetOrigin(); const Vector3D &normal = clippingProperty->GetNormal(); vtkSmartPointer clippingPlane = vtkSmartPointer::New(); clippingPlane->SetOrigin(origin[0], origin[1], origin[2]); clippingPlane->SetNormal(normal[0], normal[1], normal[2]); ls->m_ClippingPlaneCollection->AddItem(clippingPlane); } } void mitk::SurfaceVtkMapper3D::SetDefaultPropertiesForVtkProperty(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { // Shading { node->AddProperty("material.wireframeLineWidth", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("material.pointSize", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("material.ambientCoefficient", mitk::FloatProperty::New(0.05f), renderer, overwrite); node->AddProperty("material.diffuseCoefficient", mitk::FloatProperty::New(0.9f), renderer, overwrite); node->AddProperty("material.specularCoefficient", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("material.specularPower", mitk::FloatProperty::New(16.0f), renderer, overwrite); node->AddProperty("material.representation", mitk::VtkRepresentationProperty::New(), renderer, overwrite); node->AddProperty("material.interpolation", mitk::VtkInterpolationProperty::New(), renderer, overwrite); } } void mitk::SurfaceVtkMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("color", mitk::ColorProperty::New(1.0f, 1.0f, 1.0f), renderer, overwrite); node->AddProperty("opacity", mitk::FloatProperty::New(1.0), renderer, overwrite); mitk::SurfaceVtkMapper3D::SetDefaultPropertiesForVtkProperty(node, renderer, overwrite); // Shading node->AddProperty("scalar visibility", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("color mode", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("scalar mode", mitk::VtkScalarModeProperty::New(), renderer, overwrite); mitk::Surface::Pointer surface = dynamic_cast(node->GetData()); if (surface.IsNotNull()) { - if ((surface->GetVtkPolyData() != 0) && (surface->GetVtkPolyData()->GetPointData() != nullptr) && - (surface->GetVtkPolyData()->GetPointData()->GetScalars() != 0)) + if ((surface->GetVtkPolyData() != nullptr) && (surface->GetVtkPolyData()->GetPointData() != nullptr) && + (surface->GetVtkPolyData()->GetPointData()->GetScalars() != nullptr)) { node->AddProperty("scalar visibility", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("color mode", mitk::BoolProperty::New(true), renderer, overwrite); } } // Backface culling node->AddProperty("Backface Culling", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("Depth Sorting", mitk::BoolProperty::New(false), renderer, overwrite); mitk::CoreServices::GetPropertyDescriptions()->AddDescription( "Depth Sorting", "Enables correct rendering for transparent objects by ordering polygons according to the distance " "to the camera. It is not recommended to enable this property for large surfaces (rendering might " "be slow)."); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/Core/src/Rendering/mitkVtkEventProvider.cpp b/Modules/Core/src/Rendering/mitkVtkEventProvider.cpp index a17d50d41a..3ece9ccc4e 100644 --- a/Modules/Core/src/Rendering/mitkVtkEventProvider.cpp +++ b/Modules/Core/src/Rendering/mitkVtkEventProvider.cpp @@ -1,244 +1,244 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkVtkEventProvider.h" #include "mitkVtkEventAdapter.h" #include #include #include #include #include #include "mitkInteractionEvent.h" #define VTKEVENTPROVIDER_INFO MBI_INFO("mitk.core.vtkeventprovider") #define VTKEVENTPROVIDER_WARN MBI_WARN("mitk.core.vtkeventprovider") #define VTKEVENTPROVIDER_ERROR MBI_ERROR("mitk.core.vtkeventprovider") #define VTKEVENTPROVIDER_DEBUG MBI_DEBUG("mitk.core.vtkeventprovider") namespace mitk { vtkStandardNewMacro(vtkEventProvider); } //---------------------------------------------------------------------------- mitk::vtkEventProvider::vtkEventProvider() { // priority of the observer/command; we want MITK events processed in the very beginning this->Priority = 99999.99; // take over the processing of delete and keypress events from the superclass this->EventCallbackCommand->SetCallback(vtkEventProvider::ProcessEvents); // Set/Get the passive observer flag. If this is set to true, this // indicates that this command does not change the state of the // system in any way. Passive observers are processed first, and // are not called even when another command has focus. this->EventCallbackCommand->SetPassiveObserver(1); // get events first // mouse move AddInteractionEvent(vtkCommand::MouseMoveEvent); // mouse press AddInteractionEvent(vtkCommand::LeftButtonPressEvent); AddInteractionEvent(vtkCommand::MiddleButtonPressEvent); AddInteractionEvent(vtkCommand::RightButtonPressEvent); // mouse release AddInteractionEvent(vtkCommand::LeftButtonReleaseEvent); AddInteractionEvent(vtkCommand::MiddleButtonReleaseEvent); AddInteractionEvent(vtkCommand::RightButtonReleaseEvent); // wheel event AddInteractionEvent(vtkCommand::MouseWheelBackwardEvent); AddInteractionEvent(vtkCommand::MouseWheelForwardEvent); // key press event AddInteractionEvent(vtkCommand::KeyPressEvent); } mitk::vtkEventProvider::~vtkEventProvider() { - this->SetInteractor(0); + this->SetInteractor(nullptr); } void mitk::vtkEventProvider::SetMitkRenderWindow(mitk::RenderWindow *renWin) { m_RenderWindow = renWin; } mitk::RenderWindow *mitk::vtkEventProvider::GetRenderWindow() { return m_RenderWindow; } void mitk::vtkEventProvider::SetEnabled(int enabling) { if (!this->Interactor) { VTKEVENTPROVIDER_ERROR << "The interactor must be set prior to enabling/disabling widget"; return; } if (enabling) //---------------------------------------------------------- { VTKEVENTPROVIDER_DEBUG << "Enabling widget"; if (this->Enabled) // already enabled, just return { return; } this->Enabled = 1; // listen to all event types specified in m_InteractionEventsVector vtkRenderWindowInteractor *i = this->Interactor; InteractionEventsVectorType::iterator it; for (it = m_InteractionEventsVector.begin(); it != m_InteractionEventsVector.end(); ++it) { // add observer to interactorStyle i->GetInteractorStyle()->AddObserver((vtkCommand::EventIds)(*it), this->EventCallbackCommand, this->Priority); } this->InvokeEvent(vtkCommand::EnableEvent, nullptr); } else // disabling----------------------------------------------------------- { VTKEVENTPROVIDER_DEBUG << "Disabling widget"; if (!this->Enabled) // already disabled, just return { return; } this->Enabled = 0; // don't listen for events any more this->Interactor->RemoveObserver(this->EventCallbackCommand); // this->Interactor->HandleEventLoop = 0; this->InvokeEvent(vtkCommand::DisableEvent, nullptr); } } //---------------------------------------------------------------------------- // This adds the keypress event observer and the delete event observer void mitk::vtkEventProvider::SetInteractor(vtkRenderWindowInteractor *i) { if (i == this->Interactor) { return; } // if we already have an Interactor then stop observing it if (this->Interactor) this->SetEnabled(0); // disable the old interactor this->Interactor = i; this->Modified(); } //---------------------------------------------------------------------------- void mitk::vtkEventProvider::ProcessEvents(vtkObject *object, unsigned long event, void *clientData, void *vtkNotUsed(callData)) { vtkEventProvider *self = reinterpret_cast(clientData); vtkRenderWindowInteractor *rwi = static_cast(object)->GetInteractor(); // base renderer mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(self->GetRenderWindow()->GetVtkRenderWindow()); switch (event) { // key press case vtkCommand::KeyPressEvent: { VTKEVENTPROVIDER_DEBUG << "key press event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptInteractionKeyEvent(baseRenderer, event, rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } // mouse events case vtkCommand::MouseMoveEvent: { VTKEVENTPROVIDER_DEBUG << "mouse move event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptMouseMoveEvent(baseRenderer, event, rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } case vtkCommand::LeftButtonPressEvent: case vtkCommand::MiddleButtonPressEvent: case vtkCommand::RightButtonPressEvent: { VTKEVENTPROVIDER_DEBUG << "mouse press event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptMousePressEvent(baseRenderer, event, rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } case vtkCommand::LeftButtonReleaseEvent: case vtkCommand::MiddleButtonReleaseEvent: case vtkCommand::RightButtonReleaseEvent: { VTKEVENTPROVIDER_DEBUG << "mouse release event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptMouseReleaseEvent(baseRenderer, event, rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } // mouse WHEEL case vtkCommand::MouseWheelForwardEvent: case vtkCommand::MouseWheelBackwardEvent: { VTKEVENTPROVIDER_DEBUG << "mouse wheel event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptMouseWheelEvent(baseRenderer, event, rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } default: VTKEVENTPROVIDER_INFO << "VTK event not mapped properly."; break; } } void mitk::vtkEventProvider::RemoveInteractionEvent(unsigned long ievent) { InteractionEventsVectorType::iterator it; if (m_InteractionEventsVector.size() > 0) { it = std::find(m_InteractionEventsVector.begin(), m_InteractionEventsVector.end(), ievent); if (it != m_InteractionEventsVector.end()) { m_InteractionEventsVector.erase(it); return; } } } void mitk::vtkEventProvider::AddInteractionEvent(unsigned long ievent) { // Remove event if it already exists RemoveInteractionEvent(ievent); m_InteractionEventsVector.push_back(ievent); } diff --git a/Modules/Core/src/Rendering/mitkVtkPropRenderer.cpp b/Modules/Core/src/Rendering/mitkVtkPropRenderer.cpp index 6309fc8ca8..15d12c26fb 100644 --- a/Modules/Core/src/Rendering/mitkVtkPropRenderer.cpp +++ b/Modules/Core/src/Rendering/mitkVtkPropRenderer.cpp @@ -1,680 +1,680 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkVtkPropRenderer.h" // MAPPERS #include "mitkCameraController.h" #include "mitkImageVtkMapper2D.h" #include "mitkMapper.h" #include "mitkPlaneGeometryDataVtkMapper3D.h" #include "mitkVtkMapper.h" #include #include #include #include #include #include #include #include #include #include // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::VtkPropRenderer::VtkPropRenderer(const char *name, vtkRenderWindow *renWin, mitk::RenderingManager *rm, mitk::BaseRenderer::RenderingMode::Type renderingMode) : BaseRenderer(name, renWin, rm, renderingMode), m_CameraInitializedForMapperID(0) { didCount = false; m_WorldPointPicker = vtkWorldPointPicker::New(); m_PointPicker = vtkPointPicker::New(); m_PointPicker->SetTolerance(0.0025); m_CellPicker = vtkCellPicker::New(); m_CellPicker->SetTolerance(0.0025); mitk::PlaneGeometryDataVtkMapper3D::Pointer geometryMapper = mitk::PlaneGeometryDataVtkMapper3D::New(); m_CurrentWorldPlaneGeometryMapper = geometryMapper; m_CurrentWorldPlaneGeometryNode->SetMapper(2, geometryMapper); m_LightKit = vtkLightKit::New(); m_LightKit->AddLightsToRenderer(m_VtkRenderer); m_PickingMode = WorldPointPicking; m_TextRenderer = vtkRenderer::New(); m_TextRenderer->SetRenderWindow(renWin); m_TextRenderer->SetInteractive(0); m_TextRenderer->SetErase(0); } /*! \brief Destructs the VtkPropRenderer. */ mitk::VtkPropRenderer::~VtkPropRenderer() { // Workaround for GLDisplayList Bug { m_MapperID = 0; checkState(); } if (m_LightKit != nullptr) m_LightKit->Delete(); if (m_VtkRenderer != nullptr) { m_CameraController = nullptr; m_VtkRenderer->Delete(); m_VtkRenderer = nullptr; } else m_CameraController = nullptr; if (m_WorldPointPicker != nullptr) m_WorldPointPicker->Delete(); if (m_PointPicker != nullptr) m_PointPicker->Delete(); if (m_CellPicker != nullptr) m_CellPicker->Delete(); if (m_TextRenderer != nullptr) m_TextRenderer->Delete(); } void mitk::VtkPropRenderer::SetDataStorage(mitk::DataStorage *storage) { if (storage == nullptr || storage == m_DataStorage) return; BaseRenderer::SetDataStorage(storage); static_cast(m_CurrentWorldPlaneGeometryMapper.GetPointer()) ->SetDataStorageForTexture(m_DataStorage.GetPointer()); // Compute the geometry from the current data tree bounds and set it as world geometry this->SetWorldGeometryToDataStorageBounds(); } bool mitk::VtkPropRenderer::SetWorldGeometryToDataStorageBounds() { if (m_DataStorage.IsNull()) return false; // initialize world geometry mitk::TimeGeometry::Pointer geometry = m_DataStorage->ComputeVisibleBoundingGeometry3D(nullptr, "includeInBoundingBox"); if (geometry.IsNull()) return false; this->SetWorldTimeGeometry(geometry); this->GetVtkRenderer()->ResetCamera(); this->GetCameraController()->Fit(); this->Modified(); return true; } /*! \brief Called by the vtkMitkRenderProp in order to start MITK rendering process. */ int mitk::VtkPropRenderer::Render(mitk::VtkPropRenderer::RenderType type) { // Do we have objects to render? if (this->GetEmptyWorldGeometry()) return 0; if (m_DataStorage.IsNull()) return 0; // Update mappers and prepare mapper queue if (type == VtkPropRenderer::Opaque) this->PrepareMapperQueue(); // go through the generated list and let the sorted mappers paint for (auto it = m_MappersMap.cbegin(); it != m_MappersMap.cend(); it++) { Mapper *mapper = (*it).second; mapper->MitkRender(this, type); } // Render text if (type == VtkPropRenderer::Overlay) { if (m_TextCollection.size() > 0) { m_TextRenderer->SetViewport(this->GetVtkRenderer()->GetViewport()); for (TextMapType::iterator it = m_TextCollection.begin(); it != m_TextCollection.end(); ++it) m_TextRenderer->AddViewProp((*it).second); m_TextRenderer->Render(); } } return 1; } /*! \brief PrepareMapperQueue iterates the datatree PrepareMapperQueue iterates the datatree in order to find mappers which shall be rendered. Also, it sortes the mappers wrt to their layer. */ void mitk::VtkPropRenderer::PrepareMapperQueue() { // variable for counting LOD-enabled mappers m_NumberOfVisibleLODEnabledMappers = 0; // Do we have to update the mappers ? if (m_LastUpdateTime < GetMTime() || m_LastUpdateTime < this->GetCurrentWorldPlaneGeometry()->GetMTime()) { Update(); } else if (m_MapperID >= 1 && m_MapperID < 6) Update(); // remove all text properties before mappers will add new ones m_TextRenderer->RemoveAllViewProps(); for (unsigned int i = 0; i < m_TextCollection.size(); i++) { m_TextCollection[i]->Delete(); } m_TextCollection.clear(); // clear priority_queue m_MappersMap.clear(); int mapperNo = 0; // DataStorage if (m_DataStorage.IsNull()) return; DataStorage::SetOfObjects::ConstPointer allObjects = m_DataStorage->GetAll(); for (DataStorage::SetOfObjects::ConstIterator it = allObjects->Begin(); it != allObjects->End(); ++it) { const DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; const mitk::Mapper::Pointer mapper = node->GetMapper(m_MapperID); if (mapper.IsNull()) continue; bool visible = true; node->GetVisibility(visible, this, "visible"); // The information about LOD-enabled mappers is required by RenderingManager if (mapper->IsLODEnabled(this) && visible) { ++m_NumberOfVisibleLODEnabledMappers; } // mapper without a layer property get layer number 1 int layer = 1; node->GetIntProperty("layer", layer, this); int nr = (layer << 16) + mapperNo; m_MappersMap.insert(std::pair(nr, mapper)); mapperNo++; } } void mitk::VtkPropRenderer::Update(mitk::DataNode *datatreenode) { if (datatreenode != nullptr) { mitk::Mapper::Pointer mapper = datatreenode->GetMapper(m_MapperID); if (mapper.IsNotNull()) { if (GetCurrentWorldPlaneGeometry()->IsValid()) { mapper->Update(this); { VtkMapper *vtkmapper = dynamic_cast(mapper.GetPointer()); if (vtkmapper != nullptr) { vtkmapper->UpdateVtkTransform(this); } } } } } } void mitk::VtkPropRenderer::Update() { if (m_DataStorage.IsNull()) return; mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetAll(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) Update(it->Value()); Modified(); m_LastUpdateTime = GetMTime(); } /*! \brief This method is called from the two Constructors */ void mitk::VtkPropRenderer::InitRenderer(vtkRenderWindow *renderWindow) { BaseRenderer::InitRenderer(renderWindow); vtkCallbackCommand *renderCallbackCommand = vtkCallbackCommand::New(); renderCallbackCommand->SetCallback(VtkPropRenderer::RenderingCallback); renderWindow->GetInteractor()->AddObserver(vtkCommand::RenderEvent, renderCallbackCommand); renderCallbackCommand->Delete(); if (renderWindow == nullptr) { m_InitNeeded = false; m_ResizeNeeded = false; return; } m_InitNeeded = true; m_ResizeNeeded = true; m_LastUpdateTime = 0; } void mitk::VtkPropRenderer::RenderingCallback(vtkObject *caller, unsigned long, void *, void *) { vtkRenderWindowInteractor *renderWindowInteractor = dynamic_cast(caller); if (!renderWindowInteractor) return; mitk::BaseRenderer *renderer = mitk::BaseRenderer::GetInstance(renderWindowInteractor->GetRenderWindow()); if (renderer) renderer->RequestUpdate(); } /*! \brief Resize the OpenGL Window */ void mitk::VtkPropRenderer::Resize(int w, int h) { BaseRenderer::Resize(w, h); m_RenderingManager->RequestUpdate(this->GetRenderWindow()); } void mitk::VtkPropRenderer::InitSize(int w, int h) { m_RenderWindow->SetSize(w, h); Superclass::InitSize(w, h); Modified(); Update(); if (m_VtkRenderer != nullptr) { int w = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); m_VtkRenderer->ResetCamera(); vtkObject::SetGlobalWarningDisplay(w); } this->GetCameraController()->Fit(); } int mitk::VtkPropRenderer::WriteSimpleText( std::string text, double posX, double posY, double color1, double color2, double color3, float opacity) { this->GetVtkRenderer()->ViewToDisplay(); if (!text.empty()) { Point2D p; vtkTextActor *textActor = vtkTextActor::New(); textActor->SetDisplayPosition(posX, posY); textActor->SetInput(text.c_str()); textActor->SetTextScaleModeToNone(); textActor->GetTextProperty()->SetColor(color1, color2, color3); // TODO: Read color from node property textActor->GetTextProperty()->SetOpacity(opacity); int text_id = m_TextCollection.size(); m_TextCollection.insert(TextMapType::value_type(text_id, textActor)); return text_id; } else { return -1; } } void mitk::VtkPropRenderer::SetMapperID(const MapperSlotId mapperId) { if (m_MapperID != mapperId) Superclass::SetMapperID(mapperId); // Workaround for GL Displaylist Bug checkState(); } /*! \brief Activates the current renderwindow. */ void mitk::VtkPropRenderer::MakeCurrent() { if (m_RenderWindow != nullptr) m_RenderWindow->MakeCurrent(); } void mitk::VtkPropRenderer::PickWorldPoint(const mitk::Point2D &displayPoint, mitk::Point3D &worldPoint) const { if (this->GetRenderWindow()->GetNeverRendered() != 0) return; // somebody called picking before we ever rendered; cannot have enough information yet switch (m_PickingMode) { case (WorldPointPicking): { m_WorldPointPicker->Pick(displayPoint[0], displayPoint[1], 0, m_VtkRenderer); vtk2itk(m_WorldPointPicker->GetPickPosition(), worldPoint); break; } case (PointPicking): { m_PointPicker->Pick(displayPoint[0], displayPoint[1], 0, m_VtkRenderer); vtk2itk(m_PointPicker->GetPickPosition(), worldPoint); break; } case (CellPicking): { m_CellPicker->Pick(displayPoint[0], displayPoint[1], 0, m_VtkRenderer); vtk2itk(m_CellPicker->GetPickPosition(), worldPoint); break; } } // todo: is this picking in 2D renderwindows? // Superclass::PickWorldPoint(displayPoint, worldPoint); } mitk::DataNode *mitk::VtkPropRenderer::PickObject(const Point2D &displayPosition, Point3D &worldPosition) const { m_CellPicker->InitializePickList(); // Iterate over all DataStorage objects to determine all vtkProps intended // for picking DataStorage::SetOfObjects::ConstPointer allObjects = m_DataStorage->GetAll(); for (DataStorage::SetOfObjects::ConstIterator it = allObjects->Begin(); it != allObjects->End(); ++it) { const DataNode *node = it->Value(); if (node == nullptr) continue; bool pickable = false; node->GetBoolProperty("pickable", pickable); if (!pickable) continue; VtkMapper *mapper = dynamic_cast(node->GetMapper(m_MapperID)); if (mapper == nullptr) continue; vtkProp *prop = mapper->GetVtkProp((mitk::BaseRenderer *)this); if (prop == nullptr) continue; m_CellPicker->AddPickList(prop); } // Do the picking and retrieve the picked vtkProp (if any) m_CellPicker->PickFromListOn(); m_CellPicker->Pick(displayPosition[0], displayPosition[1], 0.0, m_VtkRenderer); m_CellPicker->PickFromListOff(); vtk2itk(m_CellPicker->GetPickPosition(), worldPosition); vtkProp *prop = m_CellPicker->GetViewProp(); if (prop == nullptr) { return nullptr; } // Iterate over all DataStorage objects to determine if the retrieved // vtkProp is owned by any associated mapper. for (DataStorage::SetOfObjects::ConstIterator it = allObjects->Begin(); it != allObjects->End(); ++it) { DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; mitk::Mapper *mapper = node->GetMapper(m_MapperID); if (mapper == nullptr) continue; mitk::VtkMapper *vtkmapper = dynamic_cast(mapper); if (vtkmapper) { // if vtk-based, then ... if (vtkmapper->HasVtkProp(prop, const_cast(this))) { return node; } } } return nullptr; } // todo: is this 2D renderwindow picking? // return Superclass::PickObject( displayPosition, worldPosition ); vtkTextProperty *mitk::VtkPropRenderer::GetTextLabelProperty(int text_id) { return this->m_TextCollection[text_id]->GetTextProperty(); } void mitk::VtkPropRenderer::InitPathTraversal() { if (m_DataStorage.IsNotNull()) { this->UpdatePaths(); this->m_Paths->InitTraversal(); } } void mitk::VtkPropRenderer::UpdatePaths() { if (m_DataStorage.IsNull()) { return; } if (GetMTime() > m_PathTime || (m_Paths != nullptr && m_Paths->GetMTime() > m_PathTime)) { // Create the list to hold all the paths m_Paths = vtkSmartPointer::New(); DataStorage::SetOfObjects::ConstPointer objects = m_DataStorage->GetAll(); for (DataStorage::SetOfObjects::const_iterator iter = objects->begin(); iter != objects->end(); ++iter) { vtkSmartPointer onePath = vtkSmartPointer::New(); Mapper *mapper = (*iter)->GetMapper(BaseRenderer::Standard3D); if (mapper) { VtkMapper *vtkmapper = dynamic_cast(mapper); { vtkProp *prop = vtkmapper->GetVtkProp(this); if (prop && prop->GetVisibility()) { // add to assembly path onePath->AddNode(prop, prop->GetMatrix()); m_Paths->AddItem(onePath); } } } } m_PathTime.Modified(); } } int mitk::VtkPropRenderer::GetNumberOfPaths() { UpdatePaths(); return m_Paths->GetNumberOfItems(); } vtkAssemblyPath *mitk::VtkPropRenderer::GetNextPath() { - return m_Paths ? m_Paths->GetNextItem() : 0; + return m_Paths ? m_Paths->GetNextItem() : nullptr; } void mitk::VtkPropRenderer::ReleaseGraphicsResources(vtkWindow * /*renWin*/) { if (m_DataStorage.IsNull()) return; DataStorage::SetOfObjects::ConstPointer allObjects = m_DataStorage->GetAll(); for (DataStorage::SetOfObjects::const_iterator iter = allObjects->begin(); iter != allObjects->end(); ++iter) { DataNode::Pointer node = *iter; if (node.IsNull()) continue; Mapper *mapper = node->GetMapper(m_MapperID); if (mapper) { VtkMapper *vtkmapper = dynamic_cast(mapper); if (vtkmapper) vtkmapper->ReleaseGraphicsResources(this); } } } const vtkWorldPointPicker *mitk::VtkPropRenderer::GetWorldPointPicker() const { return m_WorldPointPicker; } const vtkPointPicker *mitk::VtkPropRenderer::GetPointPicker() const { return m_PointPicker; } const vtkCellPicker *mitk::VtkPropRenderer::GetCellPicker() const { return m_CellPicker; } mitk::VtkPropRenderer::MappersMapType mitk::VtkPropRenderer::GetMappersMap() const { return m_MappersMap; } // Workaround for GL Displaylist bug static int glWorkAroundGlobalCount = 0; bool mitk::VtkPropRenderer::useImmediateModeRendering() { return glWorkAroundGlobalCount > 1; } void mitk::VtkPropRenderer::checkState() { if (m_MapperID == Standard3D) { if (!didCount) { didCount = true; glWorkAroundGlobalCount++; if (glWorkAroundGlobalCount == 2) { MITK_INFO << "Multiple 3D Renderwindows active...: turning Immediate Rendering ON for legacy mappers"; // vtkMapper::GlobalImmediateModeRenderingOn(); } } } else { if (didCount) { didCount = false; glWorkAroundGlobalCount--; if (glWorkAroundGlobalCount == 1) { MITK_INFO << "Single 3D Renderwindow active...: turning Immediate Rendering OFF for legacy mappers"; // vtkMapper::GlobalImmediateModeRenderingOff(); } } } } //### Contains all methods which are neceassry before each VTK Render() call void mitk::VtkPropRenderer::PrepareRender() { if (this->GetMapperID() != m_CameraInitializedForMapperID) { Initialize2DvtkCamera(); // Set parallel projection etc. } GetCameraController()->AdjustCameraToPlane(); } bool mitk::VtkPropRenderer::Initialize2DvtkCamera() { if (this->GetMapperID() == Standard3D) { // activate parallel projection for 2D this->GetVtkRenderer()->GetActiveCamera()->SetParallelProjection(false); vtkSmartPointer style = vtkSmartPointer::New(); this->GetRenderWindow()->GetInteractor()->SetInteractorStyle(style); this->GetRenderWindow()->GetInteractor()->EnableRenderOff(); m_CameraInitializedForMapperID = Standard3D; } else if (this->GetMapperID() == Standard2D) { // activate parallel projection for 2D this->GetVtkRenderer()->GetActiveCamera()->SetParallelProjection(true); // turn the light out in the scene in order to render correct grey values. // TODO Implement a property for light in the 2D render windows (in another method) this->GetVtkRenderer()->RemoveAllLights(); vtkSmartPointer style = vtkSmartPointer::New(); this->GetRenderWindow()->GetInteractor()->SetInteractorStyle(style); this->GetRenderWindow()->GetInteractor()->EnableRenderOff(); m_CameraInitializedForMapperID = Standard2D; } return true; } diff --git a/Modules/Core/test/mitkDataStorageTest.cpp b/Modules/Core/test/mitkDataStorageTest.cpp index 8d95c5c18b..929571597b 100644 --- a/Modules/Core/test/mitkDataStorageTest.cpp +++ b/Modules/Core/test/mitkDataStorageTest.cpp @@ -1,878 +1,878 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "mitkColorProperty.h" #include "mitkDataNode.h" #include "mitkGroupTagProperty.h" #include "mitkImage.h" #include "mitkReferenceCountWatcher.h" #include "mitkStringProperty.h" #include "mitkSurface.h" #include "mitkDataStorage.h" #include "mitkIOUtil.h" #include "mitkMessage.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateData.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateDimension.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateProperty.h" #include "mitkNodePredicateSource.h" #include "mitkStandaloneDataStorage.h" //#include "mitkPicFileReader.h" #include "mitkTestingMacros.h" void TestDataStorage(mitk::DataStorage *ds, std::string filename); namespace mitk { class TestStandaloneDataStorage : public StandaloneDataStorage { public: mitkClassMacro(TestStandaloneDataStorage, mitk::DataStorage); itkFactorylessNewMacro(Self) itkCloneMacro(Self) std::map GetModifiedObserverTags() const { return m_NodeModifiedObserverTags; } std::map GetDeletedObserverTags() const { return m_NodeDeleteObserverTags; } protected: TestStandaloneDataStorage() {} }; } class DSEventReceiver // Helper class for event testing { public: const mitk::DataNode *m_NodeAdded; const mitk::DataNode *m_NodeRemoved; DSEventReceiver() : m_NodeAdded(nullptr), m_NodeRemoved(nullptr) {} void OnAdd(const mitk::DataNode *node) { m_NodeAdded = node; } void OnRemove(const mitk::DataNode *node) { m_NodeRemoved = node; } }; /// /// \brief a class for checking if the datastorage is really thread safe /// /// Therefore it listens to a node contained in the datastorage. when this node /// gets removed and deleted, this class gets informed by calling OnObjectDelete(). /// in OnObjectDelete() an empty node gets added. this must not cause a deadlock /// struct ItkDeleteEventListener { - ItkDeleteEventListener(mitk::DataStorage *ds) : m_Node(0), m_DataStorage(ds), m_DeleteObserverTag(0) {} + ItkDeleteEventListener(mitk::DataStorage *ds) : m_Node(nullptr), m_DataStorage(ds), m_DeleteObserverTag(0) {} void SetNode(mitk::DataNode *_Node) { if (m_Node) return; m_Node = _Node; itk::MemberCommand::Pointer onObjectDelete = itk::MemberCommand::New(); onObjectDelete->SetCallbackFunction(this, &ItkDeleteEventListener::OnObjectDelete); m_DeleteObserverTag = m_Node->AddObserver(itk::DeleteEvent(), onObjectDelete); } void OnObjectDelete(const itk::Object * /*caller*/, const itk::EventObject &) { mitk::DataNode::Pointer node = mitk::DataNode::New(); m_DataStorage->Add(node); // SHOULD NOT CAUSE A DEADLOCK! m_DataStorage->Remove(node); // tidy up: remove the empty node again - m_Node = 0; + m_Node = nullptr; } protected: mitk::DataNode *m_Node; mitk::DataStorage::Pointer m_DataStorage; unsigned int m_DeleteObserverTag; }; //## Documentation //## main testing method //## NOTE: the current Singleton implementation of DataTreeStorage will lead to crashes if a testcase fails //## and therefore mitk::DataStorage::ShutdownSingleton() is not called. int mitkDataStorageTest(int argc, char *argv[]) { MITK_TEST_BEGIN("DataStorageTest"); // muellerm: test observer tag remove mitk::TestStandaloneDataStorage::Pointer testDS = mitk::TestStandaloneDataStorage::New(); mitk::DataNode::Pointer n1 = mitk::DataNode::New(); testDS->Add(n1); MITK_TEST_CONDITION_REQUIRED(testDS->GetModifiedObserverTags().size() == 1, "Testing if modified" " observer was added."); MITK_TEST_CONDITION_REQUIRED(testDS->GetDeletedObserverTags().size() == 1, "Testing if delete" " observer was added."); testDS->Remove(n1); MITK_TEST_CONDITION_REQUIRED(testDS->GetModifiedObserverTags().size() == 0, "Testing if modified" " observer was removed."); MITK_TEST_CONDITION_REQUIRED(testDS->GetDeletedObserverTags().size() == 0, "Testing if delete" " observer was removed."); /* Create StandaloneDataStorage */ MITK_TEST_OUTPUT(<< "Create StandaloneDataStorage : "); mitk::StandaloneDataStorage::Pointer sds; try { sds = mitk::StandaloneDataStorage::New(); MITK_TEST_CONDITION_REQUIRED(sds.IsNotNull(), "Testing Instatiation"); } catch (...) { MITK_TEST_FAILED_MSG(<< "Exception during creation of StandaloneDataStorage"); } MITK_TEST_OUTPUT(<< "Testing StandaloneDataStorage: "); MITK_TEST_CONDITION_REQUIRED(argc > 1, "Testing correct test invocation"); TestDataStorage(sds, argv[1]); // TODO: Add specific StandaloneDataStorage Tests here sds = nullptr; MITK_TEST_END(); } //##Documentation //## @brief Test for the DataStorage class and its associated classes (e.g. the predicate classes) //## This method will be called once for each subclass of DataStorage void TestDataStorage(mitk::DataStorage *ds, std::string filename) { /* DataStorage valid? */ MITK_TEST_CONDITION_REQUIRED(ds != nullptr, "DataStorage valid?"); // Take the ItkImageFile Reader for the .nrrd data format. // (was previously pic which is now deprecated format) mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(filename)[0].GetPointer()); // create some DataNodes to fill the ds mitk::DataNode::Pointer n1 = mitk::DataNode::New(); // node with image and name property // mitk::Image::Pointer image = mitk::Image::New(); // unsigned int imageDimensions[] = { 10, 10, 10, 10 }; // mitk::PixelType pt(typeid(int)); // image->Initialize( pt, 4, imageDimensions ); n1->SetData(image); n1->SetProperty("name", mitk::StringProperty::New("Node 1 - Image Node")); mitk::DataStorage::SetOfObjects::Pointer parents1 = mitk::DataStorage::SetOfObjects::New(); mitk::DataNode::Pointer n2 = mitk::DataNode::New(); // node with surface and name and color properties mitk::Surface::Pointer surface = mitk::Surface::New(); n2->SetData(surface); n2->SetProperty("name", mitk::StringProperty::New("Node 2 - Surface Node")); mitk::Color color; color.Set(1.0f, 1.0f, 0.0f); n2->SetColor(color); n2->SetProperty("Resection Proposal 1", mitk::GroupTagProperty::New()); mitk::DataStorage::SetOfObjects::Pointer parents2 = mitk::DataStorage::SetOfObjects::New(); parents2->InsertElement(0, n1); // n1 (image node) is source of n2 (surface node) mitk::DataNode::Pointer n3 = mitk::DataNode::New(); // node without data but with name property n3->SetProperty("name", mitk::StringProperty::New("Node 3 - Empty Node")); n3->SetProperty("Resection Proposal 1", mitk::GroupTagProperty::New()); n3->SetProperty("Resection Proposal 2", mitk::GroupTagProperty::New()); mitk::DataStorage::SetOfObjects::Pointer parents3 = mitk::DataStorage::SetOfObjects::New(); parents3->InsertElement(0, n2); // n2 is source of n3 mitk::DataNode::Pointer n4 = mitk::DataNode::New(); // node without data but with color property n4->SetColor(color); n4->SetProperty("Resection Proposal 2", mitk::GroupTagProperty::New()); mitk::DataStorage::SetOfObjects::Pointer parents4 = mitk::DataStorage::SetOfObjects::New(); parents4->InsertElement(0, n2); parents4->InsertElement(1, n3); // n2 and n3 are sources of n4 mitk::DataNode::Pointer n5 = mitk::DataNode::New(); // extra node n5->SetProperty("name", mitk::StringProperty::New("Node 5")); try /* adding objects */ { /* Add an object */ ds->Add(n1, parents1); MITK_TEST_CONDITION_REQUIRED((ds->GetAll()->Size() == 1) && (ds->GetAll()->GetElement(0) == n1), "Testing Adding a new object"); /* Check exception on adding the same object again */ MITK_TEST_OUTPUT(<< "Check exception on adding the same object again: "); MITK_TEST_FOR_EXCEPTION(..., ds->Add(n1, parents1)); MITK_TEST_CONDITION(ds->GetAll()->Size() == 1, "Test if object count is correct after exception"); /* Add an object that has a source object */ ds->Add(n2, parents2); MITK_TEST_CONDITION_REQUIRED(ds->GetAll()->Size() == 2, "Testing Adding an object that has a source object"); /* Add some more objects needed for further tests */ ds->Add(n3, parents3); // n3 object that has name property and one parent ds->Add(n4, parents4); // n4 object that has color property ds->Add(n5); // n5 has no parents MITK_TEST_CONDITION_REQUIRED(ds->GetAll()->Size() == 5, "Adding some more objects needed for further tests"); } catch (...) { MITK_TEST_FAILED_MSG(<< "Exeption during object creation"); } try /* object retrieval methods */ { /* Requesting all Objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetAll(); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((stlAll.size() == 5) // check if all tree nodes are in resultset && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n5) != stlAll.end()), "Testing GetAll()"); } /* Requesting a named object */ { mitk::NodePredicateProperty::Pointer predicate( mitk::NodePredicateProperty::New("name", mitk::StringProperty::New("Node 2 - Surface Node"))); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n2), "Requesting a named object"); } /* Requesting objects of specific data type */ { mitk::NodePredicateDataType::Pointer predicate(mitk::NodePredicateDataType::New("Image")); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects of specific data type") } /* Requesting objects of specific dimension */ { mitk::NodePredicateDimension::Pointer predicate(mitk::NodePredicateDimension::New(4)); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects of specific dimension") } /* Requesting objects with specific data object */ { mitk::NodePredicateData::Pointer predicate(mitk::NodePredicateData::New(image)); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n1), "Requesting objects with specific data object") } /* Requesting objects with nullptr data */ { mitk::NodePredicateData::Pointer predicate(mitk::NodePredicateData::New(nullptr)); mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 3) && (std::find(all->begin(), all->end(), n3) != all->end()) && (std::find(all->begin(), all->end(), n4) != all->end()) && (std::find(all->begin(), all->end(), n5) != all->end()), "Requesting objects with nullptr data"); } /* Requesting objects that meet a conjunction criteria */ { mitk::NodePredicateDataType::Pointer p1 = mitk::NodePredicateDataType::New("Surface"); mitk::NodePredicateProperty::Pointer p2 = mitk::NodePredicateProperty::New("color", mitk::ColorProperty::New(color)); mitk::NodePredicateAnd::Pointer predicate = mitk::NodePredicateAnd::New(); predicate->AddPredicate(p1); predicate->AddPredicate(p2); // objects must be of datatype "Surface" and have red color (= n2) const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 1) && (all->GetElement(0) == n2), "Requesting objects that meet a conjunction criteria"); } /* Requesting objects that meet a disjunction criteria */ { mitk::NodePredicateDataType::Pointer p1(mitk::NodePredicateDataType::New("Image")); mitk::NodePredicateProperty::Pointer p2( mitk::NodePredicateProperty::New("color", mitk::ColorProperty::New(color))); mitk::NodePredicateOr::Pointer predicate = mitk::NodePredicateOr::New(); predicate->AddPredicate(p1); predicate->AddPredicate(p2); // objects must be of datatype "Surface" or have red color (= n1, n2, n4) const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); MITK_TEST_CONDITION((all->Size() == 3) && (std::find(all->begin(), all->end(), n1) != all->end()) && (std::find(all->begin(), all->end(), n2) != all->end()) && (std::find(all->begin(), all->end(), n4) != all->end()), "Requesting objects that meet a disjunction criteria"); } /* Requesting objects that do not meet a criteria */ { mitk::ColorProperty::Pointer cp = mitk::ColorProperty::New(color); mitk::NodePredicateProperty::Pointer proppred(mitk::NodePredicateProperty::New("color", cp)); mitk::NodePredicateNot::Pointer predicate(mitk::NodePredicateNot::New(proppred)); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(predicate); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 3) // check if correct objects are in resultset && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n5) != stlAll.end()), "Requesting objects that do not meet a criteria"); } /* Requesting *direct* source objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n3, nullptr, true); // Get direct parents of n3 (=n2) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 1) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()), "Requesting *direct* source objects"); } /* Requesting *all* source objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n3, nullptr, false); // Get all parents of n3 (= n1 + n2) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 2) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()), "Requesting *all* source objects"); // check if n1 and n2 are the resultset } /* Requesting *all* sources of object with multiple parents */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, nullptr, false); // Get all parents of n4 (= n1 + n2 + n3) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n1 and n2 and n3 are the resultset , "Requesting *all* sources of object with multiple parents"); } /* Requesting *direct* derived objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, nullptr, true); // Get direct childs of n1 (=n2) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 1) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) // check if n1 is the resultset , "Requesting *direct* derived objects"); } ///* Requesting *direct* derived objects with multiple parents/derivations */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n2, nullptr, true); // Get direct childs of n2 (=n3 + n4) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 2) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n3 is the resultset && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()) // check if n4 is the resultset , "Requesting *direct* derived objects with multiple parents/derivations"); } //* Requesting *all* derived objects */ { const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, nullptr, false); // Get all childs of n1 (=n2, n3, n4) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()), "Requesting *all* derived objects"); } /* Checking for circular source relationships */ { parents1->InsertElement(0, n4); // make n1 derived from n4 (which is derived from n2, which is derived from n1) const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources( n4, nullptr, false); // Get all parents of n4 (= n1 + n2 + n3, not n4 itself and not multiple versions of the nodes!) std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION( (all->Size() == 3) && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) // check if n1 and n2 and n3 are the resultset , "Checking for circular source relationships"); } ///* Checking for circular derivation relationships can not be performed, because the internal derivations /// datastructure // can not be accessed from the outside. (Therefore it should not be possible to create these circular relations // */ //* Checking GroupTagProperty */ { mitk::GroupTagProperty::Pointer tp = mitk::GroupTagProperty::New(); mitk::NodePredicateProperty::Pointer pred(mitk::NodePredicateProperty::New("Resection Proposal 1", tp)); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(pred); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 2) // check if n2 and n3 are in resultset && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()), "Checking GroupTagProperty"); } /* Checking GroupTagProperty 2 */ { mitk::GroupTagProperty::Pointer tp = mitk::GroupTagProperty::New(); mitk::NodePredicateProperty::Pointer pred(mitk::NodePredicateProperty::New("Resection Proposal 2", tp)); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSubset(pred); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 2) // check if n3 and n4 are in resultset && (std::find(stlAll.begin(), stlAll.end(), n3) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()), "Checking GroupTagProperty 2"); } /* Checking direct sources with condition */ { mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("Surface"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, true); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 1) // check if n2 is in resultset && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()), "checking direct sources with condition"); } /* Checking all sources with condition */ { mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("Image"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, false); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 1) // check if n1 is in resultset && (std::find(stlAll.begin(), stlAll.end(), n1) != stlAll.end()), "Checking all sources with condition"); } /* Checking all sources with condition with empty resultset */ { mitk::NodePredicateDataType::Pointer pred = mitk::NodePredicateDataType::New("VesselTree"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetSources(n4, pred, false); MITK_TEST_CONDITION(all->Size() == 0, "Checking all sources with condition with empty resultset"); // check if resultset is empty } /* Checking direct derivations with condition */ { mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("color"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, pred, true); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 1) // check if n2 is in resultset && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()), "Checking direct derivations with condition"); } /* Checking all derivations with condition */ { mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("color"); const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetDerivations(n1, pred, false); std::vector stlAll = all->CastToSTLConstContainer(); MITK_TEST_CONDITION((all->Size() == 2) // check if n2 and n4 are in resultset && (std::find(stlAll.begin(), stlAll.end(), n2) != stlAll.end()) && (std::find(stlAll.begin(), stlAll.end(), n4) != stlAll.end()), "Checking direct derivations with condition"); } /* Checking named node method */ MITK_TEST_CONDITION(ds->GetNamedNode("Node 2 - Surface Node") == n2, "Checking named node method"); MITK_TEST_CONDITION(ds->GetNamedNode(std::string("Node 2 - Surface Node")) == n2, "Checking named node(std::string) method"); /* Checking named node method with wrong name */ MITK_TEST_CONDITION(ds->GetNamedNode("This name does not exist") == nullptr, "Checking named node method with wrong name"); /* Checking named object method */ MITK_TEST_CONDITION(ds->GetNamedObject("Node 1 - Image Node") == image, "Checking named object method"); MITK_TEST_CONDITION(ds->GetNamedObject(std::string("Node 1 - Image Node")) == image, "Checking named object(std::string) method"); /* Checking named object method with wrong DataType */ MITK_TEST_CONDITION(ds->GetNamedObject("Node 1 - Image Node") == nullptr, "Checking named object method with wrong DataType"); /* Checking named object method with wrong name */ MITK_TEST_CONDITION(ds->GetNamedObject("This name does not exist") == nullptr, "Checking named object method with wrong name"); /* Checking GetNamedDerivedNode with valid name and direct derivation only */ MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 2 - Surface Node", n1, true) == n2, "Checking GetNamedDerivedNode with valid name & direct derivation only"); /* Checking GetNamedDerivedNode with invalid Name and direct derivation only */ MITK_TEST_CONDITION(ds->GetNamedDerivedNode("wrong name", n1, true) == nullptr, "Checking GetNamedDerivedNode with invalid name & direct derivation only"); /* Checking GetNamedDerivedNode with invalid Name and direct derivation only */ MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 3 - Empty Node", n1, false) == n3, "Checking GetNamedDerivedNode with invalid name & direct derivation only"); /* Checking GetNamedDerivedNode with valid Name but direct derivation only */ MITK_TEST_CONDITION(ds->GetNamedDerivedNode("Node 3 - Empty Node", n1, true) == nullptr, "Checking GetNamedDerivedNode with valid Name but direct derivation only"); /* Checking GetNode with valid predicate */ { mitk::NodePredicateDataType::Pointer p(mitk::NodePredicateDataType::New("Image")); MITK_TEST_CONDITION(ds->GetNode(p) == n1, "Checking GetNode with valid predicate"); } /* Checking GetNode with invalid predicate */ { mitk::NodePredicateDataType::Pointer p(mitk::NodePredicateDataType::New("PointSet")); MITK_TEST_CONDITION(ds->GetNode(p) == nullptr, "Checking GetNode with invalid predicate"); } } // object retrieval methods catch (...) { MITK_TEST_FAILED_MSG(<< "Exeption during object retrieval (GetXXX() Methods)"); } try /* object removal methods */ { /* Checking removal of a node without relations */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); int refCountbeforeDS = watcher->GetReferenceCount(); ds->Add(extra); MITK_TEST_CONDITION(ds->GetNamedNode("extra") == extra, "Adding extra node"); ds->Remove(extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr) && (refCountbeforeDS == watcher->GetReferenceCount()), "Checking removal of a node without relations"); extra = nullptr; } /* Checking removal of a node with a parent */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); int refCountbeforeDS = watcher->GetReferenceCount(); ds->Add(extra, n1); // n1 is parent of extra MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1 , "Adding extra node"); ds->Remove(extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr) && (refCountbeforeDS == watcher->GetReferenceCount()) && (ds->GetDerivations(n1)->Size() == 1), "Checking removal of a node with a parent"); extra = nullptr; } /* Checking removal of a node with two parents */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); int refCountbeforeDS = watcher->GetReferenceCount(); mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New(); p->push_back(n1); p->push_back(n2); ds->Add(extra, p); // n1 and n2 are parents of extra MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1 && (ds->GetDerivations(n2)->Size() == 3), "add extra node"); ds->Remove(extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == nullptr) && (refCountbeforeDS == watcher->GetReferenceCount()) && (ds->GetDerivations(n1)->Size() == 1) // after remove, only n2 should be derived from n1 && (ds->GetDerivations(n2)->Size() == 2) // after remove, only n3 and n4 should be derived from n2 , "Checking removal of a node with two parents"); extra = nullptr; } /* Checking removal of a node with two derived nodes */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); int refCountbeforeDS = watcher->GetReferenceCount(); ds->Add(extra); mitk::DataNode::Pointer d1 = mitk::DataNode::New(); d1->SetProperty("name", mitk::StringProperty::New("d1")); ds->Add(d1, extra); mitk::DataNode::Pointer d2 = mitk::DataNode::New(); d2->SetProperty("name", mitk::StringProperty::New("d2")); ds->Add(d2, extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1") == d1) && (ds->GetNamedNode("d2") == d2) && (ds->GetSources(d1)->Size() == 1) // extra should be source of d1 && (ds->GetSources(d2)->Size() == 1) // extra should be source of d2 && (ds->GetDerivations(extra)->Size() == 2) // d1 and d2 should be derived from extra , "add extra node"); ds->Remove(extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr) && (ds->GetNamedNode("d1") == d1) && (ds->GetNamedNode("d2") == d2) && (refCountbeforeDS == watcher->GetReferenceCount()) && (ds->GetSources(d1)->Size() == 0) // after remove, d1 should not have a source anymore && (ds->GetSources(d2)->Size() == 0) // after remove, d2 should not have a source anymore , "Checking removal of a node with two derived nodes"); extra = nullptr; } /* Checking removal of a node with two parents and two derived nodes */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); mitk::ReferenceCountWatcher::Pointer n1watcher = new mitk::ReferenceCountWatcher(n1); int refCountbeforeDS = watcher->GetReferenceCount(); mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New(); p->push_back(n1); p->push_back(n2); ds->Add(extra, p); // n1 and n2 are parents of extra mitk::DataNode::Pointer d1 = mitk::DataNode::New(); d1->SetProperty("name", mitk::StringProperty::New("d1x")); ds->Add(d1, extra); mitk::DataNode::Pointer d2 = mitk::DataNode::New(); d2->SetProperty("name", mitk::StringProperty::New("d2x")); ds->Add(d2, extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1x") == d1) && (ds->GetNamedNode("d2x") == d2) && (ds->GetSources(d1)->Size() == 1) // extra should be source of d1 && (ds->GetSources(d2)->Size() == 1) // extra should be source of d2 && (ds->GetDerivations(n1)->Size() == 2) // n2 and extra should be derived from n1 && (ds->GetDerivations(n2)->Size() == 3) // n3, n4 and extra should be derived from n2 && (ds->GetDerivations(extra)->Size() == 2) // d1 and d2 should be derived from extra , "add extra node"); ds->Remove(extra); MITK_TEST_CONDITION( (ds->GetNamedNode("extra") == nullptr) && (ds->GetNamedNode("d1x") == d1) && (ds->GetNamedNode("d2x") == d2) && (refCountbeforeDS == watcher->GetReferenceCount()) && (ds->GetDerivations(n1)->Size() == 1) // after remove, only n2 should be derived from n1 && (ds->GetDerivations(n2)->Size() == 2) // after remove, only n3 and n4 should be derived from n2 && (ds->GetSources(d1)->Size() == 0) // after remove, d1 should not have a source anymore && (ds->GetSources(d2)->Size() == 0) // after remove, d2 should not have a source anymore , "Checking removal of a node with two parents and two derived nodes"); extra = nullptr; } } catch (...) { MITK_TEST_FAILED_MSG(<< "Exeption during object removal methods"); } /* Checking for node is it's own parent exception */ { MITK_TEST_FOR_EXCEPTION_BEGIN(...); mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New(); p->push_back(n1); p->push_back(extra); // extra is parent of extra!!! ds->Add(extra, p); MITK_TEST_FOR_EXCEPTION_END(...); } /* Checking reference count of node after add and remove */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); extra->SetProperty("name", mitk::StringProperty::New("extra")); mitk::DataStorage::SetOfObjects::Pointer p = mitk::DataStorage::SetOfObjects::New(); p->push_back(n1); p->push_back(n3); ds->Add(extra, p); extra = nullptr; ds->Remove(ds->GetNamedNode("extra")); MITK_TEST_CONDITION(watcher->GetReferenceCount() == 0, "Checking reference count of node after add and remove"); } /* Checking removal of a node with two derived nodes [ dataStorage->GetDerivations( rootNode )] see bug #3426 */ { mitk::DataNode::Pointer extra = mitk::DataNode::New(); extra->SetProperty("name", mitk::StringProperty::New("extra")); ds->Add(extra); mitk::DataNode::Pointer d1y = mitk::DataNode::New(); d1y->SetProperty("name", mitk::StringProperty::New("d1y")); mitk::ReferenceCountWatcher::Pointer watcherD1y = new mitk::ReferenceCountWatcher(d1y); int refCountbeforeDS = watcherD1y->GetReferenceCount(); ds->Add(d1y, extra); mitk::DataNode::Pointer d2y = mitk::DataNode::New(); d2y->SetProperty("name", mitk::StringProperty::New("d2y")); ds->Add(d2y, extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1y") == d1y) && (ds->GetNamedNode("d2y") == d2y) && (ds->GetSources(d1y)->Size() == 1) // extra should be source of d1y && (ds->GetSources(d2y)->Size() == 1) // extra should be source of d2y && (ds->GetDerivations(extra)->Size() == 2) // d1y and d2y should be derived from extra , "add extra node"); ds->Remove(ds->GetDerivations(extra)); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == extra) && (ds->GetNamedNode("d1y") == nullptr) // d1y should be nullptr now && (ds->GetNamedNode("d2y") == nullptr) // d2y should be nullptr now && (refCountbeforeDS == watcherD1y->GetReferenceCount()), "Checking removal of subset of two derived nodes from one parent node"); ds->Remove(extra); MITK_TEST_CONDITION((ds->GetNamedNode("extra") == nullptr), "Checking removal of a parent node"); extra = nullptr; } /* Checking GetGrouptags() */ { const std::set groupTags = ds->GetGroupTags(); MITK_TEST_CONDITION((groupTags.size() == 2) && (std::find(groupTags.begin(), groupTags.end(), "Resection Proposal 1") != groupTags.end()) && (std::find(groupTags.begin(), groupTags.end(), "Resection Proposal 2") != groupTags.end()), "Checking GetGrouptags()"); } /* Checking Event handling */ DSEventReceiver listener; try { ds->AddNodeEvent += mitk::MessageDelegate1(&listener, &DSEventReceiver::OnAdd); ds->RemoveNodeEvent += mitk::MessageDelegate1(&listener, &DSEventReceiver::OnRemove); mitk::DataNode::Pointer extra = mitk::DataNode::New(); mitk::ReferenceCountWatcher::Pointer watcher = new mitk::ReferenceCountWatcher(extra); ds->Add(extra); MITK_TEST_CONDITION(listener.m_NodeAdded == extra.GetPointer(), "Checking AddEvent"); ds->Remove(extra); MITK_TEST_CONDITION(listener.m_NodeRemoved == extra.GetPointer(), "Checking RemoveEvent"); /* RemoveListener */ ds->AddNodeEvent -= mitk::MessageDelegate1(&listener, &DSEventReceiver::OnAdd); ds->RemoveNodeEvent -= mitk::MessageDelegate1(&listener, &DSEventReceiver::OnRemove); listener.m_NodeAdded = nullptr; listener.m_NodeRemoved = nullptr; ds->Add(extra); ds->Remove(extra); MITK_TEST_CONDITION((listener.m_NodeRemoved == nullptr) && (listener.m_NodeAdded == nullptr), "Checking RemoveListener"); std::cout << "Pointer handling after event handling: " << std::flush; extra = nullptr; // delete reference to the node. its memory should be freed now MITK_TEST_CONDITION(watcher->GetReferenceCount() == 0, "Pointer handling after event handling"); } catch (...) { /* cleanup */ ds->AddNodeEvent -= mitk::MessageDelegate1(&listener, &DSEventReceiver::OnAdd); ds->RemoveNodeEvent -= mitk::MessageDelegate1(&listener, &DSEventReceiver::OnRemove); MITK_TEST_FAILED_MSG(<< "Exception during object removal methods"); } // Checking ComputeBoundingGeometry3D method*/ const mitk::DataStorage::SetOfObjects::ConstPointer all = ds->GetAll(); mitk::TimeGeometry::Pointer geometry = ds->ComputeBoundingGeometry3D(); MITK_TEST_CONDITION(geometry->CountTimeSteps() == 4, "Test for number or time steps with ComputeBoundingGeometry()"); mitk::TimeBounds timebounds = geometry->GetTimeBounds(); MITK_TEST_CONDITION((timebounds[0] == 0) && (timebounds[1] == 4), "Test for timebounds with ComputeBoundingGeometry()"); for (unsigned int i = 0; i < geometry->CountTimeSteps(); i++) { mitk::BaseGeometry::Pointer subGeometry = geometry->GetGeometryForTimeStep(i); mitk::TimeBounds bounds = geometry->GetTimeBounds(i); MITK_TEST_CONDITION((bounds[0] == i) && (bounds[1] == i + 1), "Test for timebounds of geometry at different time steps with ComputeBoundingGeometry()"); } geometry = ds->ComputeBoundingGeometry3D(all); MITK_TEST_CONDITION(geometry->CountTimeSteps() == 4, "Test for number or time steps with ComputeBoundingGeometry(allNodes)"); timebounds = geometry->GetTimeBounds(); MITK_TEST_CONDITION((timebounds[0] == 0) && (timebounds[1] == 4), "Test for timebounds with ComputeBoundingGeometry(allNodes)"); for (unsigned int i = 0; i < geometry->CountTimeSteps(); i++) { mitk::BaseGeometry::Pointer subGeometry = geometry->GetGeometryForTimeStep(i); mitk::TimeBounds bounds = geometry->GetTimeBounds(i); MITK_TEST_CONDITION((bounds[0] == i) && (bounds[1] == i + 1), "Test for timebounds of geometry at different time steps with ComputeBoundingGeometry()"); } // test for thread safety of DataStorage try { mitk::StandaloneDataStorage::Pointer standaloneDataStorage = mitk::StandaloneDataStorage::New(); ItkDeleteEventListener listener(standaloneDataStorage); { mitk::DataNode::Pointer emptyNode = mitk::DataNode::New(); mitk::DataNode *pEmptyNode = emptyNode; listener.SetNode(emptyNode); standaloneDataStorage->Add(emptyNode); - emptyNode = 0; // emptyNode is still alive because standaloneDataStorage + emptyNode = nullptr; // emptyNode is still alive because standaloneDataStorage // owns it standaloneDataStorage->Remove(pEmptyNode); // this should not freeze the whole thing } } catch (...) { MITK_TEST_FAILED_MSG(<< "Exception during testing DataStorage thread safe"); } /* Clear DataStorage */ ds->Remove(ds->GetAll()); MITK_TEST_CONDITION(ds->GetAll()->Size() == 0, "Checking Clear DataStorage"); } diff --git a/Modules/Core/test/mitkExtractSliceFilterTest.cpp b/Modules/Core/test/mitkExtractSliceFilterTest.cpp index a78db36483..9612ce6e71 100644 --- a/Modules/Core/test/mitkExtractSliceFilterTest.cpp +++ b/Modules/Core/test/mitkExtractSliceFilterTest.cpp @@ -1,1073 +1,1073 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // use this to create the test volume on the fly #define CREATE_VOLUME // use this to save the created volume //#define SAVE_VOLUME // use this to calculate the error from the sphere mathematical model to our pixel based one //#define CALC_TESTFAILURE_DEVIATION // use this to render an oblique slice through a specified image //#define SHOW_SLICE_IN_RENDER_WINDOW // use this to have infos printed in mbilog //#define EXTRACTOR_DEBUG /*these are the deviations calculated by the function CalcTestFailureDeviation (see for details)*/ #define Testfailure_Deviation_Mean_128 0.853842 #define Testfailure_Deviation_Volume_128 0.145184 #define Testfailure_Deviation_Diameter_128 1.5625 #define Testfailure_Deviation_Mean_256 0.397693 #define Testfailure_Deviation_Volume_256 0.0141357 #define Testfailure_Deviation_Diameter_256 0.78125 #define Testfailure_Deviation_Mean_512 0.205277 #define Testfailure_Deviation_Volume_512 0.01993 #define Testfailure_Deviation_Diameter_512 0.390625 class mitkExtractSliceFilterTestClass { public: static void TestSlice(mitk::PlaneGeometry *planeGeometry, std::string testname) { TestPlane = planeGeometry; TestName = testname; mitk::ScalarType centerCoordValue = TestvolumeSize / 2.0; mitk::ScalarType center[3] = {centerCoordValue, centerCoordValue, centerCoordValue}; mitk::Point3D centerIndex(center); double radius = TestvolumeSize / 4.0; if (TestPlane->Distance(centerIndex) >= radius) return; // outside sphere // feed ExtractSliceFilter mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(); slicer->SetInput(TestVolume); slicer->SetWorldGeometry(TestPlane); slicer->Update(); MITK_TEST_CONDITION_REQUIRED(slicer->GetOutput() != nullptr, "Extractor returned a slice"); mitk::Image::Pointer reslicedImage = slicer->GetOutput(); AccessFixedDimensionByItk(reslicedImage, TestSphereRadiusByItk, 2); AccessFixedDimensionByItk(reslicedImage, TestSphereAreaByItk, 2); /* double devArea, devDiameter; if(TestvolumeSize == 128.0){ devArea = Testfailure_Deviation_Volume_128; devDiameter = Testfailure_Deviation_Diameter_128; } else if(TestvolumeSize == 256.0){devArea = Testfailure_Deviation_Volume_256; devDiameter = Testfailure_Deviation_Diameter_256;} else if (TestvolumeSize == 512.0){devArea = Testfailure_Deviation_Volume_512; devDiameter = Testfailure_Deviation_Diameter_512;} else{devArea = Testfailure_Deviation_Volume_128; devDiameter = Testfailure_Deviation_Diameter_128;} */ std::string areatestName = TestName.append(" area"); std::string diametertestName = TestName.append(" testing diameter"); // TODO think about the deviation, 1% makes no sense at all MITK_TEST_CONDITION(std::abs(100 - testResults.percentageAreaCalcToPixel) < 1, areatestName); MITK_TEST_CONDITION(std::abs(100 - testResults.percentageRadiusToPixel) < 1, diametertestName); #ifdef EXTRACTOR_DEBUG MITK_INFO << TestName << " >>> " << "planeDistanceToSphereCenter: " << testResults.planeDistanceToSphereCenter; MITK_INFO << "area in pixels: " << testResults.areaInPixel << " <-> area in mm: " << testResults.areaCalculated << " = " << testResults.percentageAreaCalcToPixel << "%"; MITK_INFO << "calculated diameter: " << testResults.diameterCalculated << " <-> diameter in mm: " << testResults.diameterInMM << " <-> diameter in pixel: " << testResults.diameterInPixel << " = " << testResults.percentageRadiusToPixel << "%"; #endif } /* * get the radius of the slice of a sphere based on pixel distance from edge to edge of the circle. */ template static void TestSphereRadiusByItk(itk::Image *inputImage) { typedef itk::Image InputImageType; // set the index to the middle of the image's edge at x and y axis typename InputImageType::IndexType currentIndexX; currentIndexX[0] = (int)(TestvolumeSize / 2.0); currentIndexX[1] = 0; typename InputImageType::IndexType currentIndexY; currentIndexY[0] = 0; currentIndexY[1] = (int)(TestvolumeSize / 2.0); // remember the last pixel value double lastValueX = inputImage->GetPixel(currentIndexX); double lastValueY = inputImage->GetPixel(currentIndexY); // storage for the index marks std::vector indicesX; std::vector indicesY; /*Get four indices on the edge of the circle*/ while (currentIndexX[1] < TestvolumeSize && currentIndexX[0] < TestvolumeSize) { // move x direction currentIndexX[1] += 1; // move y direction currentIndexY[0] += 1; if (inputImage->GetPixel(currentIndexX) > lastValueX) { // mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexX[0]; markIndex[1] = currentIndexX[1]; indicesX.push_back(markIndex); } else if (inputImage->GetPixel(currentIndexX) < lastValueX) { // mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexX[0]; markIndex[1] = currentIndexX[1] - 1; // value inside the sphere indicesX.push_back(markIndex); } if (inputImage->GetPixel(currentIndexY) > lastValueY) { // mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexY[0]; markIndex[1] = currentIndexY[1]; indicesY.push_back(markIndex); } else if (inputImage->GetPixel(currentIndexY) < lastValueY) { // mark the current index typename InputImageType::IndexType markIndex; markIndex[0] = currentIndexY[0]; markIndex[1] = currentIndexY[1] - 1; // value inside the sphere indicesY.push_back(markIndex); } // found both marks? if (indicesX.size() == 2 && indicesY.size() == 2) break; // the new 'last' values lastValueX = inputImage->GetPixel(currentIndexX); lastValueY = inputImage->GetPixel(currentIndexY); } /* *If we are here we found the four marks on the edge of the circle. *For the case our plane is rotated and shifted, we have to calculate the center of the circle, *else the center is the intersection of both straight lines between the marks. *When we have the center, the diameter of the circle will be checked to the reference value(math!). */ // each distance from the first mark of each direction to the center of the straight line between the marks double distanceToCenterX = std::abs(indicesX[0][1] - indicesX[1][1]) / 2.0; // double distanceToCenterY = std::abs(indicesY[0][0] - indicesY[1][0]) / 2.0; // the center of the straight lines typename InputImageType::IndexType centerX; // typename InputImageType::IndexType centerY; centerX[0] = indicesX[0][0]; centerX[1] = indicesX[0][1] + distanceToCenterX; // TODO think about implicit cast to int. this is not the real center of the image, which could be between two // pixels // centerY[0] = indicesY[0][0] + distanceToCenterY; // centerY[1] = inidcesY[0][1]; typename InputImageType::IndexType currentIndex(centerX); lastValueX = inputImage->GetPixel(currentIndex); long sumpixels = 0; std::vector diameterIndices; // move up while (currentIndex[1] < TestvolumeSize) { currentIndex[1] += 1; if (inputImage->GetPixel(currentIndex) != lastValueX) { typename InputImageType::IndexType markIndex; markIndex[0] = currentIndex[0]; markIndex[1] = currentIndex[1] - 1; diameterIndices.push_back(markIndex); break; } sumpixels++; lastValueX = inputImage->GetPixel(currentIndex); } currentIndex[1] -= sumpixels; // move back to center to go in the other direction lastValueX = inputImage->GetPixel(currentIndex); // move down while (currentIndex[1] >= 0) { currentIndex[1] -= 1; if (inputImage->GetPixel(currentIndex) != lastValueX) { typename InputImageType::IndexType markIndex; markIndex[0] = currentIndex[0]; markIndex[1] = currentIndex[1]; // outside sphere because we want to calculate the distance from edge to edge diameterIndices.push_back(markIndex); break; } sumpixels++; lastValueX = inputImage->GetPixel(currentIndex); } /* *Now sumpixels should be the apromximate diameter of the circle. This is checked with the calculated diameter from *the plane transformation(math). */ mitk::Point3D volumeCenter; volumeCenter[0] = volumeCenter[1] = volumeCenter[2] = TestvolumeSize / 2.0; double planeDistanceToSphereCenter = TestPlane->Distance(volumeCenter); double sphereRadius = TestvolumeSize / 4.0; // calculate the radius of the circle cut from the sphere by the plane double diameter = 2.0 * std::sqrt(std::pow(sphereRadius, 2) - std::pow(planeDistanceToSphereCenter, 2)); double percentageRadiusToPixel = 100 / diameter * sumpixels; /* *calculate the radius in mm by the both marks of the center line by using the world coordinates */ // get the points as 3D coordinates mitk::Vector3D diameterPointRight, diameterPointLeft; diameterPointRight[2] = diameterPointLeft[2] = 0.0; diameterPointLeft[0] = diameterIndices[0][0]; diameterPointLeft[1] = diameterIndices[0][1]; diameterPointRight[0] = diameterIndices[1][0]; diameterPointRight[1] = diameterIndices[1][1]; // transform to worldcoordinates TestVolume->GetGeometry()->IndexToWorld(diameterPointLeft, diameterPointLeft); TestVolume->GetGeometry()->IndexToWorld(diameterPointRight, diameterPointRight); // euklidian distance double diameterInMM = ((diameterPointLeft * -1.0) + diameterPointRight).GetNorm(); testResults.diameterInMM = diameterInMM; testResults.diameterCalculated = diameter; testResults.diameterInPixel = sumpixels; testResults.percentageRadiusToPixel = percentageRadiusToPixel; testResults.planeDistanceToSphereCenter = planeDistanceToSphereCenter; } /*brute force the area pixel by pixel*/ template static void TestSphereAreaByItk(itk::Image *inputImage) { typedef itk::Image InputImageType; typedef itk::ImageRegionConstIterator ImageIterator; ImageIterator imageIterator(inputImage, inputImage->GetLargestPossibleRegion()); imageIterator.GoToBegin(); int sumPixelsInArea = 0; while (!imageIterator.IsAtEnd()) { if (inputImage->GetPixel(imageIterator.GetIndex()) == pixelValueSet) sumPixelsInArea++; ++imageIterator; } mitk::Point3D volumeCenter; volumeCenter[0] = volumeCenter[1] = volumeCenter[2] = TestvolumeSize / 2.0; double planeDistanceToSphereCenter = TestPlane->Distance(volumeCenter); double sphereRadius = TestvolumeSize / 4.0; // calculate the radius of the circle cut from the sphere by the plane double radius = std::sqrt(std::pow(sphereRadius, 2) - std::pow(planeDistanceToSphereCenter, 2)); double areaInMM = 3.14159265358979 * std::pow(radius, 2); testResults.areaCalculated = areaInMM; testResults.areaInPixel = sumPixelsInArea; testResults.percentageAreaCalcToPixel = 100 / areaInMM * sumPixelsInArea; } /* * random a voxel. define plane through this voxel. reslice at the plane. compare the pixel vaues of the voxel * in the volume with the pixel value in the resliced image. * there are some indice shifting problems which causes the test to fail for oblique planes. seems like the chosen * worldcoordinate is not corrresponding to the index in the 2D image. and so the pixel values are not the same as * expected. */ static void PixelvalueBasedTest() { /* setup itk image */ typedef itk::Image ImageType; typedef itk::ImageRegionConstIterator ImageIterator; ImageType::Pointer image = ImageType::New(); ImageType::IndexType start; start[0] = start[1] = start[2] = 0; ImageType::SizeType size; size[0] = size[1] = size[2] = 32; ImageType::RegionType imgRegion; imgRegion.SetSize(size); imgRegion.SetIndex(start); image->SetRegions(imgRegion); image->SetSpacing(1.0); image->Allocate(); ImageIterator imageIterator(image, image->GetLargestPossibleRegion()); imageIterator.GoToBegin(); unsigned short pixelValue = 0; // fill the image with distinct values while (!imageIterator.IsAtEnd()) { image->SetPixel(imageIterator.GetIndex(), pixelValue); ++imageIterator; ++pixelValue; } /* end setup itk image */ mitk::Image::Pointer imageInMitk; CastToMitkImage(image, imageInMitk); /*mitk::ImageWriter::Pointer writer = mitk::ImageWriter::New(); writer->SetInput(imageInMitk); std::string file = "C:\\Users\\schroedt\\Desktop\\cube.nrrd"; writer->SetFileName(file); writer->Update();*/ PixelvalueBasedTestByPlane(imageInMitk, mitk::PlaneGeometry::Frontal); PixelvalueBasedTestByPlane(imageInMitk, mitk::PlaneGeometry::Sagittal); PixelvalueBasedTestByPlane(imageInMitk, mitk::PlaneGeometry::Axial); } static void PixelvalueBasedTestByPlane(mitk::Image *imageInMitk, mitk::PlaneGeometry::PlaneOrientation orientation) { typedef itk::Image ImageType; // set the seed of the rand function - srand((unsigned)time(0)); + srand((unsigned)time(nullptr)); /* setup a random orthogonal plane */ int sliceindex = 17; // rand() % 32; bool isFrontside = true; bool isRotated = false; if (orientation == mitk::PlaneGeometry::Axial) { /*isFrontside = false; isRotated = true;*/ } mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(imageInMitk->GetGeometry(), orientation, sliceindex, isFrontside, isRotated); mitk::Point3D origin = plane->GetOrigin(); mitk::Vector3D normal; normal = plane->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 plane->SetOrigin(origin); // we dont need this any more, because we are only testing orthogonal planes /*mitk::Vector3D rotationVector; rotationVector[0] = randFloat(); rotationVector[1] = randFloat(); rotationVector[2] = randFloat(); float degree = randFloat() * 180.0; mitk::RotationOperation* op = new mitk::RotationOperation(mitk::OpROTATE, plane->GetCenter(), rotationVector, degree); plane->ExecuteOperation(op); delete op;*/ /* end setup plane */ /* define a point in the 3D volume. * add the two axis vectors of the plane (each multiplied with a * random number) to the origin. now the two random numbers * become our index coordinates in the 2D image, because the * length of the axis vectors is 1. */ mitk::Point3D planeOrigin = plane->GetOrigin(); mitk::Vector3D axis0, axis1; axis0 = plane->GetAxisVector(0); axis1 = plane->GetAxisVector(1); axis0.Normalize(); axis1.Normalize(); unsigned char n1 = 7; // rand() % 32; unsigned char n2 = 13; // rand() % 32; mitk::Point3D testPoint3DInWorld; testPoint3DInWorld = planeOrigin + (axis0 * n1) + (axis1 * n2); // get the index of the point in the 3D volume ImageType::IndexType testPoint3DInIndex; imageInMitk->GetGeometry()->WorldToIndex(testPoint3DInWorld, testPoint3DInIndex); itk::Index<3> testPoint2DInIndex; /* end define a point in the 3D volume.*/ // do reslicing at the plane mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(); slicer->SetInput(imageInMitk); slicer->SetWorldGeometry(plane); slicer->Update(); mitk::Image::Pointer slice = slicer->GetOutput(); // Get TestPoiont3D as Index in Slice slice->GetGeometry()->WorldToIndex(testPoint3DInWorld, testPoint2DInIndex); mitk::Point3D p, sliceIndexToWorld, imageIndexToWorld; p[0] = testPoint2DInIndex[0]; p[1] = testPoint2DInIndex[1]; p[2] = testPoint2DInIndex[2]; slice->GetGeometry()->IndexToWorld(p, sliceIndexToWorld); p[0] = testPoint3DInIndex[0]; p[1] = testPoint3DInIndex[1]; p[2] = testPoint3DInIndex[2]; imageInMitk->GetGeometry()->IndexToWorld(p, imageIndexToWorld); itk::Index<2> testPoint2DIn2DIndex; testPoint2DIn2DIndex[0] = testPoint2DInIndex[0]; testPoint2DIn2DIndex[1] = testPoint2DInIndex[1]; typedef mitk::ImagePixelReadAccessor VolumeReadAccessorType; typedef mitk::ImagePixelReadAccessor SliceReadAccessorType; VolumeReadAccessorType VolumeReadAccessor(imageInMitk); SliceReadAccessorType SliceReadAccessor(slice); // compare the pixelvalues of the defined point in the 3D volume with the value of the resliced image unsigned short valueAt3DVolume = VolumeReadAccessor.GetPixelByIndex(testPoint3DInIndex); unsigned short valueAtSlice = SliceReadAccessor.GetPixelByIndex(testPoint2DIn2DIndex); // valueAt3DVolume == valueAtSlice is not always working. because of rounding errors // indices are shifted MITK_TEST_CONDITION(valueAt3DVolume == valueAtSlice, "comparing pixelvalues for orthogonal plane"); vtkSmartPointer imageInVtk = imageInMitk->GetVtkImageData(); vtkSmartPointer sliceInVtk = slice->GetVtkImageData(); double PixelvalueByMitkOutput = sliceInVtk->GetScalarComponentAsDouble(n1, n2, 0, 0); // double valueVTKinImage = imageInVtk->GetScalarComponentAsDouble(testPoint3DInIndex[0], testPoint3DInIndex[1], // testPoint3DInIndex[2], 0); /* Test that everything is working equally if vtkoutput is used instead of the default output * from mitk ImageToImageFilter */ mitk::ExtractSliceFilter::Pointer slicerWithVtkOutput = mitk::ExtractSliceFilter::New(); slicerWithVtkOutput->SetInput(imageInMitk); slicerWithVtkOutput->SetWorldGeometry(plane); slicerWithVtkOutput->SetVtkOutputRequest(true); slicerWithVtkOutput->Update(); vtkSmartPointer vtkImageByVtkOutput = slicerWithVtkOutput->GetVtkOutput(); double PixelvalueByVtkOutput = vtkImageByVtkOutput->GetScalarComponentAsDouble(n1, n2, 0, 0); MITK_TEST_CONDITION(PixelvalueByMitkOutput == PixelvalueByVtkOutput, "testing convertion of image output vtk->mitk by reslicer"); /*================ mbilog outputs ===========================*/ #ifdef EXTRACTOR_DEBUG MITK_INFO << "\n" << "TESTINFO index: " << sliceindex << " orientation: " << orientation << " frontside: " << isFrontside << " rotated: " << isRotated; MITK_INFO << "\n" << "slice index to world: " << sliceIndexToWorld; MITK_INFO << "\n" << "image index to world: " << imageIndexToWorld; MITK_INFO << "\n" << "vtk: slice: " << PixelvalueByMitkOutput << ", image: " << valueVTKinImage; MITK_INFO << "\n" << "testPoint3D InWorld" << testPoint3DInWorld << " is " << testPoint2DInIndex << " in 2D"; MITK_INFO << "\n" << "randoms: " << ((int)n1) << ", " << ((int)n2); MITK_INFO << "\n" << "point is inside plane: " << plane->IsInside(testPoint3DInWorld) << " and volume: " << imageInMitk->GetGeometry()->IsInside(testPoint3DInWorld); MITK_INFO << "\n" << "volume idx: " << testPoint3DInIndex << " = " << valueAt3DVolume; MITK_INFO << "\n" << "volume world: " << testPoint3DInWorld << " = " << valueAt3DVolumeByWorld; MITK_INFO << "\n" << "slice idx: " << testPoint2DInIndex << " = " << valueAtSlice; itk::Index<3> curr; curr[0] = curr[1] = curr[2] = 0; for (int i = 0; i < 32; ++i) { for (int j = 0; j < 32; ++j) { ++curr[1]; if (SliceReadAccessor.GetPixelByIndex(curr) == valueAt3DVolume) { MITK_INFO << "\n" << valueAt3DVolume << " MATCHED mitk " << curr; } } curr[1] = 0; ++curr[0]; } typedef itk::Image Image2DType; Image2DType::Pointer img = Image2DType::New(); CastToItkImage(slice, img); typedef itk::ImageRegionConstIterator Iterator2D; Iterator2D iter(img, img->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { if (img->GetPixel(iter.GetIndex()) == valueAt3DVolume) MITK_INFO << "\n" << valueAt3DVolume << " MATCHED itk " << iter.GetIndex(); ++iter; } #endif // EXTRACTOR_DEBUG } /* random a float value */ static float randFloat() { return (((float)rand() + 1.0) / ((float)RAND_MAX + 1.0)) + (((float)rand() + 1.0) / ((float)RAND_MAX + 1.0)) / ((float)RAND_MAX + 1.0); } /* create a sphere with the size of the given testVolumeSize*/ static void InitializeTestVolume() { #ifdef CREATE_VOLUME // do sphere creation ItkVolumeGeneration(); #ifdef SAVE_VOLUME // save in file mitk::ImageWriter::Pointer writer = mitk::ImageWriter::New(); writer->SetInput(TestVolume); std::string file; std::ostringstream filename; filename << "C:\\home\\schroedt\\MITK\\Modules\\ImageExtraction\\Testing\\Data\\sphere_"; filename << TestvolumeSize; filename << ".nrrd"; file = filename.str(); writer->SetFileName(file); writer->Update(); #endif // SAVE_VOLUME #endif #ifndef CREATE_VOLUME // read from file mitk::StandardFileLocations::Pointer locator = mitk::StandardFileLocations::GetInstance(); std::string filename = locator->FindFile("sphere_512.nrrd.mhd", "Modules/ImageExtraction/Testing/Data"); TestVolume = dynamic_cast(mitk::IOUtil::Load(filename)[0].GetPointer()); #endif #ifdef CALC_TESTFAILURE_DEVIATION // get the TestFailureDeviation in % AccessFixedDimensionByItk(TestVolume, CalcTestFailureDeviation, 3); #endif } // the test result of the sphere reslice struct SliceProperties { double planeDistanceToSphereCenter; double diameterInMM; double diameterInPixel; double diameterCalculated; double percentageRadiusToPixel; double areaCalculated; double areaInPixel; double percentageAreaCalcToPixel; }; static mitk::Image::Pointer TestVolume; static double TestvolumeSize; static mitk::PlaneGeometry::Pointer TestPlane; static std::string TestName; static unsigned char pixelValueSet; static SliceProperties testResults; static double TestFailureDeviation; private: /* * Generate a sphere with a radius of TestvolumeSize / 4.0 */ static void ItkVolumeGeneration() { typedef itk::Image TestVolumeType; typedef itk::ImageRegionConstIterator ImageIterator; TestVolumeType::Pointer sphereImage = TestVolumeType::New(); TestVolumeType::IndexType start; start[0] = start[1] = start[2] = 0; TestVolumeType::SizeType size; size[0] = size[1] = size[2] = TestvolumeSize; TestVolumeType::RegionType imgRegion; imgRegion.SetSize(size); imgRegion.SetIndex(start); sphereImage->SetRegions(imgRegion); sphereImage->SetSpacing(1.0); sphereImage->Allocate(); sphereImage->FillBuffer(0); mitk::Vector3D center; center[0] = center[1] = center[2] = TestvolumeSize / 2.0; double radius = TestvolumeSize / 4.0; double pixelValue = pixelValueSet; ImageIterator imageIterator(sphereImage, sphereImage->GetLargestPossibleRegion()); imageIterator.GoToBegin(); mitk::Vector3D currentVoxelInIndex; while (!imageIterator.IsAtEnd()) { currentVoxelInIndex[0] = imageIterator.GetIndex()[0]; currentVoxelInIndex[1] = imageIterator.GetIndex()[1]; currentVoxelInIndex[2] = imageIterator.GetIndex()[2]; double distanceToCenter = (center + (currentVoxelInIndex * -1.0)).GetNorm(); // if distance to center is smaller then the radius of the sphere if (distanceToCenter < radius) { sphereImage->SetPixel(imageIterator.GetIndex(), pixelValue); } ++imageIterator; } CastToMitkImage(sphereImage, TestVolume); } /* calculate the devation of the voxel object to the mathematical sphere object. * this is use to make a statement about the accuracy of the resliced image, eg. the circle's diameter or area. */ template static void CalcTestFailureDeviation(itk::Image *inputImage) { typedef itk::Image InputImageType; typedef itk::ImageRegionConstIterator ImageIterator; ImageIterator iterator(inputImage, inputImage->GetLargestPossibleRegion()); iterator.GoToBegin(); int volumeInPixel = 0; while (!iterator.IsAtEnd()) { if (inputImage->GetPixel(iterator.GetIndex()) == pixelValueSet) volumeInPixel++; ++iterator; } double diameter = TestvolumeSize / 2.0; double volumeCalculated = (1.0 / 6.0) * 3.14159265358979 * std::pow(diameter, 3); double volumeDeviation = std::abs(100 - (100 / volumeCalculated * volumeInPixel)); typename InputImageType::IndexType index; index[0] = index[1] = TestvolumeSize / 2.0; index[2] = 0; int sumpixels = 0; while (index[2] < TestvolumeSize) { if (inputImage->GetPixel(index) == pixelValueSet) sumpixels++; index[2] += 1; } double diameterDeviation = std::abs(100 - (100 / diameter * sumpixels)); #ifdef DEBUG MITK_INFO << "volume deviation: " << volumeDeviation << " diameter deviation:" << diameterDeviation; #endif mitkExtractSliceFilterTestClass::TestFailureDeviation = (volumeDeviation + diameterDeviation) / 2.0; } }; /*================ #END class ================*/ /*================#BEGIN Instanciation of members ================*/ mitk::Image::Pointer mitkExtractSliceFilterTestClass::TestVolume = mitk::Image::New(); double mitkExtractSliceFilterTestClass::TestvolumeSize = 256.0; mitk::PlaneGeometry::Pointer mitkExtractSliceFilterTestClass::TestPlane = mitk::PlaneGeometry::New(); std::string mitkExtractSliceFilterTestClass::TestName = ""; unsigned char mitkExtractSliceFilterTestClass::pixelValueSet = 255; mitkExtractSliceFilterTestClass::SliceProperties mitkExtractSliceFilterTestClass::testResults = { -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0}; double mitkExtractSliceFilterTestClass::TestFailureDeviation = 0.0; /*================ #END Instanciation of members ================*/ /*================ #BEGIN test main ================*/ int mitkExtractSliceFilterTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("mitkExtractSliceFilterTest") // pixelvalue based testing mitkExtractSliceFilterTestClass::PixelvalueBasedTest(); // initialize sphere test volume mitkExtractSliceFilterTestClass::InitializeTestVolume(); mitk::Vector3D spacing = mitkExtractSliceFilterTestClass::TestVolume->GetGeometry()->GetSpacing(); // the center of the sphere = center of image double sphereCenter = mitkExtractSliceFilterTestClass::TestvolumeSize / 2.0; double planeSize = mitkExtractSliceFilterTestClass::TestvolumeSize; /* axial plane */ mitk::PlaneGeometry::Pointer geometryAxial = mitk::PlaneGeometry::New(); geometryAxial->InitializeStandardPlane( planeSize, planeSize, spacing, mitk::PlaneGeometry::Axial, sphereCenter, false, true); geometryAxial->ChangeImageGeometryConsideringOriginOffset(true); mitk::Point3D origin = geometryAxial->GetOrigin(); mitk::Vector3D normal; normal = geometryAxial->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 // geometryAxial->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometryAxial, "Testing axial plane"); /* end axial plane */ /* sagittal plane */ mitk::PlaneGeometry::Pointer geometrySagital = mitk::PlaneGeometry::New(); geometrySagital->InitializeStandardPlane( planeSize, planeSize, spacing, mitk::PlaneGeometry::Sagittal, sphereCenter, true, false); geometrySagital->ChangeImageGeometryConsideringOriginOffset(true); origin = geometrySagital->GetOrigin(); normal = geometrySagital->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 // geometrySagital->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometrySagital, "Testing sagittal plane"); /* sagittal plane */ /* sagittal shifted plane */ mitk::PlaneGeometry::Pointer geometrySagitalShifted = mitk::PlaneGeometry::New(); geometrySagitalShifted->InitializeStandardPlane( planeSize, planeSize, spacing, mitk::PlaneGeometry::Sagittal, (sphereCenter - 14), true, false); geometrySagitalShifted->ChangeImageGeometryConsideringOriginOffset(true); origin = geometrySagitalShifted->GetOrigin(); normal = geometrySagitalShifted->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 // geometrySagitalShifted->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometrySagitalShifted, "Testing sagittal plane shifted"); /* end sagittal shifted plane */ /* coronal plane */ mitk::PlaneGeometry::Pointer geometryCoronal = mitk::PlaneGeometry::New(); geometryCoronal->InitializeStandardPlane( planeSize, planeSize, spacing, mitk::PlaneGeometry::Frontal, sphereCenter, true, false); geometryCoronal->ChangeImageGeometryConsideringOriginOffset(true); origin = geometryCoronal->GetOrigin(); normal = geometryCoronal->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 // geometryCoronal->SetOrigin(origin); mitkExtractSliceFilterTestClass::TestSlice(geometryCoronal, "Testing coronal plane"); /* end coronal plane */ /* oblique plane */ mitk::PlaneGeometry::Pointer obliquePlane = mitk::PlaneGeometry::New(); obliquePlane->InitializeStandardPlane( planeSize, planeSize, spacing, mitk::PlaneGeometry::Sagittal, sphereCenter, true, false); obliquePlane->ChangeImageGeometryConsideringOriginOffset(true); origin = obliquePlane->GetOrigin(); normal = obliquePlane->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 // obliquePlane->SetOrigin(origin); mitk::Vector3D rotationVector; rotationVector[0] = 0.2; rotationVector[1] = 0.4; rotationVector[2] = 0.62; float degree = 37.0; mitk::RotationOperation *op = new mitk::RotationOperation(mitk::OpROTATE, obliquePlane->GetCenter(), rotationVector, degree); obliquePlane->ExecuteOperation(op); delete op; mitkExtractSliceFilterTestClass::TestSlice(obliquePlane, "Testing oblique plane"); /* end oblique plane */ #ifdef SHOW_SLICE_IN_RENDER_WINDOW /*================ #BEGIN vtk render code ================*/ // set reslicer for renderwindow mitk::Image::Pointer pic = dynamic_cast(mitk::IOUtil::Load(filename)[0].GetPointer()); vtkSmartPointer slicer = vtkSmartPointer::New(); slicer->SetInput(pic->GetVtkImageData()); mitk::PlaneGeometry::Pointer obliquePl = mitk::PlaneGeometry::New(); obliquePl->InitializeStandardPlane( pic->GetGeometry(), mitk::PlaneGeometry::Sagittal, pic->GetGeometry()->GetCenter()[0], true, false); obliquePl->ChangeImageGeometryConsideringOriginOffset(true); mitk::Point3D origin2 = obliquePl->GetOrigin(); mitk::Vector3D n; n = obliquePl->GetNormal(); n.Normalize(); origin2 += n * 0.5; // pixelspacing is 1, so half the spacing is 0.5 obliquePl->SetOrigin(origin2); mitk::Vector3D rotation; rotation[0] = 0.534307; rotation[1] = 0.000439605; rotation[2] = 0.423017; MITK_INFO << rotation; mitk::RotationOperation *operation = new mitk::RotationOperation(mitk::OpROTATE, obliquePl->GetCenter(), rotationVector, degree); obliquePl->ExecuteOperation(operation); delete operation; double origin[3]; origin[0] = obliquePl->GetOrigin()[0]; origin[1] = obliquePl->GetOrigin()[1]; origin[2] = obliquePl->GetOrigin()[2]; slicer->SetResliceAxesOrigin(origin); mitk::Vector3D right, bottom, normal; right = obliquePl->GetAxisVector(0); bottom = obliquePl->GetAxisVector(1); normal = obliquePl->GetNormal(); right.Normalize(); bottom.Normalize(); normal.Normalize(); double cosines[9]; mitk::vnl2vtk(right.GetVnlVector(), cosines); // x mitk::vnl2vtk(bottom.GetVnlVector(), cosines + 3); // y mitk::vnl2vtk(normal.GetVnlVector(), cosines + 6); // n slicer->SetResliceAxesDirectionCosines(cosines); slicer->SetOutputDimensionality(2); slicer->Update(); // set vtk renderwindow vtkSmartPointer vtkPlane = vtkSmartPointer::New(); vtkPlane->SetOrigin(0.0, 0.0, 0.0); // These two points define the axes of the plane in combination with the origin. // Point 1 is the x-axis and point 2 the y-axis. // Each plane is transformed according to the view (axial, coronal and saggital) afterwards. vtkPlane->SetPoint1(1.0, 0.0, 0.0); // P1: (xMax, yMin, depth) vtkPlane->SetPoint2(0.0, 1.0, 0.0); // P2: (xMin, yMax, depth) // these are not the correct values for all slices, only a square plane by now vtkSmartPointer imageMapper = vtkSmartPointer::New(); imageMapper->SetInputConnection(vtkPlane->GetOutputPort()); vtkSmartPointer lookupTable = vtkSmartPointer::New(); // built a default lookuptable lookupTable->SetRampToLinear(); lookupTable->SetSaturationRange(0.0, 0.0); lookupTable->SetHueRange(0.0, 0.0); lookupTable->SetValueRange(0.0, 1.0); lookupTable->Build(); // map all black values to transparent lookupTable->SetTableValue(0, 0.0, 0.0, 0.0, 0.0); lookupTable->SetRange(-255.0, 255.0); // lookupTable->SetRange(-1022.0, 1184.0);//pic3D range vtkSmartPointer texture = vtkSmartPointer::New(); texture->SetInput(slicer->GetOutput()); texture->SetLookupTable(lookupTable); texture->SetMapColorScalarsThroughLookupTable(true); vtkSmartPointer imageActor = vtkSmartPointer::New(); imageActor->SetMapper(imageMapper); imageActor->SetTexture(texture); // Setup renderers vtkSmartPointer renderer = vtkSmartPointer::New(); renderer->AddActor(imageActor); // Setup render window vtkSmartPointer renderWindow = vtkSmartPointer::New(); renderWindow->AddRenderer(renderer); // Setup render window interactor vtkSmartPointer renderWindowInteractor = vtkSmartPointer::New(); vtkSmartPointer style = vtkSmartPointer::New(); renderWindowInteractor->SetInteractorStyle(style); // Render and start interaction renderWindowInteractor->SetRenderWindow(renderWindow); // renderer->AddViewProp(imageActor); renderWindow->Render(); renderWindowInteractor->Start(); // always end with this! /*================ #END vtk render code ================*/ #endif // SHOW_SLICE_IN_RENDER_WINDOW MITK_TEST_END() } diff --git a/Modules/Core/test/mitkImageToItkTest.cpp b/Modules/Core/test/mitkImageToItkTest.cpp index d122c7cfd1..1c061afd4a 100644 --- a/Modules/Core/test/mitkImageToItkTest.cpp +++ b/Modules/Core/test/mitkImageToItkTest.cpp @@ -1,163 +1,163 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "itkDiffusionTensor3D.h" #include "mitkITKImageImport.h" #include "mitkImage.h" #include "mitkReferenceCountWatcher.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include #include #include #include static mitk::Image::Pointer GetEmptyTestImageWithGeometry(mitk::PixelType pt) { // create empty image mitk::Image::Pointer imgMem; imgMem = mitk::Image::New(); // create geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); // initialize image imgMem->Initialize(pt, 40, *planegeometry); return imgMem; } class mitkImageToItkTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkImageToItkTestSuite); MITK_TEST(ImageCastIntToFloat_EmptyImage); MITK_TEST(ImageCastDoubleToFloat_EmptyImage); MITK_TEST(ImageCastFloatToFloatTensor_EmptyImage_ThrowsException); MITK_TEST(ImageCastFloatTensorToFloatTensor_EmptyImage); MITK_TEST(ImageCastDoubleToTensorDouble_EmptyImage_ThrowsException); MITK_TEST(ImageCastToItkAndBack_SamePointer_Success); MITK_TEST(ImageCastToItk_TestImage_Success); CPPUNIT_TEST_SUITE_END(); private: mitk::Image::Pointer m_TestImage; public: void setUp() override { // empty on purpose } void tearDown() override { m_TestImage = nullptr; } void ImageCastIntToFloat_EmptyImage() { mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakeScalarPixelType()); itk::Image::Pointer itkImage; mitk::CastToItkImage(m_TestImage, itkImage); mitk::Image::Pointer mitkImageAfterCast = mitk::ImportItkImage(itkImage); CPPUNIT_ASSERT_MESSAGE("Cast output is not null", mitkImageAfterCast.IsNotNull()); } void ImageCastDoubleToFloat_EmptyImage() { mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakeScalarPixelType()); itk::Image::Pointer diffImage; mitk::CastToItkImage(m_TestImage, diffImage); CPPUNIT_ASSERT_MESSAGE("Casting scalar double (MITK) image to scalar float tensor (ITK). Result shouldn't be nullptr.", diffImage.IsNotNull()); } void ImageCastFloatToFloatTensor_EmptyImage_ThrowsException() { mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakeScalarPixelType()); itk::Image, 3>::Pointer diffImage; CPPUNIT_ASSERT_THROW_MESSAGE("Casting scalar float (MITK) image to scalar float (ITK) throws exception.", mitk::CastToItkImage(m_TestImage, diffImage), mitk::AccessByItkException); } void ImageCastFloatTensorToFloatTensor_EmptyImage() { typedef itk::Image, 3> ItkTensorImageType; mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakePixelType()); itk::Image, 3>::Pointer diffImage; mitk::CastToItkImage(m_TestImage, diffImage); MITK_TEST_CONDITION_REQUIRED(diffImage.IsNotNull(), "Casting float tensor (MITK) image to float tensor (ITK). Result shouldn't be nullptr"); mitk::Image::Pointer mitkImageAfterCast = mitk::ImportItkImage(diffImage); MITK_ASSERT_EQUAL(mitkImageAfterCast, m_TestImage, "Same type, images shoul be equal."); } void ImageCastDoubleToTensorDouble_EmptyImage_ThrowsException() { mitk::Image::Pointer m_TestImage = GetEmptyTestImageWithGeometry(mitk::MakeScalarPixelType()); itk::Image, 3>::Pointer diffImage; CPPUNIT_ASSERT_THROW(mitk::CastToItkImage(m_TestImage, diffImage), mitk::AccessByItkException); } void ImageCastToItkAndBack_SamePointer_Success() { typedef itk::Image ItkImageType; ItkImageType::Pointer itkImage = ItkImageType::New(); std::string m_ImagePath = GetTestDataFilePath("Pic3D.nrrd"); mitk::Image::Pointer testDataImage = dynamic_cast(mitk::IOUtil::Load(m_ImagePath)[0].GetPointer()); // modify ITK image itk::Matrix dir = itkImage->GetDirection(); dir *= 2; itkImage->SetDirection(dir); CPPUNIT_ASSERT_THROW_MESSAGE("No exception thrown for casting back the same memory", testDataImage = mitk::GrabItkImageMemory(itkImage, testDataImage), itk::ExceptionObject); CPPUNIT_ASSERT(testDataImage.IsNotNull()); } void ImageCastToItk_TestImage_Success() { itk::Image::Pointer itkImage; std::string m_ImagePath = GetTestDataFilePath("Pic3D.nrrd"); mitk::Image::Pointer testDataImage = dynamic_cast(mitk::IOUtil::Load(m_ImagePath)[0].GetPointer()); mitk::CastToItkImage(testDataImage, itkImage); mitk::Image::Pointer mitkImageAfterCast = mitk::ImportItkImage(itkImage); // dereference itk image - itkImage = 0; + itkImage = nullptr; MITK_ASSERT_EQUAL(mitkImageAfterCast, testDataImage, "Cast with test data followed by import produces same images"); } }; // END TEST SUITE CLASS DECL MITK_TEST_SUITE_REGISTRATION(mitkImageToItk); diff --git a/Modules/Core/test/mitkPlanePositionManagerTest.cpp b/Modules/Core/test/mitkPlanePositionManagerTest.cpp index 5842cfaf06..5677151ee0 100644 --- a/Modules/Core/test/mitkPlanePositionManagerTest.cpp +++ b/Modules/Core/test/mitkPlanePositionManagerTest.cpp @@ -1,277 +1,277 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBaseProperty.h" #include "mitkDataNode.h" #include "mitkGeometry3D.h" #include "mitkImage.h" #include "mitkInteractionConst.h" #include "mitkPlaneGeometry.h" #include "mitkPlanePositionManager.h" #include "mitkRotationOperation.h" #include "mitkSliceNavigationController.h" #include "mitkStandaloneDataStorage.h" #include "mitkStringProperty.h" #include "mitkSurface.h" #include "mitkTestingMacros.h" #include "usGetModuleContext.h" #include "usModuleContext.h" #include "usServiceReference.h" #include "vnl/vnl_vector.h" #include std::vector m_Geometries; std::vector m_SliceIndices; mitk::PlanePositionManagerService *m_Service; int SetUpBeforeTest() { // Getting Service us::ServiceReference serviceRef = us::GetModuleContext()->GetServiceReference(); m_Service = us::GetModuleContext()->GetService(serviceRef); - if (m_Service == 0) + if (m_Service == nullptr) return EXIT_FAILURE; // Creating different Geometries m_Geometries.reserve(100); mitk::PlaneGeometry::PlaneOrientation views[] = { mitk::PlaneGeometry::Axial, mitk::PlaneGeometry::Sagittal, mitk::PlaneGeometry::Frontal}; for (unsigned int i = 0; i < 100; ++i) { mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); mitk::ScalarType width = 256 + (0.01 * i); mitk::ScalarType height = 256 + (0.002 * i); mitk::Vector3D right; mitk::Vector3D down; right[0] = 1; right[1] = i; right[2] = 0.5; down[0] = i * 0.02; down[1] = 1; down[2] = i * 0.03; mitk::Vector3D spacing; mitk::FillVector3D(spacing, 1.0 * 0.02 * i, 1.0 * 0.15 * i, 1.0); mitk::Vector3D rightVector; mitk::FillVector3D(rightVector, 0.02 * (i + 1), 0 + (0.05 * i), 1.0); mitk::Vector3D downVector; mitk::FillVector3D(downVector, 1, 3 - 0.01 * i, 0.0345 * i); vnl_vector normal = vnl_cross_3d(rightVector.GetVnlVector(), downVector.GetVnlVector()); normal.normalize(); normal *= 1.5; mitk::Vector3D origin; origin.Fill(1); origin[0] = 12 + 0.03 * i; mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); mitk::Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightVector.GetVnlVector()); matrix.GetVnlMatrix().set_column(1, downVector.GetVnlVector()); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(origin); plane->InitializeStandardPlane(width, height, transform, views[i % 3], i, true, false); m_Geometries.push_back(plane); } return EXIT_SUCCESS; } int testAddPlanePosition() { MITK_TEST_OUTPUT(<< "Starting Test: ######### A d d P l a n e P o s i t i o n #########"); MITK_TEST_CONDITION(m_Service != nullptr, "Testing getting of PlanePositionManagerService"); unsigned int currentID(m_Service->AddNewPlanePosition(m_Geometries.at(0), 0)); bool error = ((m_Service->GetNumberOfPlanePositions() != 1) || (currentID != 0)); if (error) { MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == 1, "Checking for correct number of planepositions"); MITK_TEST_CONDITION(currentID == 0, "Testing for correct ID"); return EXIT_FAILURE; } // Adding new planes for (unsigned int i = 1; i < m_Geometries.size(); ++i) { unsigned int newID = m_Service->AddNewPlanePosition(m_Geometries.at(i), i); error = ((m_Service->GetNumberOfPlanePositions() != i + 1) || (newID != (currentID + 1))); if (error) { MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == i + 1, "Checking for correct number of planepositions"); MITK_TEST_CONDITION(newID == (currentID + 1), "Testing for correct ID"); MITK_TEST_OUTPUT(<< "New: " << newID << " Last: " << currentID); return EXIT_FAILURE; } currentID = newID; } unsigned int numberOfPlanePos = m_Service->GetNumberOfPlanePositions(); // Adding existing planes -> nothing should change for (unsigned int i = 0; i < (m_Geometries.size() - 1) * 0.5; ++i) { unsigned int newID = m_Service->AddNewPlanePosition(m_Geometries.at(i * 2), i * 2); error = ((m_Service->GetNumberOfPlanePositions() != numberOfPlanePos) || (newID != i * 2)); if (error) { MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == numberOfPlanePos, "Checking for correct number of planepositions"); MITK_TEST_CONDITION(newID == i * 2, "Testing for correct ID"); return EXIT_FAILURE; } } return EXIT_SUCCESS; } int testGetPlanePosition() { bool error(true); MITK_TEST_OUTPUT(<< "Starting Test: ######### G e t P l a n e P o s i t i o n #########"); // Testing for existing planepositions for (unsigned int i = 0; i < m_Geometries.size(); ++i) { auto plane = m_Geometries.at(i); auto op = m_Service->GetPlanePosition(i); error = (!mitk::Equal(op->GetHeight(), plane->GetExtent(1)) || !mitk::Equal(op->GetWidth(), plane->GetExtent(0)) || !mitk::Equal(op->GetSpacing(), plane->GetSpacing()) || !mitk::Equal(op->GetTransform()->GetOffset(), plane->GetIndexToWorldTransform()->GetOffset()) || !mitk::Equal(op->GetDirectionVector().GetVnlVector(), plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).normalize()) || !mitk::MatrixEqualElementWise(op->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix())); if (error) { MITK_TEST_OUTPUT(<< "Iteration: " << i) MITK_TEST_CONDITION( mitk::Equal(op->GetHeight(), plane->GetExtent(1)) && mitk::Equal(op->GetWidth(), plane->GetExtent(0)), "Checking for correct extent"); MITK_TEST_CONDITION(mitk::Equal(op->GetSpacing(), plane->GetSpacing()), "Checking for correct spacing"); MITK_TEST_CONDITION(mitk::Equal(op->GetTransform()->GetOffset(), plane->GetIndexToWorldTransform()->GetOffset()), "Checking for correct offset"); MITK_INFO << "Op: " << op->GetDirectionVector() << " plane: " << plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2) << "\n"; MITK_TEST_CONDITION(mitk::Equal(op->GetDirectionVector().GetVnlVector(), plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)), "Checking for correct direction"); MITK_TEST_CONDITION( mitk::MatrixEqualElementWise(op->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix()), "Checking for correct matrix"); return EXIT_FAILURE; } } // Testing for not existing planepositions - error = (m_Service->GetPlanePosition(100000000) != 0 || m_Service->GetPlanePosition(-1) != 0); + error = (m_Service->GetPlanePosition(100000000) != nullptr || m_Service->GetPlanePosition(-1) != nullptr); if (error) { - MITK_TEST_CONDITION(m_Service->GetPlanePosition(100000000) == 0, "Trying to get non existing pos"); - MITK_TEST_CONDITION(m_Service->GetPlanePosition(-1) == 0, "Trying to get non existing pos"); + MITK_TEST_CONDITION(m_Service->GetPlanePosition(100000000) == nullptr, "Trying to get non existing pos"); + MITK_TEST_CONDITION(m_Service->GetPlanePosition(-1) == nullptr, "Trying to get non existing pos"); return EXIT_FAILURE; } return EXIT_SUCCESS; } int testRemovePlanePosition() { MITK_TEST_OUTPUT(<< "Starting Test: ######### R e m o v e P l a n e P o s i t i o n #########"); unsigned int size = m_Service->GetNumberOfPlanePositions(); // Testing for invalid IDs bool removed = m_Service->RemovePlanePosition(-1); removed = m_Service->RemovePlanePosition(1000000); unsigned int size2 = m_Service->GetNumberOfPlanePositions(); if (removed) { MITK_TEST_CONDITION(removed == false, "Testing remove not existing planepositions"); MITK_TEST_CONDITION(size == size2, "Testing remove not existing planepositions"); return EXIT_FAILURE; } // Testing for valid IDs for (unsigned int i = 0; i < m_Geometries.size() * 0.5; i++) { removed = m_Service->RemovePlanePosition(i); unsigned int size2 = m_Service->GetNumberOfPlanePositions(); removed = (size2 == (size - (i + 1))); if (!removed) { MITK_TEST_CONDITION(removed == true, "Testing remove existing planepositions"); MITK_TEST_CONDITION(size == (size - i + 1), "Testing remove existing planepositions"); return EXIT_FAILURE; } } return EXIT_SUCCESS; } int testRemoveAll() { MITK_TEST_OUTPUT(<< "Starting Test: ######### R e m o v e A l l #########"); unsigned int numPos = m_Service->GetNumberOfPlanePositions(); MITK_INFO << numPos; m_Service->RemoveAllPlanePositions(); bool error(true); - error = (m_Service->GetNumberOfPlanePositions() != 0 || m_Service->GetPlanePosition(60) != 0); + error = (m_Service->GetNumberOfPlanePositions() != 0 || m_Service->GetPlanePosition(60) != nullptr); if (error) { MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == 0, "Testing remove all pos"); - MITK_TEST_CONDITION(m_Service->GetPlanePosition(60) == 0, "Testing remove all pos"); + MITK_TEST_CONDITION(m_Service->GetPlanePosition(60) == nullptr, "Testing remove all pos"); return EXIT_FAILURE; } return EXIT_SUCCESS; } int mitkPlanePositionManagerTest(int, char *[]) { MITK_TEST_BEGIN("PlanePositionManager"); SetUpBeforeTest(); int result; MITK_TEST_CONDITION_REQUIRED((result = testAddPlanePosition()) == EXIT_SUCCESS, ""); MITK_TEST_CONDITION_REQUIRED((result = testGetPlanePosition()) == EXIT_SUCCESS, ""); MITK_TEST_CONDITION_REQUIRED((result = testRemovePlanePosition()) == EXIT_SUCCESS, ""); MITK_TEST_CONDITION_REQUIRED((result = testRemoveAll()) == EXIT_SUCCESS, ""); MITK_TEST_END(); } diff --git a/Modules/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonOne.cpp b/Modules/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonOne.cpp index 9380cb316d..59c2261975 100644 --- a/Modules/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonOne.cpp +++ b/Modules/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonOne.cpp @@ -1,80 +1,80 @@ #include "SingletonOne.h" #include "SingletonTwo.h" #include #include #include #include US_USE_NAMESPACE //![s1] SingletonOne& SingletonOne::GetInstance() { static SingletonOne instance; return instance; } //![s1] SingletonOne::SingletonOne() : a(1) { } //![s1d] SingletonOne::~SingletonOne() { std::cout << "SingletonTwo::b = " << SingletonTwo::GetInstance().b << std::endl; } //![s1d] //![ss1gi] SingletonOneService* SingletonOneService::GetInstance() { static ServiceReference serviceRef; static ModuleContext* context = GetModuleContext(); if (!serviceRef) { // This is either the first time GetInstance() was called, // or a SingletonOneService instance has not yet been registered. serviceRef = context->GetServiceReference(); } if (serviceRef) { // We have a valid service reference. It always points to the service // with the lowest id (usually the one which was registered first). // This still might return a null pointer, if all SingletonOneService // instances have been unregistered (during unloading of the library, // for example). return context->GetService(serviceRef); } else { // No SingletonOneService instance was registered yet. - return 0; + return nullptr; } } //![ss1gi] SingletonOneService::SingletonOneService() : a(1) { SingletonTwoService* singletonTwoService = SingletonTwoService::GetInstance(); - assert(singletonTwoService != 0); + assert(singletonTwoService != nullptr); std::cout << "SingletonTwoService::b = " << singletonTwoService->b << std::endl; } //![ss1d] SingletonOneService::~SingletonOneService() { SingletonTwoService* singletonTwoService = SingletonTwoService::GetInstance(); // The module activator must ensure that a SingletonTwoService instance is // available during destruction of a SingletonOneService instance. - assert(singletonTwoService != 0); + assert(singletonTwoService != nullptr); std::cout << "SingletonTwoService::b = " << singletonTwoService->b << std::endl; } //![ss1d] diff --git a/Modules/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonTwo.cpp b/Modules/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonTwo.cpp index db18f6ae01..401abad858 100644 --- a/Modules/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonTwo.cpp +++ b/Modules/CppMicroServices/core/doc/snippets/uServices-singleton/SingletonTwo.cpp @@ -1,64 +1,64 @@ #include "SingletonTwo.h" #include "SingletonOne.h" #include #include #include US_USE_NAMESPACE SingletonTwo& SingletonTwo::GetInstance() { static SingletonTwo instance; return instance; } SingletonTwo::SingletonTwo() : b(2) { std::cout << "Constructing SingletonTwo" << std::endl; } SingletonTwo::~SingletonTwo() { std::cout << "Deleting SingletonTwo" << std::endl; std::cout << "SingletonOne::a = " << SingletonOne::GetInstance().a << std::endl; } SingletonTwoService* SingletonTwoService::GetInstance() { static ServiceReference serviceRef; static ModuleContext* context = GetModuleContext(); if (!serviceRef) { // This is either the first time GetInstance() was called, // or a SingletonTwoService instance has not yet been registered. serviceRef = context->GetServiceReference(); } if (serviceRef) { // We have a valid service reference. It always points to the service // with the lowest id (usually the one which was registered first). // This still might return a null pointer, if all SingletonTwoService // instances have been unregistered (during unloading of the library, // for example). return context->GetService(serviceRef); } else { // No SingletonTwoService instance was registered yet. - return 0; + return nullptr; } } SingletonTwoService::SingletonTwoService() : b(2) { std::cout << "Constructing SingletonTwoService" << std::endl; } SingletonTwoService::~SingletonTwoService() { std::cout << "Deleting SingletonTwoService" << std::endl; } diff --git a/Modules/CppMicroServices/core/doc/snippets/uServices-staticmodules/main.cpp b/Modules/CppMicroServices/core/doc/snippets/uServices-staticmodules/main.cpp index 4a2a0c141f..afb9bb7fd4 100644 --- a/Modules/CppMicroServices/core/doc/snippets/uServices-staticmodules/main.cpp +++ b/Modules/CppMicroServices/core/doc/snippets/uServices-staticmodules/main.cpp @@ -1,39 +1,39 @@ #include US_BEGIN_NAMESPACE struct ModuleActivator; US_END_NAMESPACE US_USE_NAMESPACE // This is just for illustration purposes in code snippets -extern "C" ModuleActivator* _us_module_activator_instance_MyStaticModule1() { return 0; } -extern "C" ModuleActivator* _us_module_activator_instance_MyStaticModule2() { return 0; } +extern "C" ModuleActivator* _us_module_activator_instance_MyStaticModule1() { return nullptr; } +extern "C" ModuleActivator* _us_module_activator_instance_MyStaticModule2() { return nullptr; } extern "C" void _us_import_module_initializer_MyStaticModule1() {} extern "C" void _us_import_module_initializer_MyStaticModule2() {} //! [ImportStaticModuleIntoMain] #include US_IMPORT_MODULE(MyStaticModule1) //! [ImportStaticModuleIntoMain] //! [ImportStaticModuleIntoMain2] #include #ifndef US_BUILD_SHARED_LIBS US_IMPORT_MODULE(CppMicroServices) US_IMPORT_MODULE(MyStaticModule2) US_INITIALIZE_STATIC_MODULE(main) #endif //! [ImportStaticModuleIntoMain2] int main(int /*argc*/, char* /*argv*/[]) { return 0; } //! [InitializeExecutable] #include US_INITIALIZE_MODULE //! [InitializeExecutable] diff --git a/Modules/CppMicroServices/core/src/module/usModule.cpp b/Modules/CppMicroServices/core/src/module/usModule.cpp index 152f50ea56..d6ffa74b42 100644 --- a/Modules/CppMicroServices/core/src/module/usModule.cpp +++ b/Modules/CppMicroServices/core/src/module/usModule.cpp @@ -1,313 +1,313 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModule.h" #include "usModuleContext.h" #include "usModuleActivator.h" #include "usModulePrivate.h" #include "usModuleResource.h" #include "usModuleSettings.h" #include "usCoreModuleContext_p.h" #include "usCoreConfig.h" US_BEGIN_NAMESPACE const std::string& Module::PROP_ID() { static const std::string s("module.id"); return s; } const std::string& Module::PROP_NAME() { static const std::string s("module.name"); return s; } const std::string& Module::PROP_LOCATION() { static const std::string s("module.location"); return s; } const std::string& Module::PROP_VERSION() { static const std::string s("module.version"); return s; } const std::string&Module::PROP_VENDOR() { static const std::string s("module.vendor"); return s; } const std::string&Module::PROP_DESCRIPTION() { static const std::string s("module.description"); return s; } const std::string&Module::PROP_AUTOLOAD_DIR() { static const std::string s("module.autoload_dir"); return s; } const std::string&Module::PROP_AUTOLOADED_MODULES() { static const std::string s("module.autoloaded_modules"); return s; } Module::Module() -: d(0) +: d(nullptr) { } Module::~Module() { delete d; } void Module::Init(CoreModuleContext* coreCtx, ModuleInfo* info) { ModulePrivate* mp = new ModulePrivate(this, coreCtx, info); std::swap(mp, d); delete mp; } void Module::Uninit() { if (d->moduleContext != nullptr) { //d->coreCtx->listeners.HooksModuleStopped(d->moduleContext); d->RemoveModuleResources(); delete d->moduleContext; - d->moduleContext = 0; + d->moduleContext = nullptr; d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::UNLOADED, this)); - d->moduleActivator = 0; + d->moduleActivator = nullptr; } } bool Module::IsLoaded() const { - return d->moduleContext != 0; + return d->moduleContext != nullptr; } void Module::Start() { if (d->moduleContext) { US_WARN << "Module " << d->info.name << " already started."; return; } d->moduleContext = new ModuleContext(this->d); typedef ModuleActivator*(*ModuleActivatorHook)(void); ModuleActivatorHook activatorHook = nullptr; std::string activator_func = "_us_module_activator_instance_" + d->info.name; void* activatorHookSym = ModuleUtils::GetSymbol(d->info, activator_func.c_str()); std::memcpy(&activatorHook, &activatorHookSym, sizeof(void*)); d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::LOADING, this)); // try to get a ModuleActivator instance if (activatorHook) { try { d->moduleActivator = activatorHook(); } catch (...) { US_ERROR << "Creating the module activator of " << d->info.name << " failed"; throw; } // This method should be "noexcept" and by not catching exceptions // here we semantically treat it that way since any exception during // static initialization will either terminate the program or cause // the dynamic loader to report an error. d->moduleActivator->Load(d->moduleContext); } #ifdef US_ENABLE_AUTOLOADING_SUPPORT if (ModuleSettings::IsAutoLoadingEnabled()) { const std::vector loadedPaths = AutoLoadModules(d->info); if (!loadedPaths.empty()) { d->moduleManifest.SetValue(PROP_AUTOLOADED_MODULES(), Any(loadedPaths)); } } #endif d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::LOADED, this)); } void Module::Stop() { - if (d->moduleContext == 0) + if (d->moduleContext == nullptr) { US_WARN << "Module " << d->info.name << " already stopped."; return; } try { d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::UNLOADING, this)); if (d->moduleActivator) { d->moduleActivator->Unload(d->moduleContext); } } catch (...) { US_WARN << "Calling the module activator Unload() method of " << d->info.name << " failed!"; try { this->Uninit(); } catch (...) {} throw; } this->Uninit(); } ModuleContext* Module::GetModuleContext() const { return d->moduleContext; } long Module::GetModuleId() const { return d->info.id; } std::string Module::GetLocation() const { return d->info.location; } std::string Module::GetName() const { return d->info.name; } ModuleVersion Module::GetVersion() const { return d->version; } Any Module::GetProperty(const std::string& key) const { return d->moduleManifest.GetValue(key); } std::vector Module::GetPropertyKeys() const { return d->moduleManifest.GetKeys(); } std::vector Module::GetRegisteredServices() const { std::vector sr; std::vector res; d->coreCtx->services.GetRegisteredByModule(d, sr); for (std::vector::const_iterator i = sr.begin(); i != sr.end(); ++i) { res.push_back(i->GetReference()); } return res; } std::vector Module::GetServicesInUse() const { std::vector sr; std::vector res; d->coreCtx->services.GetUsedByModule(const_cast(this), sr); for (std::vector::const_iterator i = sr.begin(); i != sr.end(); ++i) { res.push_back(i->GetReference()); } return res; } ModuleResource Module::GetResource(const std::string& path) const { if (!d->resourceContainer.IsValid()) { return ModuleResource(); } ModuleResource result(path, d->resourceContainer); if (result) return result; return ModuleResource(); } std::vector Module::FindResources(const std::string& path, const std::string& filePattern, bool recurse) const { std::vector result; if (!d->resourceContainer.IsValid()) { return result; } std::string normalizedPath = path; // add a leading and trailing slash if (normalizedPath.empty()) normalizedPath.push_back('/'); if (*normalizedPath.begin() != '/') normalizedPath = '/' + normalizedPath; if (*normalizedPath.rbegin() != '/') normalizedPath.push_back('/'); d->resourceContainer.FindNodes(d->info.name + normalizedPath, filePattern.empty() ? "*" : filePattern, recurse, result); return result; } US_END_NAMESPACE US_USE_NAMESPACE std::ostream& operator<<(std::ostream& os, const Module& module) { os << "Module[" << "id=" << module.GetModuleId() << ", loc=" << module.GetLocation() << ", name=" << module.GetName() << "]"; return os; } std::ostream& operator<<(std::ostream& os, Module const * module) { return operator<<(os, *module); } diff --git a/Modules/CppMicroServices/core/src/module/usModuleEvent.cpp b/Modules/CppMicroServices/core/src/module/usModuleEvent.cpp index abf57ccc38..cf4b30d706 100644 --- a/Modules/CppMicroServices/core/src/module/usModuleEvent.cpp +++ b/Modules/CppMicroServices/core/src/module/usModuleEvent.cpp @@ -1,119 +1,119 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModuleEvent.h" #include "usModule.h" US_BEGIN_NAMESPACE class ModuleEventData : public SharedData { public: ModuleEventData(ModuleEvent::Type type, Module* module) : type(type), module(module) { } ModuleEventData(const ModuleEventData& other) : SharedData(other), type(other.type), module(other.module) { } const ModuleEvent::Type type; Module* const module; private: // purposely not implemented ModuleEventData& operator=(const ModuleEventData&); }; ModuleEvent::ModuleEvent() - : d(0) + : d(nullptr) { } ModuleEvent::~ModuleEvent() { } bool ModuleEvent::IsNull() const { return !d; } ModuleEvent::ModuleEvent(Type type, Module* module) : d(new ModuleEventData(type, module)) { } ModuleEvent::ModuleEvent(const ModuleEvent& other) : d(other.d) { } ModuleEvent& ModuleEvent::operator=(const ModuleEvent& other) { d = other.d; return *this; } Module* ModuleEvent::GetModule() const { return d->module; } ModuleEvent::Type ModuleEvent::GetType() const { return d->type; } std::ostream& operator<<(std::ostream& os, ModuleEvent::Type eventType) { switch (eventType) { case ModuleEvent::LOADED: return os << "LOADED"; case ModuleEvent::UNLOADED: return os << "UNLOADED"; case ModuleEvent::LOADING: return os << "LOADING"; case ModuleEvent::UNLOADING: return os << "UNLOADING"; default: return os << "Unknown module event type (" << static_cast(eventType) << ")"; } } std::ostream& operator<<(std::ostream& os, const ModuleEvent& event) { if (event.IsNull()) return os << "NONE"; Module* m = event.GetModule(); os << event.GetType() << " #" << m->GetModuleId() << " (" << m->GetLocation() << ")"; return os; } US_END_NAMESPACE diff --git a/Modules/CppMicroServices/core/src/module/usModulePrivate.cpp b/Modules/CppMicroServices/core/src/module/usModulePrivate.cpp index 1e00b80915..01a143c7f1 100644 --- a/Modules/CppMicroServices/core/src/module/usModulePrivate.cpp +++ b/Modules/CppMicroServices/core/src/module/usModulePrivate.cpp @@ -1,148 +1,148 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModulePrivate.h" #include "usModule.h" #include "usModuleContext.h" #include "usModuleActivator.h" #include "usModuleUtils_p.h" #include "usModuleSettings.h" #include "usModuleResource.h" #include "usModuleResourceStream.h" #include "usCoreModuleContext_p.h" #include "usServiceRegistration.h" #include "usServiceReferenceBasePrivate.h" #include #include #include #include US_BEGIN_NAMESPACE AtomicInt ModulePrivate::idCounter; ModulePrivate::ModulePrivate(Module* qq, CoreModuleContext* coreCtx, ModuleInfo* info) : coreCtx(coreCtx) , info(*info) , resourceContainer(info) - , moduleContext(0) - , moduleActivator(0) + , moduleContext(nullptr) + , moduleActivator(nullptr) , q(qq) { // Check if the module provides a manifest.json file and if yes, parse it. if (resourceContainer.IsValid()) { ModuleResource manifestRes("/manifest.json", resourceContainer); if (manifestRes) { ModuleResourceStream manifestStream(manifestRes); try { moduleManifest.Parse(manifestStream); } catch (const std::exception& e) { US_ERROR << "Parsing of manifest.json for module " << info->location << " failed: " << e.what(); } } } // Check if we got version information and validate the version identifier if (moduleManifest.Contains(Module::PROP_VERSION())) { Any versionAny = moduleManifest.GetValue(Module::PROP_VERSION()); std::string errMsg; if (versionAny.Type() != typeid(std::string)) { errMsg = std::string("The version identifier must be a string"); } try { version = ModuleVersion(versionAny.ToString()); } catch (const std::exception& e) { errMsg = std::string("The version identifier is invalid: ") + e.what(); } if (!errMsg.empty()) { throw std::invalid_argument(std::string("The Json value for ") + Module::PROP_VERSION() + " for module " + info->location + " is not valid: " + errMsg); } } std::stringstream propId; propId << this->info.id; moduleManifest.SetValue(Module::PROP_ID(), propId.str()); moduleManifest.SetValue(Module::PROP_LOCATION(), this->info.location); moduleManifest.SetValue(Module::PROP_NAME(), this->info.name); if (moduleManifest.Contains(Module::PROP_AUTOLOAD_DIR())) { this->info.autoLoadDir = moduleManifest.GetValue(Module::PROP_AUTOLOAD_DIR()).ToString(); } else { this->info.autoLoadDir = this->info.name; moduleManifest.SetValue(Module::PROP_AUTOLOAD_DIR(), Any(this->info.autoLoadDir)); } } ModulePrivate::~ModulePrivate() { delete moduleContext; } void ModulePrivate::RemoveModuleResources() { coreCtx->listeners.RemoveAllListeners(moduleContext); std::vector srs; coreCtx->services.GetRegisteredByModule(this, srs); for (std::vector::iterator i = srs.begin(); i != srs.end(); ++i) { try { i->Unregister(); } catch (const std::logic_error& /*ignore*/) { // Someone has unregistered the service after stop completed. // This should not occur, but we don't want get stuck in // an illegal state so we catch it. } } srs.clear(); coreCtx->services.GetUsedByModule(q, srs); for (std::vector::const_iterator i = srs.begin(); i != srs.end(); ++i) { i->GetReference(std::string()).d->UngetService(q, false); } } US_END_NAMESPACE diff --git a/Modules/CppMicroServices/core/src/module/usModuleRegistry.cpp b/Modules/CppMicroServices/core/src/module/usModuleRegistry.cpp index 1865819e20..92b8ff7630 100644 --- a/Modules/CppMicroServices/core/src/module/usModuleRegistry.cpp +++ b/Modules/CppMicroServices/core/src/module/usModuleRegistry.cpp @@ -1,224 +1,224 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModuleRegistry.h" #include "usModule.h" #include "usModuleInfo.h" #include "usModuleContext.h" #include "usModuleActivator.h" #include "usModuleInitialization.h" #include "usCoreModuleContext_p.h" #include "usGetModuleContext.h" #include "usStaticInit_p.h" #include #include US_BEGIN_NAMESPACE typedef US_UNORDERED_MAP_TYPE ModuleMap; US_GLOBAL_STATIC(CoreModuleContext, coreModuleContext) template struct ModuleDeleter { void operator()(GlobalStatic& globalStatic) const { ModuleMap* moduleMap = globalStatic.pointer; for (ModuleMap::const_iterator i = moduleMap->begin(); i != moduleMap->end(); ++i) { delete i->second; } DefaultGlobalStaticDeleter defaultDeleter; defaultDeleter(globalStatic); } }; /** * Table of all installed modules in this framework. * Key is the module id. */ US_GLOBAL_STATIC_WITH_DELETER(ModuleMap, modules, ModuleDeleter) /** * Lock for protecting the modules object */ US_GLOBAL_STATIC(Mutex, modulesLock) /** * Lock for protecting the register count */ US_GLOBAL_STATIC(Mutex, countLock) void ModuleRegistry::Register(ModuleInfo* info) { static long regCount = 0; if (info->id > 0) { // The module was already registered - Module* module = 0; + Module* module = nullptr; { MutexLock lock(*modulesLock()); module = modules()->operator[](info->name); - assert(module != 0); + assert(module != nullptr); } module->Start(); } else { - Module* module = 0; + Module* module = nullptr; // check if the module is reloaded { MutexLock lock(*modulesLock()); ModuleMap* map = modules(); for (ModuleMap::const_iterator i = map->begin(); i != map->end(); ++i) { if (i->second->GetLocation() == info->location && i->second->GetName() == info->name) { module = i->second; info->id = module->GetModuleId(); } } } if (!module) { module = new Module(); countLock()->Lock(); info->id = ++regCount; assert(info->id == 1 ? info->name == "CppMicroServices" : true); countLock()->Unlock(); module->Init(coreModuleContext(), info); MutexLock lock(*modulesLock()); ModuleMap* map = modules(); map->insert(std::make_pair(info->name, module)); } else { module->Init(coreModuleContext(), info); } module->Start(); } } void ModuleRegistry::UnRegister(const ModuleInfo* info) { if (info->id > 1) { - Module* curr = 0; + Module* curr = nullptr; { MutexLock lock(*modulesLock()); curr = modules()->operator[](info->name); - assert(curr != 0); + assert(curr != nullptr); } curr->Stop(); } } Module* ModuleRegistry::GetModule(long id) { MutexLock lock(*modulesLock()); ModuleMap::const_iterator iter = modules()->begin(); ModuleMap::const_iterator iterEnd = modules()->end(); for (; iter != iterEnd; ++iter) { if (iter->second->GetModuleId() == id) { return iter->second; } } - return 0; + return nullptr; } Module* ModuleRegistry::GetModule(const std::string& name) { MutexLock lock(*modulesLock()); ModuleMap::const_iterator iter = modules()->find(name); if (iter != modules()->end()) { return iter->second; } - return 0; + return nullptr; } std::vector ModuleRegistry::GetModules() { MutexLock lock(*modulesLock()); std::vector result; ModuleMap* map = modules(); ModuleMap::const_iterator iter = map->begin(); ModuleMap::const_iterator iterEnd = map->end(); for (; iter != iterEnd; ++iter) { result.push_back(iter->second); } return result; } std::vector ModuleRegistry::GetLoadedModules() { MutexLock lock(*modulesLock()); std::vector result; ModuleMap::const_iterator iter = modules()->begin(); ModuleMap::const_iterator iterEnd = modules()->end(); for (; iter != iterEnd; ++iter) { if (iter->second->IsLoaded()) { result.push_back(iter->second); } } return result; } // Control the static initialization order for several core objects struct StaticInitializationOrder { StaticInitializationOrder() { ModuleSettings::GetLogLevel(); modulesLock(); countLock(); modules(); coreModuleContext(); } }; static StaticInitializationOrder _staticInitializationOrder; US_END_NAMESPACE // We initialize the CppMicroService module after making sure // that all other global statics have been initialized above US_INITIALIZE_MODULE diff --git a/Modules/CppMicroServices/core/src/module/usModuleUtils.cpp b/Modules/CppMicroServices/core/src/module/usModuleUtils.cpp index 0d927059a6..2ee7a13953 100644 --- a/Modules/CppMicroServices/core/src/module/usModuleUtils.cpp +++ b/Modules/CppMicroServices/core/src/module/usModuleUtils.cpp @@ -1,171 +1,171 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usModuleUtils_p.h" #include #include #include US_BEGIN_NAMESPACE namespace { #ifdef US_BUILD_SHARED_LIBS const bool sharedLibMode = true; #else const bool sharedLibMode = false; #endif } #ifdef __GNUC__ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include std::string GetLibraryPath_impl(void* symbol) { Dl_info info; if (dladdr(symbol, &info)) { return info.dli_fname; } else { US_DEBUG << "GetLibraryPath_impl() failed for address " << symbol; } return ""; } void* GetSymbol_impl(const ModuleInfo& moduleInfo, const char* symbol) { // Clear the last error message dlerror(); - void* selfHandle = 0; + void* selfHandle = nullptr; if (!sharedLibMode || moduleInfo.name == "main") { // Get the handle of the executable - selfHandle = dlopen(0, RTLD_LAZY); + selfHandle = dlopen(nullptr, RTLD_LAZY); } else { selfHandle = dlopen(moduleInfo.location.c_str(), RTLD_LAZY); } if (selfHandle) { void* addr = dlsym(selfHandle, symbol); if (!addr) { const char* dlerrorMsg = dlerror(); if (dlerrorMsg) { US_DEBUG << "GetSymbol_impl() failed: " << dlerrorMsg; } } dlclose(selfHandle); return addr; } else { US_DEBUG << "GetSymbol_impl() dlopen() failed: " << dlerror(); } - return 0; + return nullptr; } #elif _WIN32 #include std::string GetLibraryPath_impl(void *symbol) { HMODULE handle = 0; BOOL handleError = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, static_cast(symbol), &handle); if (!handleError) { // Test US_DEBUG << "GetLibraryPath_impl():GetModuleHandle() " << GetLastErrorStr(); return ""; } char modulePath[512]; if (GetModuleFileName(handle, modulePath, 512)) { return modulePath; } US_DEBUG << "GetLibraryPath_impl():GetModuleFileName() " << GetLastErrorStr(); return ""; } void* GetSymbol_impl(const ModuleInfo& moduleInfo, const char* symbol) { HMODULE handle = nullptr; if (!sharedLibMode || moduleInfo.name == "main") { handle = GetModuleHandle(nullptr); } else { handle = GetModuleHandle(moduleInfo.location.c_str()); } if (!handle) { US_DEBUG << "GetSymbol_impl():GetModuleHandle() " << GetLastErrorStr(); return 0; } void* addr = (void*)GetProcAddress(handle, symbol); if (!addr) { US_DEBUG << "GetSymbol_impl():GetProcAddress(handle," << symbol << ") " << GetLastErrorStr(); } return addr; } #else std::string GetLibraryPath_impl(void*) { return ""; } void* GetSymbol_impl(const ModuleInfo&, const char* symbol) { return 0; } #endif std::string ModuleUtils::GetLibraryPath(void* symbol) { return GetLibraryPath_impl(symbol); } void* ModuleUtils::GetSymbol(const ModuleInfo& module, const char* symbol) { return GetSymbol_impl(module, symbol); } US_END_NAMESPACE diff --git a/Modules/CppMicroServices/core/src/service/usLDAPExpr.cpp b/Modules/CppMicroServices/core/src/service/usLDAPExpr.cpp index 8a1c4475d2..d9befbf95b 100644 --- a/Modules/CppMicroServices/core/src/service/usLDAPExpr.cpp +++ b/Modules/CppMicroServices/core/src/service/usLDAPExpr.cpp @@ -1,851 +1,851 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usLDAPExpr_p.h" #include "usAny.h" #include "usServicePropertiesImpl_p.h" #include #include #include #include #include #include #include US_BEGIN_NAMESPACE namespace LDAPExprConstants { static LDAPExpr::Byte WILDCARD() { static LDAPExpr::Byte b = std::numeric_limits::max(); return b; } static const std::string& WILDCARD_STRING() { static std::string s(1, WILDCARD()); return s; } static const std::string& nullptrQ() { static std::string s = "Null query"; return s; } static const std::string& GARBAGE() { static std::string s = "Trailing garbage"; return s; } static const std::string& EOS() { static std::string s = "Unexpected end of query"; return s; } static const std::string& MALFORMED() { static std::string s = "Malformed query"; return s; } static const std::string& OPERATOR() { static std::string s = "Undefined operator"; return s; } } bool stricomp(const std::string::value_type& v1, const std::string::value_type& v2) { return ::tolower(v1) == ::tolower(v2); } //! Contains the current parser position and parsing utility methods. class LDAPExpr::ParseState { private: std::size_t m_pos; std::string m_str; public: ParseState(const std::string &str); //! Move m_pos to remove the prefix \a pre bool prefix(const std::string &pre); /** Peek a char at m_pos \note If index out of bounds, throw exception */ LDAPExpr::Byte peek(); //! Increment m_pos by n void skip(int n); //! return string from m_pos until the end std::string rest() const; //! Move m_pos until there's no spaces void skipWhite(); //! Get string until special chars. Move m_pos std::string getAttributeName(); //! Get string and convert * to WILDCARD std::string getAttributeValue(); //! Throw InvalidSyntaxException exception void error(const std::string &m) const; }; class LDAPExprData : public SharedData { public: LDAPExprData( int op, const std::vector& args ) : m_operator(op), m_args(args), m_attrName(), m_attrValue() { } LDAPExprData( int op, std::string attrName, const std::string& attrValue ) : m_operator(op), m_args(), m_attrName(attrName), m_attrValue(attrValue) { } LDAPExprData( const LDAPExprData& other ) : SharedData(other), m_operator(other.m_operator), m_args(other.m_args), m_attrName(other.m_attrName), m_attrValue(other.m_attrValue) { } int m_operator; std::vector m_args; std::string m_attrName; std::string m_attrValue; }; LDAPExpr::LDAPExpr() : d() { } LDAPExpr::LDAPExpr( const std::string &filter ) : d() { ParseState ps(filter); try { LDAPExpr expr = ParseExpr(ps); if (!Trim(ps.rest()).empty()) { ps.error(LDAPExprConstants::GARBAGE() + " '" + ps.rest() + "'"); } d = expr.d; } catch (const std::out_of_range&) { ps.error(LDAPExprConstants::EOS()); } } LDAPExpr::LDAPExpr( int op, const std::vector& args ) : d(new LDAPExprData(op, args)) { } LDAPExpr::LDAPExpr( int op, const std::string &attrName, const std::string &attrValue ) : d(new LDAPExprData(op, attrName, attrValue)) { } LDAPExpr::LDAPExpr( const LDAPExpr& other ) : d(other.d) { } LDAPExpr& LDAPExpr::operator=(const LDAPExpr& other) { d = other.d; return *this; } LDAPExpr::~LDAPExpr() { } std::string LDAPExpr::Trim(std::string str) { str.erase(0, str.find_first_not_of(' ')); str.erase(str.find_last_not_of(' ')+1); return str; } bool LDAPExpr::GetMatchedObjectClasses(ObjectClassSet& objClasses) const { if (d->m_operator == EQ) { if (d->m_attrName.length() == ServiceConstants::OBJECTCLASS().length() && std::equal(d->m_attrName.begin(), d->m_attrName.end(), ServiceConstants::OBJECTCLASS().begin(), stricomp) && d->m_attrValue.find(LDAPExprConstants::WILDCARD()) == std::string::npos) { objClasses.insert( d->m_attrValue ); return true; } return false; } else if (d->m_operator == AND) { bool result = false; for (std::size_t i = 0; i < d->m_args.size( ); i++) { LDAPExpr::ObjectClassSet r; if (d->m_args[i].GetMatchedObjectClasses(r)) { result = true; if (objClasses.empty()) { objClasses = r; } else { // if AND op and classes in several operands, // then only the intersection is possible. LDAPExpr::ObjectClassSet::iterator it1 = objClasses.begin(); LDAPExpr::ObjectClassSet::iterator it2 = r.begin(); while ( (it1 != objClasses.end()) && (it2 != r.end()) ) { if (*it1 < *it2) { objClasses.erase(it1++); } else if (*it2 < *it1) { ++it2; } else { // *it1 == *it2 ++it1; ++it2; } } // Anything left in set_1 from here on did not appear in set_2, // so we remove it. objClasses.erase(it1, objClasses.end()); } } } return result; } else if (d->m_operator == OR) { for (std::size_t i = 0; i < d->m_args.size( ); i++) { LDAPExpr::ObjectClassSet r; if (d->m_args[i].GetMatchedObjectClasses(r)) { std::copy(r.begin(), r.end(), std::inserter(objClasses, objClasses.begin())); } else { objClasses.clear(); return false; } } return true; } return false; } std::string LDAPExpr::ToLower(const std::string& str) { std::string lowerStr(str); std::transform(str.begin(), str.end(), lowerStr.begin(), ::tolower); return lowerStr; } bool LDAPExpr::IsSimple(const StringList& keywords, LocalCache& cache, bool matchCase ) const { if (cache.empty()) { cache.resize(keywords.size()); } if (d->m_operator == EQ) { StringList::const_iterator index; if ((index = std::find(keywords.begin(), keywords.end(), matchCase ? d->m_attrName : ToLower(d->m_attrName))) != keywords.end() && d->m_attrValue.find_first_of(LDAPExprConstants::WILDCARD()) == std::string::npos) { cache[index - keywords.begin()] = StringList(1, d->m_attrValue); return true; } } else if (d->m_operator == OR) { for (std::size_t i = 0; i < d->m_args.size( ); i++) { if (!d->m_args[i].IsSimple(keywords, cache, matchCase)) return false; } return true; } return false; } bool LDAPExpr::IsNull() const { return !d; } bool LDAPExpr::Query( const std::string& filter, const ServicePropertiesImpl& pd) { return LDAPExpr(filter).Evaluate(pd, false); } bool LDAPExpr::Evaluate( const ServicePropertiesImpl& p, bool matchCase ) const { if ((d->m_operator & SIMPLE) != 0) { // try case sensitive match first int index = p.FindCaseSensitive(d->m_attrName); if (index < 0 && !matchCase) index = p.Find(d->m_attrName); return index < 0 ? false : Compare(p.Value(index), d->m_operator, d->m_attrValue); } else { // (d->m_operator & COMPLEX) != 0 switch (d->m_operator) { case AND: for (std::size_t i = 0; i < d->m_args.size(); i++) { if (!d->m_args[i].Evaluate(p, matchCase)) return false; } return true; case OR: for (std::size_t i = 0; i < d->m_args.size(); i++) { if (d->m_args[i].Evaluate(p, matchCase)) return true; } return false; case NOT: return !d->m_args[0].Evaluate(p, matchCase); default: return false; // Cannot happen } } } bool LDAPExpr::Compare( const Any& obj, int op, const std::string& s ) const { if (obj.Empty()) return false; if (op == EQ && s == LDAPExprConstants::WILDCARD_STRING()) return true; try { const std::type_info& objType = obj.Type(); if (objType == typeid(std::string)) { return CompareString(ref_any_cast(obj), op, s); } else if (objType == typeid(std::vector)) { const std::vector& list = ref_any_cast >(obj); for (std::size_t it = 0; it != list.size(); it++) { if (CompareString(list[it], op, s)) return true; } } else if (objType == typeid(std::list)) { const std::list& list = ref_any_cast >(obj); for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) { if (CompareString(*it, op, s)) return true; } } else if (objType == typeid(char)) { return CompareString(std::string(1, ref_any_cast(obj)), op, s); } else if (objType == typeid(bool)) { if (op==LE || op==GE) return false; std::string boolVal = any_cast(obj) ? "true" : "false"; return std::equal(s.begin(), s.end(), boolVal.begin(), stricomp); } else if (objType == typeid(short)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(int)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(long int)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(long long int)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(unsigned char)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(unsigned short)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(unsigned int)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(unsigned long int)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(unsigned long long int)) { return CompareIntegralType(obj, op, s); } else if (objType == typeid(float)) { errno = 0; - char* endptr = 0; + char* endptr = nullptr; double sFloat = strtod(s.c_str(), &endptr); if ((errno == ERANGE && (sFloat == 0 || sFloat == HUGE_VAL || sFloat == -HUGE_VAL)) || (errno != 0 && sFloat == 0) || endptr == s.c_str()) { return false; } double floatVal = static_cast(any_cast(obj)); switch(op) { case LE: return floatVal <= sFloat; case GE: return floatVal >= sFloat; default: /*APPROX and EQ*/ double diff = floatVal - sFloat; return (diff < std::numeric_limits::epsilon()) && (diff > -std::numeric_limits::epsilon()); } } else if (objType == typeid(double)) { errno = 0; - char* endptr = 0; + char* endptr = nullptr; double sDouble = strtod(s.c_str(), &endptr); if ((errno == ERANGE && (sDouble == 0 || sDouble == HUGE_VAL || sDouble == -HUGE_VAL)) || (errno != 0 && sDouble == 0) || endptr == s.c_str()) { return false; } double doubleVal = any_cast(obj); switch(op) { case LE: return doubleVal <= sDouble; case GE: return doubleVal >= sDouble; default: /*APPROX and EQ*/ double diff = doubleVal - sDouble; return (diff < std::numeric_limits::epsilon()) && (diff > -std::numeric_limits::epsilon()); } } else if (objType == typeid(std::vector)) { const std::vector& list = ref_any_cast >(obj); for (std::size_t it = 0; it != list.size(); it++) { if (Compare(list[it], op, s)) return true; } } } catch (...) { // This might happen if a std::string-to-datatype conversion fails // Just consider it a false match and ignore the exception } return false; } template bool LDAPExpr::CompareIntegralType(const Any& obj, const int op, const std::string& s) const { errno = 0; - char* endptr = 0; + char* endptr = nullptr; long longInt = strtol(s.c_str(), &endptr, 10); if ((errno == ERANGE && (longInt == std::numeric_limits::max() || longInt == std::numeric_limits::min())) || (errno != 0 && longInt == 0) || endptr == s.c_str()) { return false; } T sInt = static_cast(longInt); T intVal = any_cast(obj); switch(op) { case LE: return intVal <= sInt; case GE: return intVal >= sInt; default: /*APPROX and EQ*/ return intVal == sInt; } } bool LDAPExpr::CompareString( const std::string& s1, int op, const std::string& s2 ) { switch(op) { case LE: return s1.compare(s2) <= 0; case GE: return s1.compare(s2) >= 0; case EQ: return PatSubstr(s1,s2); case APPROX: return FixupString(s2) == FixupString(s1); default: return false; } } std::string LDAPExpr::FixupString( const std::string& s ) { std::string sb; sb.reserve(s.size()); std::size_t len = s.length(); for(std::size_t i=0; im_operator std::vector v; do { v.push_back(ParseExpr(ps)); ps.skipWhite(); } while (ps.peek() == '('); std::size_t n = v.size(); if (!ps.prefix(")") || n == 0 || (op == NOT && n > 1)) ps.error(LDAPExprConstants::MALFORMED()); return LDAPExpr(op, v); } LDAPExpr LDAPExpr::ParseSimple( ParseState &ps ) { std::string attrName = ps.getAttributeName(); if (attrName.empty()) ps.error(LDAPExprConstants::MALFORMED()); int op = 0; if (ps.prefix("=")) op = EQ; else if (ps.prefix("<=")) op = LE; else if(ps.prefix(">=")) op = GE; else if(ps.prefix("~=")) op = APPROX; else { // System.out.println("undef op='" + ps.peek() + "'"); ps.error(LDAPExprConstants::OPERATOR()); // Does not return } std::string attrValue = ps.getAttributeValue(); if (!ps.prefix(")")) ps.error(LDAPExprConstants::MALFORMED()); return LDAPExpr(op, attrName, attrValue); } const std::string LDAPExpr::ToString() const { std::string res; res.append("("); if ((d->m_operator & SIMPLE) != 0) { res.append(d->m_attrName); switch (d->m_operator) { case EQ: res.append("="); break; case LE: res.append("<="); break; case GE: res.append(">="); break; case APPROX: res.append("~="); break; } for (std::size_t i = 0; i < d->m_attrValue.length(); i++) { Byte c = d->m_attrValue.at(i); if (c == '(' || c == ')' || c == '*' || c == '\\') { res.append(1, '\\'); } else if (c == LDAPExprConstants::WILDCARD()) { c = '*'; } res.append(1, c); } } else { switch (d->m_operator) { case AND: res.append("&"); break; case OR: res.append("|"); break; case NOT: res.append("!"); break; } for (std::size_t i = 0; i < d->m_args.size(); i++) { res.append(d->m_args[i].ToString()); } } res.append(")"); return res; } LDAPExpr::ParseState::ParseState( const std::string& str ) : m_pos(0), m_str() { if (str.empty()) { error(LDAPExprConstants::nullptrQ()); } m_str = str; } bool LDAPExpr::ParseState::prefix( const std::string& pre ) { std::string::iterator startIter = m_str.begin() + m_pos; if (!std::equal(pre.begin(), pre.end(), startIter)) return false; m_pos += pre.size(); return true; } char LDAPExpr::ParseState::peek() { if ( m_pos >= m_str.size() ) { throw std::out_of_range( "LDAPExpr" ); } return m_str.at(m_pos); } void LDAPExpr::ParseState::skip( int n ) { m_pos += n; } std::string LDAPExpr::ParseState::rest() const { return m_str.substr(m_pos); } void LDAPExpr::ParseState::skipWhite() { while (std::isspace(peek())) { m_pos++; } } std::string LDAPExpr::ParseState::getAttributeName() { std::size_t start = m_pos; std::size_t n = 0; bool nIsSet = false; for(;; m_pos++) { Byte c = peek(); if (c == '(' || c == ')' || c == '<' || c == '>' || c == '=' || c == '~') { break; } else if (!std::isspace(c)) { n = m_pos - start + 1; nIsSet = true; } } if (!nIsSet) { return std::string(); } return m_str.substr(start, n); } std::string LDAPExpr::ParseState::getAttributeValue() { std::string sb; bool exit = false; while( !exit ) { Byte c = peek( ); switch(c) { case '(': case ')': exit = true; break; case '*': sb.append(1, LDAPExprConstants::WILDCARD()); break; case '\\': sb.append(1, m_str.at(++m_pos)); break; default: sb.append(1, c); break; } if ( !exit ) { m_pos++; } } return sb; } void LDAPExpr::ParseState::error( const std::string &m ) const { std::string errorStr = m + ": " + (m_str.empty() ? "" : m_str.substr(m_pos)); throw std::invalid_argument(errorStr); } US_END_NAMESPACE diff --git a/Modules/CppMicroServices/core/src/service/usLDAPFilter.cpp b/Modules/CppMicroServices/core/src/service/usLDAPFilter.cpp index 6eb04f42d9..30968a4548 100644 --- a/Modules/CppMicroServices/core/src/service/usLDAPFilter.cpp +++ b/Modules/CppMicroServices/core/src/service/usLDAPFilter.cpp @@ -1,121 +1,121 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usLDAPFilter.h" #include "usLDAPExpr_p.h" #include "usServicePropertiesImpl_p.h" #include "usServiceReference.h" #include "usServiceReferenceBasePrivate.h" #include US_BEGIN_NAMESPACE class LDAPFilterData : public SharedData { public: LDAPFilterData() : ldapExpr() {} LDAPFilterData(const std::string& filter) : ldapExpr(filter) {} LDAPFilterData(const LDAPFilterData& other) : SharedData(other), ldapExpr(other.ldapExpr) {} LDAPExpr ldapExpr; }; LDAPFilter::LDAPFilter() - : d(0) + : d(nullptr) { } LDAPFilter::LDAPFilter(const std::string& filter) - : d(0) + : d(nullptr) { try { d = new LDAPFilterData(filter); } catch (const std::exception& e) { throw std::invalid_argument(e.what()); } } LDAPFilter::LDAPFilter(const LDAPFilter& other) : d(other.d) { } LDAPFilter::~LDAPFilter() { } LDAPFilter::operator bool_type() const { - return d.ConstData() != 0 ? &LDAPFilter::d : nullptr; + return d.ConstData() != nullptr ? &LDAPFilter::d : nullptr; } bool LDAPFilter::Match(const ServiceReferenceBase& reference) const { return d->ldapExpr.Evaluate(reference.d->GetProperties(), true); } bool LDAPFilter::Match(const ServiceProperties& dictionary) const { return d->ldapExpr.Evaluate(ServicePropertiesImpl(dictionary), false); } bool LDAPFilter::MatchCase(const ServiceProperties& dictionary) const { return d->ldapExpr.Evaluate(ServicePropertiesImpl(dictionary), true); } std::string LDAPFilter::ToString() const { return d->ldapExpr.ToString(); } bool LDAPFilter::operator==(const LDAPFilter& other) const { return d->ldapExpr.ToString() == other.d->ldapExpr.ToString(); } LDAPFilter& LDAPFilter::operator=(const LDAPFilter& filter) { d = filter.d; return *this; } US_END_NAMESPACE US_USE_NAMESPACE std::ostream& operator<<(std::ostream& os, const LDAPFilter& filter) { return os << filter.ToString(); } diff --git a/Modules/CppMicroServices/core/src/service/usServiceEvent.cpp b/Modules/CppMicroServices/core/src/service/usServiceEvent.cpp index 2e351a4887..a2fd65ef64 100644 --- a/Modules/CppMicroServices/core/src/service/usServiceEvent.cpp +++ b/Modules/CppMicroServices/core/src/service/usServiceEvent.cpp @@ -1,130 +1,130 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usServiceEvent.h" #include "usServiceProperties.h" US_BEGIN_NAMESPACE class ServiceEventData : public SharedData { public: ServiceEventData(const ServiceEvent::Type& type, const ServiceReferenceBase& reference) : type(type), reference(reference) { } ServiceEventData(const ServiceEventData& other) : SharedData(other), type(other.type), reference(other.reference) { } const ServiceEvent::Type type; const ServiceReferenceBase reference; private: // purposely not implemented ServiceEventData& operator=(const ServiceEventData&); }; ServiceEvent::ServiceEvent() - : d(0) + : d(nullptr) { } ServiceEvent::~ServiceEvent() { } bool ServiceEvent::IsNull() const { return !d; } ServiceEvent::ServiceEvent(Type type, const ServiceReferenceBase& reference) : d(new ServiceEventData(type, reference)) { } ServiceEvent::ServiceEvent(const ServiceEvent& other) : d(other.d) { } ServiceEvent& ServiceEvent::operator=(const ServiceEvent& other) { d = other.d; return *this; } ServiceReferenceU ServiceEvent::GetServiceReference() const { return d->reference; } ServiceEvent::Type ServiceEvent::GetType() const { return d->type; } std::ostream& operator<<(std::ostream& os, const ServiceEvent::Type& type) { switch(type) { case ServiceEvent::MODIFIED: return os << "MODIFIED"; case ServiceEvent::MODIFIED_ENDMATCH: return os << "MODIFIED_ENDMATCH"; case ServiceEvent::REGISTERED: return os << "REGISTERED"; case ServiceEvent::UNREGISTERING: return os << "UNREGISTERING"; default: return os << "unknown service event type (" << static_cast(type) << ")"; } } std::ostream& operator<<(std::ostream& os, const ServiceEvent& event) { if (event.IsNull()) return os << "NONE"; os << event.GetType(); ServiceReferenceU sr = event.GetServiceReference(); if (sr) { // Some events will not have a service reference long int sid = any_cast(sr.GetProperty(ServiceConstants::SERVICE_ID())); os << " " << sid; Any classes = sr.GetProperty(ServiceConstants::OBJECTCLASS()); os << " objectClass=" << classes.ToString() << ")"; } return os; } US_END_NAMESPACE diff --git a/Modules/CppMicroServices/core/src/service/usServiceReferenceBase.cpp b/Modules/CppMicroServices/core/src/service/usServiceReferenceBase.cpp index 442089086f..e2ce07362e 100644 --- a/Modules/CppMicroServices/core/src/service/usServiceReferenceBase.cpp +++ b/Modules/CppMicroServices/core/src/service/usServiceReferenceBase.cpp @@ -1,221 +1,221 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usServiceReferenceBase.h" #include "usServiceReferenceBasePrivate.h" #include "usServiceRegistrationBasePrivate.h" #include "usModule.h" #include "usModulePrivate.h" #include US_BEGIN_NAMESPACE ServiceReferenceBase::ServiceReferenceBase() - : d(new ServiceReferenceBasePrivate(0)) + : d(new ServiceReferenceBasePrivate(nullptr)) { } ServiceReferenceBase::ServiceReferenceBase(const ServiceReferenceBase& ref) : d(ref.d) { d->ref.Ref(); } ServiceReferenceBase::ServiceReferenceBase(ServiceRegistrationBasePrivate* reg) : d(new ServiceReferenceBasePrivate(reg)) { } void ServiceReferenceBase::SetInterfaceId(const std::string& interfaceId) { if (d->ref > 1) { // detach d->ref.Deref(); d = new ServiceReferenceBasePrivate(d->registration); } d->interfaceId = interfaceId; } ServiceReferenceBase::operator bool_type() const { - return GetModule() != 0 ? &ServiceReferenceBase::d : nullptr; + return GetModule() != nullptr ? &ServiceReferenceBase::d : nullptr; } ServiceReferenceBase& ServiceReferenceBase::operator=(int null) { if (null == 0) { if (!d->ref.Deref()) delete d; - d = new ServiceReferenceBasePrivate(0); + d = new ServiceReferenceBasePrivate(nullptr); } return *this; } ServiceReferenceBase::~ServiceReferenceBase() { if (!d->ref.Deref()) delete d; } Any ServiceReferenceBase::GetProperty(const std::string& key) const { MutexLock lock(d->registration->propsLock); return d->registration->properties.Value(key); } void ServiceReferenceBase::GetPropertyKeys(std::vector& keys) const { MutexLock lock(d->registration->propsLock); const std::vector& ks = d->registration->properties.Keys(); keys.assign(ks.begin(), ks.end()); } Module* ServiceReferenceBase::GetModule() const { - if (d->registration == 0 || d->registration->module == 0) + if (d->registration == nullptr || d->registration->module == nullptr) { - return 0; + return nullptr; } return d->registration->module->q; } void ServiceReferenceBase::GetUsingModules(std::vector& modules) const { MutexLock lock(d->registration->propsLock); ServiceRegistrationBasePrivate::ModuleToRefsMap::const_iterator end = d->registration->dependents.end(); for (ServiceRegistrationBasePrivate::ModuleToRefsMap::const_iterator iter = d->registration->dependents.begin(); iter != end; ++iter) { modules.push_back(iter->first); } } bool ServiceReferenceBase::operator<(const ServiceReferenceBase& reference) const { if (!(*this)) { return true; } if (!reference) { return false; } const Any anyR1 = GetProperty(ServiceConstants::SERVICE_RANKING()); const Any anyR2 = reference.GetProperty(ServiceConstants::SERVICE_RANKING()); assert(anyR1.Empty() || anyR1.Type() == typeid(int)); assert(anyR2.Empty() || anyR2.Type() == typeid(int)); const int r1 = anyR1.Empty() ? 0 : *any_cast(&anyR1); const int r2 = anyR2.Empty() ? 0 : *any_cast(&anyR2); if (r1 != r2) { // use ranking if ranking differs return r1 < r2; } else { const Any anyId1 = GetProperty(ServiceConstants::SERVICE_ID()); const Any anyId2 = reference.GetProperty(ServiceConstants::SERVICE_ID()); assert(anyId1.Type() == typeid(long int)); assert(anyId2.Type() == typeid(long int)); const long int id1 = *any_cast(&anyId1); const long int id2 = *any_cast(&anyId2); // otherwise compare using IDs, // is less than if it has a higher ID. return id2 < id1; } } bool ServiceReferenceBase::operator==(const ServiceReferenceBase& reference) const { return d->registration == reference.d->registration; } ServiceReferenceBase& ServiceReferenceBase::operator=(const ServiceReferenceBase& reference) { ServiceReferenceBasePrivate* curr_d = d; d = reference.d; d->ref.Ref(); if (!curr_d->ref.Deref()) delete curr_d; return *this; } bool ServiceReferenceBase::IsConvertibleTo(const std::string& interfaceId) const { return d->IsConvertibleTo(interfaceId); } std::string ServiceReferenceBase::GetInterfaceId() const { return d->interfaceId; } std::size_t ServiceReferenceBase::Hash() const { using namespace US_HASH_FUNCTION_NAMESPACE; return US_HASH_FUNCTION(ServiceRegistrationBasePrivate*, this->d->registration); } US_END_NAMESPACE US_USE_NAMESPACE std::ostream& operator<<(std::ostream& os, const ServiceReferenceBase& serviceRef) { if (serviceRef) { assert(serviceRef.GetModule() != nullptr); os << "Reference for service object registered from " << serviceRef.GetModule()->GetName() << " " << serviceRef.GetModule()->GetVersion() << " ("; std::vector keys; serviceRef.GetPropertyKeys(keys); size_t keySize = keys.size(); for(size_t i = 0; i < keySize; ++i) { os << keys[i] << "=" << serviceRef.GetProperty(keys[i]).ToString(); if (i < keySize-1) os << ","; } os << ")"; } else { os << "Invalid service reference"; } return os; } diff --git a/Modules/CppMicroServices/core/src/service/usServiceRegistrationBase.cpp b/Modules/CppMicroServices/core/src/service/usServiceRegistrationBase.cpp index bdc9c1b88c..e92dbc469c 100644 --- a/Modules/CppMicroServices/core/src/service/usServiceRegistrationBase.cpp +++ b/Modules/CppMicroServices/core/src/service/usServiceRegistrationBase.cpp @@ -1,271 +1,271 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usServiceRegistrationBase.h" #include "usServiceRegistrationBasePrivate.h" #include "usServiceListenerEntry_p.h" #include "usServiceRegistry_p.h" #include "usServiceFactory.h" #include "usModulePrivate.h" #include "usCoreModuleContext_p.h" #include US_BEGIN_NAMESPACE ServiceRegistrationBase::ServiceRegistrationBase() - : d(0) + : d(nullptr) { } ServiceRegistrationBase::ServiceRegistrationBase(const ServiceRegistrationBase& reg) : d(reg.d) { if (d) d->ref.Ref(); } ServiceRegistrationBase::ServiceRegistrationBase(ServiceRegistrationBasePrivate* registrationPrivate) : d(registrationPrivate) { if (d) d->ref.Ref(); } ServiceRegistrationBase::ServiceRegistrationBase(ModulePrivate* module, const InterfaceMap& service, const ServicePropertiesImpl& props) : d(new ServiceRegistrationBasePrivate(module, service, props)) { } ServiceRegistrationBase::operator bool_type() const { return d != nullptr ? &ServiceRegistrationBase::d : nullptr; } ServiceRegistrationBase& ServiceRegistrationBase::operator=(int null) { if (null == 0) { if (d && !d->ref.Deref()) { delete d; } - d = 0; + d = nullptr; } return *this; } ServiceRegistrationBase::~ServiceRegistrationBase() { if (d && !d->ref.Deref()) delete d; } ServiceReferenceBase ServiceRegistrationBase::GetReference(const std::string& interfaceId) const { if (!d) throw std::logic_error("ServiceRegistrationBase object invalid"); if (!d->available) throw std::logic_error("Service is unregistered"); ServiceReferenceBase ref = d->reference; ref.SetInterfaceId(interfaceId); return ref; } void ServiceRegistrationBase::SetProperties(const ServiceProperties& props) { if (!d) throw std::logic_error("ServiceRegistrationBase object invalid"); MutexLock lock(d->eventLock); ServiceEvent modifiedEndMatchEvent(ServiceEvent::MODIFIED_ENDMATCH, d->reference); ServiceListeners::ServiceListenerEntries before; // TBD, optimize the locking of services { //MutexLock lock2(d->module->coreCtx->globalFwLock); if (d->available) { // NYI! Optimize the MODIFIED_ENDMATCH code int old_rank = 0; int new_rank = 0; std::vector classes; { MutexLock lock3(d->propsLock); { const Any& any = d->properties.Value(ServiceConstants::SERVICE_RANKING()); if (any.Type() == typeid(int)) old_rank = any_cast(any); } d->module->coreCtx->listeners.GetMatchingServiceListeners(modifiedEndMatchEvent, before, false); classes = ref_any_cast >(d->properties.Value(ServiceConstants::OBJECTCLASS())); long int sid = any_cast(d->properties.Value(ServiceConstants::SERVICE_ID())); d->properties = ServiceRegistry::CreateServiceProperties(props, classes, false, false, sid); { const Any& any = d->properties.Value(ServiceConstants::SERVICE_RANKING()); if (any.Type() == typeid(int)) new_rank = any_cast(any); } } if (old_rank != new_rank) { d->module->coreCtx->services.UpdateServiceRegistrationOrder(*this, classes); } } else { throw std::logic_error("Service is unregistered"); } } ServiceEvent modifiedEvent(ServiceEvent::MODIFIED, d->reference); ServiceListeners::ServiceListenerEntries matchingListeners; d->module->coreCtx->listeners.GetMatchingServiceListeners(modifiedEvent, matchingListeners); d->module->coreCtx->listeners.ServiceChanged(matchingListeners, modifiedEvent, before); d->module->coreCtx->listeners.ServiceChanged(before, modifiedEndMatchEvent); } void ServiceRegistrationBase::Unregister() { if (!d) throw std::logic_error("ServiceRegistrationBase object invalid"); if (d->unregistering) return; // Silently ignore redundant unregistration. { MutexLock lock(d->eventLock); if (d->unregistering) return; d->unregistering = true; if (d->available) { if (d->module) { d->module->coreCtx->services.RemoveServiceRegistration(*this); } } else { throw std::logic_error("Service is unregistered"); } } if (d->module) { ServiceListeners::ServiceListenerEntries listeners; ServiceEvent unregisteringEvent(ServiceEvent::UNREGISTERING, d->reference); d->module->coreCtx->listeners.GetMatchingServiceListeners(unregisteringEvent, listeners); d->module->coreCtx->listeners.ServiceChanged( listeners, unregisteringEvent); } { MutexLock lock(d->eventLock); { MutexLock lock2(d->propsLock); d->available = false; InterfaceMap::const_iterator factoryIter = d->service.find("org.cppmicroservices.factory"); if (d->module && factoryIter != d->service.end()) { ServiceFactory* serviceFactory = reinterpret_cast(factoryIter->second); ServiceRegistrationBasePrivate::ModuleToServicesMap::const_iterator end = d->prototypeServiceInstances.end(); // unget all prototype services for (ServiceRegistrationBasePrivate::ModuleToServicesMap::const_iterator i = d->prototypeServiceInstances.begin(); i != end; ++i) { for (std::list::const_iterator listIter = i->second.begin(); listIter != i->second.end(); ++listIter) { const InterfaceMap& service = *listIter; try { // NYI, don't call inside lock serviceFactory->UngetService(i->first, *this, service); } catch (const std::exception& /*ue*/) { US_WARN << "ServiceFactory UngetService implementation threw an exception"; } } } // unget module scope services ServiceRegistrationBasePrivate::ModuleToServiceMap::const_iterator moduleEnd = d->moduleServiceInstance.end(); for (ServiceRegistrationBasePrivate::ModuleToServiceMap::const_iterator i = d->moduleServiceInstance.begin(); i != moduleEnd; ++i) { try { // NYI, don't call inside lock serviceFactory->UngetService(i->first, *this, i->second); } catch (const std::exception& /*ue*/) { US_WARN << "ServiceFactory UngetService implementation threw an exception"; } } } - d->module = 0; + d->module = nullptr; d->dependents.clear(); d->service.clear(); d->prototypeServiceInstances.clear(); d->moduleServiceInstance.clear(); // increment the reference count, since "d->reference" was used originally // to keep d alive. d->ref.Ref(); d->reference = 0; d->unregistering = false; } } } bool ServiceRegistrationBase::operator<(const ServiceRegistrationBase& o) const { if ((!d && !o.d) || !o.d) return false; if (!d) return true; return d->reference <(o.d->reference); } bool ServiceRegistrationBase::operator==(const ServiceRegistrationBase& registration) const { return d == registration.d; } ServiceRegistrationBase& ServiceRegistrationBase::operator=(const ServiceRegistrationBase& registration) { ServiceRegistrationBasePrivate* curr_d = d; d = registration.d; if (d) d->ref.Ref(); if (curr_d && !curr_d->ref.Deref()) delete curr_d; return *this; } US_END_NAMESPACE diff --git a/Modules/CppMicroServices/core/src/service/usServiceRegistry.cpp b/Modules/CppMicroServices/core/src/service/usServiceRegistry.cpp index 11306e7045..9afcecdec7 100644 --- a/Modules/CppMicroServices/core/src/service/usServiceRegistry.cpp +++ b/Modules/CppMicroServices/core/src/service/usServiceRegistry.cpp @@ -1,338 +1,338 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include "usServiceRegistry_p.h" #include "usServiceFactory.h" #include "usPrototypeServiceFactory.h" #include "usServiceRegistry_p.h" #include "usServiceRegistrationBasePrivate.h" #include "usModulePrivate.h" #include "usCoreModuleContext_p.h" US_BEGIN_NAMESPACE ServicePropertiesImpl ServiceRegistry::CreateServiceProperties(const ServiceProperties& in, const std::vector& classes, bool isFactory, bool isPrototypeFactory, long sid) { static long nextServiceID = 1; ServiceProperties props(in); if (!classes.empty()) { props.insert(std::make_pair(ServiceConstants::OBJECTCLASS(), classes)); } props.insert(std::make_pair(ServiceConstants::SERVICE_ID(), sid != -1 ? sid : nextServiceID++)); if (isPrototypeFactory) { props.insert(std::make_pair(ServiceConstants::SERVICE_SCOPE(), ServiceConstants::SCOPE_PROTOTYPE())); } else if (isFactory) { props.insert(std::make_pair(ServiceConstants::SERVICE_SCOPE(), ServiceConstants::SCOPE_MODULE())); } else { props.insert(std::make_pair(ServiceConstants::SERVICE_SCOPE(), ServiceConstants::SCOPE_SINGLETON())); } return ServicePropertiesImpl(props); } ServiceRegistry::ServiceRegistry(CoreModuleContext* coreCtx) : core(coreCtx) { } ServiceRegistry::~ServiceRegistry() { Clear(); } void ServiceRegistry::Clear() { services.clear(); serviceRegistrations.clear(); classServices.clear(); - core = 0; + core = nullptr; } ServiceRegistrationBase ServiceRegistry::RegisterService(ModulePrivate* module, const InterfaceMap& service, const ServiceProperties& properties) { if (service.empty()) { throw std::invalid_argument("Can't register empty InterfaceMap as a service"); } // Check if we got a service factory bool isFactory = service.count("org.cppmicroservices.factory") > 0; bool isPrototypeFactory = (isFactory ? dynamic_cast(reinterpret_cast(service.find("org.cppmicroservices.factory")->second)) != nullptr : false); std::vector classes; // Check if service implements claimed classes and that they exist. for (InterfaceMap::const_iterator i = service.begin(); i != service.end(); ++i) { if (i->first.empty() || (!isFactory && i->second == nullptr)) { throw std::invalid_argument("Can't register as null class"); } classes.push_back(i->first); } ServiceRegistrationBase res(module, service, CreateServiceProperties(properties, classes, isFactory, isPrototypeFactory)); { MutexLock lock(mutex); services.insert(std::make_pair(res, classes)); serviceRegistrations.push_back(res); for (std::vector::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::vector& s = classServices[*i]; std::vector::iterator ip = std::lower_bound(s.begin(), s.end(), res); s.insert(ip, res); } } ServiceReferenceBase r = res.GetReference(std::string()); ServiceListeners::ServiceListenerEntries listeners; ServiceEvent registeredEvent(ServiceEvent::REGISTERED, r); module->coreCtx->listeners.GetMatchingServiceListeners(registeredEvent, listeners); module->coreCtx->listeners.ServiceChanged(listeners, registeredEvent); return res; } void ServiceRegistry::UpdateServiceRegistrationOrder(const ServiceRegistrationBase& sr, const std::vector& classes) { MutexLock lock(mutex); for (std::vector::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::vector& s = classServices[*i]; s.erase(std::remove(s.begin(), s.end(), sr), s.end()); s.insert(std::lower_bound(s.begin(), s.end(), sr), sr); } } void ServiceRegistry::Get(const std::string& clazz, std::vector& serviceRegs) const { MutexLock lock(mutex); Get_unlocked(clazz, serviceRegs); } void ServiceRegistry::Get_unlocked(const std::string& clazz, std::vector& serviceRegs) const { MapClassServices::const_iterator i = classServices.find(clazz); if (i != classServices.end()) { serviceRegs = i->second; } } ServiceReferenceBase ServiceRegistry::Get(ModulePrivate* module, const std::string& clazz) const { MutexLock lock(mutex); try { std::vector srs; Get_unlocked(clazz, "", module, srs); US_DEBUG << "get service ref " << clazz << " for module " << module->info.name << " = " << srs.size() << " refs"; if (!srs.empty()) { return srs.back(); } } catch (const std::invalid_argument& ) { } return ServiceReferenceBase(); } void ServiceRegistry::Get(const std::string& clazz, const std::string& filter, ModulePrivate* module, std::vector& res) const { MutexLock lock(mutex); Get_unlocked(clazz, filter, module, res); } void ServiceRegistry::Get_unlocked(const std::string& clazz, const std::string& filter, ModulePrivate* module, std::vector& res) const { std::vector::const_iterator s; std::vector::const_iterator send; std::vector v; LDAPExpr ldap; if (clazz.empty()) { if (!filter.empty()) { ldap = LDAPExpr(filter); LDAPExpr::ObjectClassSet matched; if (ldap.GetMatchedObjectClasses(matched)) { v.clear(); for(LDAPExpr::ObjectClassSet::const_iterator className = matched.begin(); className != matched.end(); ++className) { MapClassServices::const_iterator i = classServices.find(*className); if (i != classServices.end()) { std::copy(i->second.begin(), i->second.end(), std::back_inserter(v)); } } if (!v.empty()) { s = v.begin(); send = v.end(); } else { return; } } else { s = serviceRegistrations.begin(); send = serviceRegistrations.end(); } } else { s = serviceRegistrations.begin(); send = serviceRegistrations.end(); } } else { MapClassServices::const_iterator it = classServices.find(clazz); if (it != classServices.end()) { s = it->second.begin(); send = it->second.end(); } else { return; } if (!filter.empty()) { ldap = LDAPExpr(filter); } } for (; s != send; ++s) { ServiceReferenceBase sri = s->GetReference(clazz); if (filter.empty() || ldap.Evaluate(s->d->properties, false)) { res.push_back(sri); } } if (!res.empty()) { if (module != nullptr) { core->serviceHooks.FilterServiceReferences(module->moduleContext, clazz, filter, res); } else { core->serviceHooks.FilterServiceReferences(nullptr, clazz, filter, res); } } } void ServiceRegistry::RemoveServiceRegistration(const ServiceRegistrationBase& sr) { MutexLock lock(mutex); assert(sr.d->properties.Value(ServiceConstants::OBJECTCLASS()).Type() == typeid(std::vector)); const std::vector& classes = ref_any_cast >( sr.d->properties.Value(ServiceConstants::OBJECTCLASS())); services.erase(sr); serviceRegistrations.erase(std::remove(serviceRegistrations.begin(), serviceRegistrations.end(), sr), serviceRegistrations.end()); for (std::vector::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::vector& s = classServices[*i]; if (s.size() > 1) { s.erase(std::remove(s.begin(), s.end(), sr), s.end()); } else { classServices.erase(*i); } } } void ServiceRegistry::GetRegisteredByModule(ModulePrivate* p, std::vector& res) const { MutexLock lock(mutex); for (std::vector::const_iterator i = serviceRegistrations.begin(); i != serviceRegistrations.end(); ++i) { if (i->d->module == p) { res.push_back(*i); } } } void ServiceRegistry::GetUsedByModule(Module* p, std::vector& res) const { MutexLock lock(mutex); for (std::vector::const_iterator i = serviceRegistrations.begin(); i != serviceRegistrations.end(); ++i) { if (i->d->IsUsedByModule(p)) { res.push_back(*i); } } } US_END_NAMESPACE diff --git a/Modules/CppMicroServices/core/src/util/usSharedLibrary.cpp b/Modules/CppMicroServices/core/src/util/usSharedLibrary.cpp index da0e89ae31..3c4abcdd52 100644 --- a/Modules/CppMicroServices/core/src/util/usSharedLibrary.cpp +++ b/Modules/CppMicroServices/core/src/util/usSharedLibrary.cpp @@ -1,273 +1,273 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usSharedLibrary.h" #include #include #include #include #if defined(US_PLATFORM_POSIX) #include #elif defined(US_PLATFORM_WINDOWS) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #else #error Unsupported platform #endif US_BEGIN_NAMESPACE #ifdef US_PLATFORM_POSIX static const char PATH_SEPARATOR = '/'; #else static const char PATH_SEPARATOR = '\\'; #endif class SharedLibraryPrivate : public SharedData { public: SharedLibraryPrivate() : m_Handle(nullptr) #ifdef US_PLATFORM_WINDOWS , m_Suffix(".dll") #elif defined(US_PLATFORM_APPLE) , m_Suffix(".dylib") , m_Prefix("lib") #else , m_Suffix(".so") , m_Prefix("lib") #endif {} void* m_Handle; std::string m_Name; std::string m_Path; std::string m_FilePath; std::string m_Suffix; std::string m_Prefix; }; SharedLibrary::SharedLibrary() : d(new SharedLibraryPrivate) { } SharedLibrary::SharedLibrary(const SharedLibrary& other) : d(other.d) { } SharedLibrary::SharedLibrary(const std::string& libPath, const std::string& name) : d(new SharedLibraryPrivate) { d->m_Name = name; d->m_Path = libPath; } SharedLibrary::SharedLibrary(const std::string& absoluteFilePath) : d(new SharedLibraryPrivate) { d->m_FilePath = absoluteFilePath; SetFilePath(absoluteFilePath); } SharedLibrary::~SharedLibrary() { } SharedLibrary& SharedLibrary::operator =(const SharedLibrary& other) { d = other.d; return *this; } void SharedLibrary::Load(int flags) { if (d->m_Handle) throw std::logic_error(std::string("Library already loaded: ") + GetFilePath()); std::string libPath = GetFilePath(); #ifdef US_PLATFORM_POSIX d->m_Handle = dlopen(libPath.c_str(), flags); if (!d->m_Handle) { const char* err = dlerror(); throw std::runtime_error(err ? std::string(err) : (std::string("Error loading ") + libPath)); } #else d->m_Handle = LoadLibrary(libPath.c_str()); if (!d->m_Handle) { std::string errMsg = "Loading "; errMsg.append(libPath).append("failed with error: ").append(GetLastErrorStr()); throw std::runtime_error(errMsg); } #endif } void SharedLibrary::Load() { #ifdef US_PLATFORM_POSIX #ifdef US_GCC_RTTI_WORKAROUND_NEEDED Load(RTLD_LAZY | RTLD_GLOBAL); #else Load(RTLD_LAZY | RTLD_LOCAL); #endif #else Load(0); #endif } void SharedLibrary::Unload() { if (d->m_Handle) { #ifdef US_PLATFORM_POSIX if (dlclose(d->m_Handle)) { const char* err = dlerror(); throw std::runtime_error(err ? std::string(err) : (std::string("Error unloading ") + GetLibraryPath())); } #else if (!FreeLibrary((HMODULE)d->m_Handle)) { std::string errMsg = "Unloading "; errMsg.append(GetLibraryPath()).append("failed with error: ").append(GetLastErrorStr()); throw std::runtime_error(errMsg); } #endif - d->m_Handle = 0; + d->m_Handle = nullptr; } } void SharedLibrary::SetName(const std::string& name) { if (IsLoaded() || !d->m_FilePath.empty()) return; d.Detach(); d->m_Name = name; } std::string SharedLibrary::GetName() const { return d->m_Name; } std::string SharedLibrary::GetFilePath(const std::string& name) const { if (!d->m_FilePath.empty()) return d->m_FilePath; return GetLibraryPath() + PATH_SEPARATOR + GetPrefix() + name + GetSuffix(); } void SharedLibrary::SetFilePath(const std::string& absoluteFilePath) { if (IsLoaded()) return; d.Detach(); d->m_FilePath = absoluteFilePath; std::string name = d->m_FilePath; std::size_t pos = d->m_FilePath.find_last_of(PATH_SEPARATOR); if (pos != std::string::npos) { d->m_Path = d->m_FilePath.substr(0, pos); name = d->m_FilePath.substr(pos+1); } else { d->m_Path.clear(); } if (name.size() >= d->m_Prefix.size() && name.compare(0, d->m_Prefix.size(), d->m_Prefix) == 0) { name = name.substr(d->m_Prefix.size()); } if (name.size() >= d->m_Suffix.size() && name.compare(name.size()-d->m_Suffix.size(), d->m_Suffix.size(), d->m_Suffix) == 0) { name = name.substr(0, name.size()-d->m_Suffix.size()); } d->m_Name = name; } std::string SharedLibrary::GetFilePath() const { return GetFilePath(d->m_Name); } void SharedLibrary::SetLibraryPath(const std::string& path) { if (IsLoaded() || !d->m_FilePath.empty()) return; d.Detach(); d->m_Path = path; } std::string SharedLibrary::GetLibraryPath() const { return d->m_Path; } void SharedLibrary::SetSuffix(const std::string& suffix) { if (IsLoaded() || !d->m_FilePath.empty()) return; d.Detach(); d->m_Suffix = suffix; } std::string SharedLibrary::GetSuffix() const { return d->m_Suffix; } void SharedLibrary::SetPrefix(const std::string& prefix) { if (IsLoaded() || !d->m_FilePath.empty()) return; d.Detach(); d->m_Prefix = prefix; } std::string SharedLibrary::GetPrefix() const { return d->m_Prefix; } void* SharedLibrary::GetHandle() const { return d->m_Handle; } bool SharedLibrary::IsLoaded() const { return d->m_Handle != nullptr; } US_END_NAMESPACE diff --git a/Modules/CppMicroServices/core/src/util/usUtils.cpp b/Modules/CppMicroServices/core/src/util/usUtils.cpp index 13e80bd0f0..a93b5ab93d 100644 --- a/Modules/CppMicroServices/core/src/util/usUtils.cpp +++ b/Modules/CppMicroServices/core/src/util/usUtils.cpp @@ -1,350 +1,350 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usUtils_p.h" #include "usLog_p.h" #include "usModuleInfo.h" #include "usModuleSettings.h" #include #include #include #include #include #ifdef US_PLATFORM_POSIX #include #include #include #include #else #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include "dirent_win32.h" #endif //------------------------------------------------------------------- // Module auto-loading //------------------------------------------------------------------- namespace { #if !defined(US_PLATFORM_LINUX) std::string library_suffix() { #ifdef US_PLATFORM_WINDOWS return ".dll"; #elif defined(US_PLATFORM_APPLE) return ".dylib"; #else return ".so"; #endif } #endif #ifdef US_PLATFORM_POSIX const char DIR_SEP = '/'; bool load_impl(const std::string& modulePath) { void* handle = dlopen(modulePath.c_str(), RTLD_NOW | RTLD_LOCAL); if (handle == nullptr) { US_WARN << dlerror(); } return (handle != nullptr); } #elif defined(US_PLATFORM_WINDOWS) const char DIR_SEP = '\\'; bool load_impl(const std::string& modulePath) { void* handle = LoadLibrary(modulePath.c_str()); if (handle == nullptr) { US_WARN << us::GetLastErrorStr(); } return (handle != nullptr); } #else #ifdef US_ENABLE_AUTOLOADING_SUPPORT #error "Missing load_impl implementation for this platform." #else bool load_impl(const std::string&) { return false; } #endif #endif } US_BEGIN_NAMESPACE std::vector AutoLoadModulesFromPath(const std::string& absoluteBasePath, const std::string& subDir) { std::vector loadedModules; std::string loadPath = absoluteBasePath + DIR_SEP + subDir; DIR* dir = opendir(loadPath.c_str()); #ifdef CMAKE_INTDIR // Try intermediate output directories if (dir == nullptr) { std::size_t indexOfLastSeparator = absoluteBasePath.find_last_of(DIR_SEP); if (indexOfLastSeparator != std::string::npos) { std::string intermediateDir = absoluteBasePath.substr(indexOfLastSeparator+1); bool equalSubDir = intermediateDir.size() == std::strlen(CMAKE_INTDIR); for (std::size_t i = 0; equalSubDir && i < intermediateDir.size(); ++i) { if (std::tolower(intermediateDir[i]) != std::tolower(CMAKE_INTDIR[i])) { equalSubDir = false; } } if (equalSubDir) { loadPath = absoluteBasePath.substr(0, indexOfLastSeparator+1) + subDir + DIR_SEP + CMAKE_INTDIR; dir = opendir(loadPath.c_str()); } } } #endif if (dir != nullptr) { struct dirent *ent = nullptr; while ((ent = readdir(dir)) != nullptr) { bool loadFile = true; #ifdef _DIRENT_HAVE_D_TYPE if (ent->d_type != DT_UNKNOWN && ent->d_type != DT_REG) { loadFile = false; } #endif std::string entryFileName(ent->d_name); // On Linux, library file names can have version numbers appended. On other platforms, we // check the file ending. This could be refined for Linux in the future. #if !defined(US_PLATFORM_LINUX) if (entryFileName.rfind(library_suffix()) != (entryFileName.size() - library_suffix().size())) { loadFile = false; } #endif if (!loadFile) continue; std::string libPath = loadPath; if (!libPath.empty() && libPath.find_last_of(DIR_SEP) != libPath.size() -1) { libPath += DIR_SEP; } libPath += entryFileName; US_DEBUG << "Auto-loading module " << libPath; if (!load_impl(libPath)) { US_WARN << "Auto-loading of module " << libPath << " failed."; } else { loadedModules.push_back(libPath); } } closedir(dir); } return loadedModules; } std::vector AutoLoadModules(const ModuleInfo& moduleInfo) { std::vector loadedModules; if (moduleInfo.autoLoadDir.empty()) { return loadedModules; } ModuleSettings::PathList autoLoadPaths = ModuleSettings::GetAutoLoadPaths(); std::size_t indexOfLastSeparator = moduleInfo.location.find_last_of(DIR_SEP); std::string moduleBasePath = moduleInfo.location.substr(0, indexOfLastSeparator); for (ModuleSettings::PathList::iterator i = autoLoadPaths.begin(); i != autoLoadPaths.end(); ++i) { if (*i == ModuleSettings::CURRENT_MODULE_PATH()) { // Load all modules from a directory located relative to this modules location // and named after this modules library name. *i = moduleBasePath; } } // We could have introduced a duplicate above, so remove it. std::sort(autoLoadPaths.begin(), autoLoadPaths.end()); autoLoadPaths.erase(std::unique(autoLoadPaths.begin(), autoLoadPaths.end()), autoLoadPaths.end()); for (ModuleSettings::PathList::iterator i = autoLoadPaths.begin(); i != autoLoadPaths.end(); ++i) { if (i->empty()) continue; std::vector paths = AutoLoadModulesFromPath(*i, moduleInfo.autoLoadDir); loadedModules.insert(loadedModules.end(), paths.begin(), paths.end()); } return loadedModules; } US_END_NAMESPACE //------------------------------------------------------------------- // Error handling //------------------------------------------------------------------- US_BEGIN_NAMESPACE std::string GetLastErrorStr() { #ifdef US_PLATFORM_POSIX return std::string(strerror(errno)); #else // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, nullptr ); std::string errMsg((LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); return errMsg; #endif } -static MsgHandler handler = 0; +static MsgHandler handler = nullptr; MsgHandler installMsgHandler(MsgHandler h) { MsgHandler old = handler; handler = h; return old; } void message_output(MsgType msgType, const char *buf) { if (handler) { (*handler)(msgType, buf); } else { fprintf(stderr, "%s\n", buf); fflush(stderr); } if (msgType == ErrorMsg) { #if defined(_MSC_VER) && !defined(NDEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) // get the current report mode int reportMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW); _CrtSetReportMode(_CRT_ERROR, reportMode); int ret = _CrtDbgReport(_CRT_ERROR, __FILE__, __LINE__, CppMicroServices_VERSION_STR, buf); if (ret == 0 && reportMode & _CRTDBG_MODE_WNDW) return; // ignore else if (ret == 1) _CrtDbgBreak(); #endif #ifdef US_PLATFORM_POSIX abort(); // trap; generates core dump #else exit(1); // goodbye cruel world #endif } } #ifdef US_HAVE_CXXABI_H #include #endif US_Core_EXPORT ::std::string GetDemangledName(const ::std::type_info& typeInfo) { ::std::string result; #ifdef US_HAVE_CXXABI_H int status = 0; - char* demangled = abi::__cxa_demangle(typeInfo.name(), 0, 0, &status); + char* demangled = abi::__cxa_demangle(typeInfo.name(), nullptr, nullptr, &status); if (demangled && status == 0) { result = demangled; free(demangled); } #elif defined(US_PLATFORM_WINDOWS) const char* demangled = typeInfo.name(); if (demangled != nullptr) { result = demangled; // remove "struct" qualifiers std::size_t pos = 0; while (pos != std::string::npos) { if ((pos = result.find("struct ", pos)) != std::string::npos) { result = result.substr(0, pos) + result.substr(pos + 7); pos += 8; } } // remove "class" qualifiers pos = 0; while (pos != std::string::npos) { if ((pos = result.find("class ", pos)) != std::string::npos) { result = result.substr(0, pos) + result.substr(pos + 6); pos += 7; } } } #else (void)typeInfo; #endif return result; } US_END_NAMESPACE diff --git a/Modules/CppMicroServices/core/test/modules/libA/usTestModuleA.cpp b/Modules/CppMicroServices/core/test/modules/libA/usTestModuleA.cpp index 9f6210573c..fd8cec3512 100644 --- a/Modules/CppMicroServices/core/test/modules/libA/usTestModuleA.cpp +++ b/Modules/CppMicroServices/core/test/modules/libA/usTestModuleA.cpp @@ -1,77 +1,77 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usTestModuleAService.h" #include #include #include US_BEGIN_NAMESPACE struct TestModuleA : public TestModuleAService { TestModuleA(ModuleContext* mc) { US_INFO << "Registering TestModuleAService"; sr = mc->RegisterService(this); } void Unregister() { if (sr) { sr.Unregister(); sr = 0; } } private: ServiceRegistration sr; }; class TestModuleAActivator : public ModuleActivator { public: - TestModuleAActivator() : s(0) {} + TestModuleAActivator() : s(nullptr) {} ~TestModuleAActivator() override { delete s; } void Load(ModuleContext* context) override { s = new TestModuleA(context); } void Unload(ModuleContext*) override { } private: TestModuleA* s; }; US_END_NAMESPACE US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(TestModuleAActivator)) diff --git a/Modules/CppMicroServices/core/test/modules/libA2/usTestModuleA2.cpp b/Modules/CppMicroServices/core/test/modules/libA2/usTestModuleA2.cpp index 405db6024a..cda719e8d2 100644 --- a/Modules/CppMicroServices/core/test/modules/libA2/usTestModuleA2.cpp +++ b/Modules/CppMicroServices/core/test/modules/libA2/usTestModuleA2.cpp @@ -1,76 +1,76 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usTestModuleA2Service.h" #include #include US_BEGIN_NAMESPACE struct TestModuleA2 : public TestModuleA2Service { TestModuleA2(ModuleContext* mc) { US_INFO << "Registering TestModuleA2Service"; sr = mc->RegisterService(this); } void Unregister() { if (sr) { sr.Unregister(); } } private: ServiceRegistration sr; }; class TestModuleA2Activator : public ModuleActivator { public: - TestModuleA2Activator() : s(0) {} + TestModuleA2Activator() : s(nullptr) {} ~TestModuleA2Activator() override { delete s; } void Load(ModuleContext* context) override { s = new TestModuleA2(context); } void Unload(ModuleContext* /*context*/) override { s->Unregister(); } private: TestModuleA2* s; }; US_END_NAMESPACE US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(TestModuleA2Activator)) diff --git a/Modules/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleB.cpp b/Modules/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleB.cpp index 9ed3fddf50..8728304f9b 100644 --- a/Modules/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleB.cpp +++ b/Modules/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleB.cpp @@ -1,66 +1,66 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usTestModuleBService.h" #include #include #include US_BEGIN_NAMESPACE struct TestModuleB : public TestModuleBService { TestModuleB(ModuleContext* mc) { US_INFO << "Registering TestModuleBService"; mc->RegisterService(this); } }; class TestModuleBActivator : public ModuleActivator { public: - TestModuleBActivator() : s(0) {} + TestModuleBActivator() : s(nullptr) {} ~TestModuleBActivator() override { delete s; } void Load(ModuleContext* context) override { s = new TestModuleB(context); } void Unload(ModuleContext*) override { } private: TestModuleB* s; }; US_END_NAMESPACE US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(TestModuleBActivator)) US_IMPORT_MODULE(TestModuleImportedByB) diff --git a/Modules/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleImportedByB.cpp b/Modules/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleImportedByB.cpp index 5e003d537a..a8369d09c2 100644 --- a/Modules/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleImportedByB.cpp +++ b/Modules/CppMicroServices/core/test/modules/libBWithStatic/usTestModuleImportedByB.cpp @@ -1,64 +1,64 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usTestModuleBService.h" #include #include US_BEGIN_NAMESPACE struct TestModuleImportedByB : public TestModuleBService { TestModuleImportedByB(ModuleContext* mc) { US_INFO << "Registering TestModuleImportedByB"; mc->RegisterService(this); } }; class TestModuleImportedByBActivator : public ModuleActivator { public: - TestModuleImportedByBActivator() : s(0) {} + TestModuleImportedByBActivator() : s(nullptr) {} ~TestModuleImportedByBActivator() override { delete s; } void Load(ModuleContext* context) override { s = new TestModuleImportedByB(context); } void Unload(ModuleContext*) override { } private: TestModuleImportedByB* s; }; US_END_NAMESPACE US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(TestModuleImportedByBActivator)) diff --git a/Modules/CppMicroServices/core/test/modules/libS/usTestModuleS.cpp b/Modules/CppMicroServices/core/test/modules/libS/usTestModuleS.cpp index 98972f40c9..46e52c4818 100644 --- a/Modules/CppMicroServices/core/test/modules/libS/usTestModuleS.cpp +++ b/Modules/CppMicroServices/core/test/modules/libS/usTestModuleS.cpp @@ -1,142 +1,142 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "../../usServiceControlInterface.h" #include "usTestModuleSService0.h" #include "usTestModuleSService1.h" #include "usTestModuleSService2.h" #include "usTestModuleSService3.h" #include #include #include US_BEGIN_NAMESPACE class TestModuleS : public ServiceControlInterface, public TestModuleSService0, public TestModuleSService1, public TestModuleSService2, public TestModuleSService3 { public: TestModuleS(ModuleContext* mc) : mc(mc) { for(int i = 0; i <= 3; ++i) { servregs.push_back(ServiceRegistrationU()); } sreg = mc->RegisterService(this); sciReg = mc->RegisterService(this); } virtual const char* GetNameOfClass() const { return "TestModuleS"; } void ServiceControl(int offset, const std::string& operation, int ranking) override { if (0 <= offset && offset <= 3) { if (operation == "register") { if (!servregs[offset]) { std::stringstream servicename; servicename << SERVICE << offset; InterfaceMap ifm; ifm.insert(std::make_pair(servicename.str(), static_cast(this))); ServiceProperties props; props.insert(std::make_pair(ServiceConstants::SERVICE_RANKING(), Any(ranking))); servregs[offset] = mc->RegisterService(ifm, props); } } if (operation == "unregister") { if (servregs[offset]) { ServiceRegistrationU sr1 = servregs[offset]; sr1.Unregister(); servregs[offset] = 0; } } } } void Unregister() { if (sreg) { sreg.Unregister(); } if (sciReg) { sciReg.Unregister(); } } private: static const std::string SERVICE; // = "us::TestModuleSService" ModuleContext* mc; std::vector servregs; ServiceRegistration sreg; ServiceRegistration sciReg; }; const std::string TestModuleS::SERVICE = "us::TestModuleSService"; class TestModuleSActivator : public ModuleActivator { public: - TestModuleSActivator() : s(0) {} + TestModuleSActivator() : s(nullptr) {} ~TestModuleSActivator() override { delete s; } void Load(ModuleContext* context) override { s = new TestModuleS(context); } void Unload(ModuleContext* /*context*/) override { #ifndef US_BUILD_SHARED_LIBS s->Unregister(); #endif } private: TestModuleS* s; }; US_END_NAMESPACE US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(TestModuleSActivator)) diff --git a/Modules/CppMicroServices/core/test/modules/libSL1/usActivatorSL1.cpp b/Modules/CppMicroServices/core/test/modules/libSL1/usActivatorSL1.cpp index 4bb29185b0..3837d932e1 100644 --- a/Modules/CppMicroServices/core/test/modules/libSL1/usActivatorSL1.cpp +++ b/Modules/CppMicroServices/core/test/modules/libSL1/usActivatorSL1.cpp @@ -1,106 +1,106 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include "usFooService.h" US_BEGIN_NAMESPACE class ActivatorSL1 : public ModuleActivator, public ModulePropsInterface, public ServiceTrackerCustomizer { public: ActivatorSL1() - : tracker(0), context(0) + : tracker(nullptr), context(nullptr) { } ~ActivatorSL1() override { delete tracker; } void Load(ModuleContext* context) override { this->context = context; InterfaceMap im = MakeInterfaceMap(this); im.insert(std::make_pair(std::string("ActivatorSL1"), this)); sr = context->RegisterService(im); delete tracker; tracker = new FooTracker(context, this); tracker->Open(); } void Unload(ModuleContext* /*context*/) override { tracker->Close(); } const Properties& GetProperties() const override { return props; } FooService* AddingService(const ServiceReferenceType& reference) override { props["serviceAdded"] = true; FooService* fooService = context->GetService(reference); fooService->foo(); return fooService; } void ModifiedService(const ServiceReferenceType& /*reference*/, FooService* /*service*/) override {} void RemovedService(const ServiceReferenceType& /*reference*/, FooService* /*service*/) override { props["serviceRemoved"] = true; } private: ModulePropsInterface::Properties props; ServiceRegistrationU sr; typedef ServiceTracker FooTracker; FooTracker* tracker; ModuleContext* context; }; // ActivatorSL1 US_END_NAMESPACE US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(ActivatorSL1)) diff --git a/Modules/CppMicroServices/core/test/modules/libSL3/usActivatorSL3.cpp b/Modules/CppMicroServices/core/test/modules/libSL3/usActivatorSL3.cpp index 95212637f2..5ee4fb2c35 100644 --- a/Modules/CppMicroServices/core/test/modules/libSL3/usActivatorSL3.cpp +++ b/Modules/CppMicroServices/core/test/modules/libSL3/usActivatorSL3.cpp @@ -1,99 +1,99 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include US_BEGIN_NAMESPACE class ActivatorSL3 : public ModuleActivator, public ModulePropsInterface, public ServiceTrackerCustomizer { public: - ActivatorSL3() : tracker(0), context(0) {} + ActivatorSL3() : tracker(nullptr), context(nullptr) {} ~ActivatorSL3() override { delete tracker; } void Load(ModuleContext* context) override { this->context = context; InterfaceMap im = MakeInterfaceMap(this); im.insert(std::make_pair(std::string("ActivatorSL3"), this)); sr = context->RegisterService(im); delete tracker; tracker = new FooTracker(context, this); tracker->Open(); } void Unload(ModuleContext* /*context*/) override { tracker->Close(); } const ModulePropsInterface::Properties& GetProperties() const override { return props; } FooService* AddingService(const ServiceReferenceType& reference) override { props["serviceAdded"] = true; US_INFO << "SL3: Adding reference =" << reference; FooService* fooService = context->GetService(reference); fooService->foo(); return fooService; } void ModifiedService(const ServiceReferenceType& /*reference*/, FooService* /*service*/) override { } void RemovedService(const ServiceReferenceType& reference, FooService* /*service*/) override { props["serviceRemoved"] = true; US_INFO << "SL3: Removing reference =" << reference; } private: typedef ServiceTracker FooTracker; FooTracker* tracker; ModuleContext* context; ServiceRegistrationU sr; ModulePropsInterface::Properties props; }; US_END_NAMESPACE US_EXPORT_MODULE_ACTIVATOR(US_PREPEND_NAMESPACE(ActivatorSL3)) diff --git a/Modules/CppMicroServices/core/test/usLogTest.cpp b/Modules/CppMicroServices/core/test/usLogTest.cpp index 13839cdf45..c812a0d724 100644 --- a/Modules/CppMicroServices/core/test/usLogTest.cpp +++ b/Modules/CppMicroServices/core/test/usLogTest.cpp @@ -1,177 +1,177 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include "usTestingMacros.h" US_USE_NAMESPACE static int lastMsgType = -1; static std::string lastMsg; void handleMessages(MsgType type, const char* msg) { lastMsgType = type; lastMsg.assign(msg); } void resetLastMsg() { lastMsgType = -1; lastMsg.clear(); } void logMsg(MsgType msgType, int logLevel) { resetLastMsg(); std::string logMsg; switch (msgType) { case DebugMsg: { logMsg = "Debug msg"; US_DEBUG << logMsg; #if !defined(US_ENABLE_DEBUG_OUTPUT) if (logLevel == DebugMsg) logLevel = InfoMsg; #endif break; } case InfoMsg: { logMsg = "Info msg"; US_INFO << logMsg; break; } case WarningMsg: { logMsg = "Warning msg"; US_WARN << logMsg; break; } case ErrorMsg: { // Skip error messages logLevel = 100; break; } } if (msgType >= logLevel) { US_TEST_CONDITION(lastMsgType == msgType && lastMsg.find(logMsg) != std::string::npos, "Testing for logged message") } else { US_TEST_CONDITION(lastMsgType == -1 && lastMsg.empty(), "Testing for skipped log message") } } void testLogMessages() { // Use the default message handler - installMsgHandler(0); + installMsgHandler(nullptr); { US_DEBUG << "Msg"; US_DEBUG(false) << "Msg"; US_INFO << "Msg"; US_INFO(false) << "Msg"; US_WARN << "Msg"; US_WARN(false) << "Msg"; } US_TEST_CONDITION(lastMsg.empty(), "Testing default message handler"); resetLastMsg(); installMsgHandler(handleMessages); { US_DEBUG << "Msg"; } #if !defined(US_ENABLE_DEBUG_OUTPUT) US_TEST_CONDITION(lastMsgType == -1 && lastMsg.empty(), "Testing suppressed debug message") #else US_TEST_CONDITION(lastMsgType == 0 && lastMsg.find("Msg") != std::string::npos, "Testing debug message") #endif resetLastMsg(); { US_DEBUG(false) << "No msg"; } US_TEST_CONDITION(lastMsgType == -1 && lastMsg.empty(), "Testing disabled debug message") resetLastMsg(); { US_INFO << "Info msg"; } US_TEST_CONDITION(lastMsgType == 1 && lastMsg.find("Info msg") != std::string::npos, "Testing informational message") resetLastMsg(); { US_WARN << "Warn msg"; } US_TEST_CONDITION(lastMsgType == 2 && lastMsg.find("Warn msg") != std::string::npos, "Testing warning message") resetLastMsg(); // We cannot test US_ERROR since it will call abort(). - installMsgHandler(0); + installMsgHandler(nullptr); { US_INFO << "Info msg"; } US_TEST_CONDITION(lastMsgType == -1 && lastMsg.empty(), "Testing message handler reset") resetLastMsg(); } void testLogLevels() { installMsgHandler(handleMessages); MsgType logLevel = ModuleSettings::GetLogLevel(); US_TEST_CONDITION_REQUIRED(logLevel == DebugMsg, "Default log level") logMsg(DebugMsg, logLevel); logMsg(InfoMsg, logLevel); logMsg(WarningMsg, logLevel); logMsg(ErrorMsg, logLevel); for (int level = ErrorMsg; level >= 0; --level) { ModuleSettings::SetLogLevel(static_cast(level)); logMsg(DebugMsg, level); logMsg(InfoMsg, level); logMsg(WarningMsg, level); logMsg(ErrorMsg, level); } - installMsgHandler(0); + installMsgHandler(nullptr); resetLastMsg(); } int usLogTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("DebugOutputTest"); testLogMessages(); testLogLevels(); US_TEST_END() } diff --git a/Modules/CppMicroServices/core/test/usModuleHooksTest.cpp b/Modules/CppMicroServices/core/test/usModuleHooksTest.cpp index 69ccaaa54a..3ea644f605 100644 --- a/Modules/CppMicroServices/core/test/usModuleHooksTest.cpp +++ b/Modules/CppMicroServices/core/test/usModuleHooksTest.cpp @@ -1,194 +1,194 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include #include "usTestingMacros.h" #include "usTestingConfig.h" US_USE_NAMESPACE namespace { #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif class TestModuleListener { public: void ModuleChanged(const ModuleEvent moduleEvent) { this->events.push_back(moduleEvent); } std::vector events; }; class TestModuleFindHook : public ModuleFindHook { public: void Find(const ModuleContext* /*context*/, ShrinkableVector& modules) override { for (ShrinkableVector::iterator i = modules.begin(); i != modules.end();) { if ((*i)->GetName() == "TestModuleA") { i = modules.erase(i); } else { ++i; } } } }; class TestModuleEventHook : public ModuleEventHook { public: void Event(const ModuleEvent& event, ShrinkableVector& contexts) override { if (event.GetType() == ModuleEvent::LOADING || event.GetType() == ModuleEvent::UNLOADING) { contexts.erase(std::remove(contexts.begin(), contexts.end(), GetModuleContext()), contexts.end()); } } }; void TestFindHook() { SharedLibrary libA(LIB_PATH, "TestModuleA"); #ifdef US_BUILD_SHARED_LIBS try { libA.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } #endif Module* moduleA = GetModuleContext()->GetModule("TestModuleA"); - US_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for existing module TestModuleA") + US_TEST_CONDITION_REQUIRED(moduleA != nullptr, "Test for existing module TestModuleA") US_TEST_CONDITION(moduleA->GetName() == "TestModuleA", "Test module name") US_TEST_CONDITION(moduleA->IsLoaded() == true, "Test if loaded correctly"); long moduleAId = moduleA->GetModuleId(); US_TEST_CONDITION_REQUIRED(moduleAId > 0, "Test for valid module id") US_TEST_CONDITION_REQUIRED(GetModuleContext()->GetModule(moduleAId) != nullptr, "Test for non-filtered GetModule(long) result") TestModuleFindHook findHook; ServiceRegistration findHookReg = GetModuleContext()->RegisterService(&findHook); US_TEST_CONDITION_REQUIRED(GetModuleContext()->GetModule(moduleAId) == nullptr, "Test for filtered GetModule(long) result") std::vector modules = GetModuleContext()->GetModules(); for (std::vector::iterator i = modules.begin(); i != modules.end(); ++i) { if((*i)->GetName() == "TestModuleA") { US_TEST_FAILED_MSG(<< "TestModuleA not filtered from GetModules()") } } findHookReg.Unregister(); libA.Unload(); } #ifdef US_BUILD_SHARED_LIBS void TestEventHook() { TestModuleListener moduleListener; GetModuleContext()->AddModuleListener(&moduleListener, &TestModuleListener::ModuleChanged); SharedLibrary libA(LIB_PATH, "TestModuleA"); try { libA.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } US_TEST_CONDITION_REQUIRED(moduleListener.events.size() == 2, "Test for received load module events") libA.Unload(); US_TEST_CONDITION_REQUIRED(moduleListener.events.size() == 4, "Test for received unload module events") TestModuleEventHook eventHook; ServiceRegistration eventHookReg = GetModuleContext()->RegisterService(&eventHook); moduleListener.events.clear(); try { libA.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } US_TEST_CONDITION_REQUIRED(moduleListener.events.size() == 1, "Test for filtered load module events") US_TEST_CONDITION_REQUIRED(moduleListener.events[0].GetType() == ModuleEvent::LOADED, "Test for LOADED event") libA.Unload(); US_TEST_CONDITION_REQUIRED(moduleListener.events.size() == 2, "Test for filtered unload module events") US_TEST_CONDITION_REQUIRED(moduleListener.events[1].GetType() == ModuleEvent::UNLOADED, "Test for UNLOADED event") eventHookReg.Unregister(); GetModuleContext()->RemoveModuleListener(&moduleListener, &TestModuleListener::ModuleChanged); } #endif } // end unnamed namespace int usModuleHooksTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ModuleHooksTest"); TestFindHook(); #ifdef US_BUILD_SHARED_LIBS TestEventHook(); #endif US_TEST_END() } diff --git a/Modules/CppMicroServices/core/test/usModuleManifestTest.cpp b/Modules/CppMicroServices/core/test/usModuleManifestTest.cpp index 62f7eb0388..7273d9d9ff 100644 --- a/Modules/CppMicroServices/core/test/usModuleManifestTest.cpp +++ b/Modules/CppMicroServices/core/test/usModuleManifestTest.cpp @@ -1,96 +1,96 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include #include "usTestingMacros.h" #include "usTestingConfig.h" US_USE_NAMESPACE namespace { #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif } // end unnamed namespace int usModuleManifestTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ModuleManifestTest"); SharedLibrary target(LIB_PATH, "TestModuleM"); #ifdef US_BUILD_SHARED_LIBS try { target.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what() << " + in frameSL02a:FAIL" ); } #endif Module* moduleM = ModuleRegistry::GetModule("TestModuleM"); - US_TEST_CONDITION_REQUIRED(moduleM != 0, "Test for existing module TestModuleM") + US_TEST_CONDITION_REQUIRED(moduleM != nullptr, "Test for existing module TestModuleM") US_TEST_CONDITION(moduleM->GetProperty(Module::PROP_NAME()).ToString() == "TestModuleM", "Module name") US_TEST_CONDITION(moduleM->GetName() == "TestModuleM", "Module name 2") US_TEST_CONDITION(moduleM->GetProperty(Module::PROP_DESCRIPTION()).ToString() == "My Module description", "Module description") US_TEST_CONDITION(moduleM->GetLocation() == moduleM->GetProperty(Module::PROP_LOCATION()).ToString(), "Module location") US_TEST_CONDITION(moduleM->GetProperty(Module::PROP_VERSION()).ToString() == "1.0.0", "Module version") US_TEST_CONDITION(moduleM->GetVersion() == ModuleVersion(1,0,0), "Module version 2") Any anyVector = moduleM->GetProperty("vector"); US_TEST_CONDITION_REQUIRED(anyVector.Type() == typeid(std::vector), "vector type") std::vector& vec = ref_any_cast >(anyVector); US_TEST_CONDITION_REQUIRED(vec.size() == 3, "vector size") US_TEST_CONDITION_REQUIRED(vec[0].Type() == typeid(std::string), "vector 0 type") US_TEST_CONDITION_REQUIRED(vec[0].ToString() == "first", "vector 0 value") US_TEST_CONDITION_REQUIRED(vec[1].Type() == typeid(int), "vector 1 type") US_TEST_CONDITION_REQUIRED(any_cast(vec[1]) == 2, "vector 1 value") Any anyMap = moduleM->GetProperty("map"); US_TEST_CONDITION_REQUIRED(anyMap.Type() == typeid(std::map), "map type") std::map& m = ref_any_cast >(anyMap); US_TEST_CONDITION_REQUIRED(m.size() == 3, "map size") US_TEST_CONDITION_REQUIRED(m["string"].Type() == typeid(std::string), "map 0 type") US_TEST_CONDITION_REQUIRED(m["string"].ToString() == "hi", "map 0 value") US_TEST_CONDITION_REQUIRED(m["number"].Type() == typeid(int), "map 1 type") US_TEST_CONDITION_REQUIRED(any_cast(m["number"]) == 4, "map 1 value") US_TEST_CONDITION_REQUIRED(m["list"].Type() == typeid(std::vector), "map 2 type") US_TEST_CONDITION_REQUIRED(any_cast >(m["list"]).size() == 2, "map 2 value size") target.Unload(); US_TEST_END() } diff --git a/Modules/CppMicroServices/core/test/usModuleTest.cpp b/Modules/CppMicroServices/core/test/usModuleTest.cpp index 28f024804b..583cd175d6 100644 --- a/Modules/CppMicroServices/core/test/usModuleTest.cpp +++ b/Modules/CppMicroServices/core/test/usModuleTest.cpp @@ -1,350 +1,350 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include #include #include #include "usTestUtilModuleListener.h" #include "usTestDriverActivator.h" #include "usTestingMacros.h" #include "usTestingConfig.h" US_USE_NAMESPACE namespace { #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; static const char PATH_SEPARATOR = '\\'; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; static const char PATH_SEPARATOR = '/'; #endif // Check that the executable's activator was loaded and called void frame01() { US_TEST_CONDITION_REQUIRED(TestDriverActivator::LoadCalled(), "ModuleActivator::Load() called for executable") } // Verify that the same member function pointers registered as listeners // with different receivers works. void frame02a() { ModuleContext* mc = GetModuleContext(); TestModuleListener listener1; TestModuleListener listener2; try { mc->RemoveModuleListener(&listener1, &TestModuleListener::ModuleChanged); mc->AddModuleListener(&listener1, &TestModuleListener::ModuleChanged); mc->RemoveModuleListener(&listener2, &TestModuleListener::ModuleChanged); mc->AddModuleListener(&listener2, &TestModuleListener::ModuleChanged); } catch (const std::logic_error& ise) { US_TEST_FAILED_MSG( << "module listener registration failed " << ise.what() << " : frameSL02a:FAIL" ); } SharedLibrary target(LIB_PATH, "TestModuleA"); #ifdef US_BUILD_SHARED_LIBS // Start the test target try { target.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what() << " + in frameSL02a:FAIL" ); } #endif Module* moduleA = ModuleRegistry::GetModule("TestModuleA"); - US_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for existing module TestModuleA") + US_TEST_CONDITION_REQUIRED(moduleA != nullptr, "Test for existing module TestModuleA") std::vector pEvts; #ifdef US_BUILD_SHARED_LIBS pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleA)); pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleA)); #endif std::vector seEvts; US_TEST_CONDITION(listener1.CheckListenerEvents(pEvts, seEvts), "Check first module listener") US_TEST_CONDITION(listener2.CheckListenerEvents(pEvts, seEvts), "Check second module listener") mc->RemoveModuleListener(&listener1, &TestModuleListener::ModuleChanged); mc->RemoveModuleListener(&listener2, &TestModuleListener::ModuleChanged); target.Unload(); } // Verify information from the ModuleInfo struct void frame005a(ModuleContext* mc) { Module* m = mc->GetModule(); // check expected headers US_TEST_CONDITION("main" == m->GetName(), "Test module name") US_TEST_CONDITION(ModuleVersion(0,1,0) == m->GetVersion(), "Test test driver module version") US_TEST_CONDITION(ModuleVersion(CppMicroServices_MAJOR_VERSION, CppMicroServices_MINOR_VERSION, CppMicroServices_PATCH_VERSION) == ModuleRegistry::GetModule(1)->GetVersion(), "Test CppMicroServices version") } // Get context id, location and status of the module void frame010a(ModuleContext* mc) { Module* m = mc->GetModule(); long int contextid = m->GetModuleId(); US_DEBUG << "CONTEXT ID:" << contextid; std::string location = m->GetLocation(); US_DEBUG << "LOCATION:" << location; US_TEST_CONDITION(!location.empty(), "Test for non-empty module location") US_TEST_CONDITION(m->IsLoaded(), "Test for loaded flag") US_TEST_CONDITION(ModuleSettings::GetStoragePath().empty(), "Test for empty base storage path") US_TEST_CONDITION(m->GetModuleContext()->GetDataFile("").empty(), "Test for empty data path") US_TEST_CONDITION(m->GetModuleContext()->GetDataFile("bla").empty(), "Test for empty data file path") } //---------------------------------------------------------------------------- //Test result of GetService(ServiceReference()). Should throw std::invalid_argument void frame018a(ModuleContext* mc) { try { mc->GetService(ServiceReferenceU()); US_DEBUG << "Got service object, expected std::invalid_argument exception"; US_TEST_FAILED_MSG(<< "Got service object, excpected std::invalid_argument exception") } catch (const std::invalid_argument& ) {} catch (...) { US_TEST_FAILED_MSG(<< "Got wrong exception, expected std::invalid_argument") } } // Load libA and check that it exists and that the service it registers exists, // also check that the expected events occur and that the storage paths are correct void frame020a(ModuleContext* mc, TestModuleListener& listener, #ifdef US_BUILD_SHARED_LIBS SharedLibrary& libA) { try { libA.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } #else SharedLibrary& /*libA*/) { #endif ModuleSettings::SetStoragePath(std::string("/tmp") + PATH_SEPARATOR); US_TEST_CONDITION(ModuleSettings::GetStoragePath() == "/tmp", "Test for valid base storage path") Module* moduleA = ModuleRegistry::GetModule("TestModuleA"); - US_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for existing module TestModuleA") + US_TEST_CONDITION_REQUIRED(moduleA != nullptr, "Test for existing module TestModuleA") US_TEST_CONDITION(moduleA->GetName() == "TestModuleA", "Test module name") std::cout << moduleA->GetModuleContext()->GetDataFile("") << std::endl; std::stringstream ss; ss << moduleA->GetModuleId(); const std::string baseStoragePath = std::string("/tmp") + PATH_SEPARATOR + ss.str() + "_TestModuleA" + PATH_SEPARATOR; US_TEST_CONDITION(moduleA->GetModuleContext()->GetDataFile("") == baseStoragePath, "Test for valid data path") US_TEST_CONDITION(moduleA->GetModuleContext()->GetDataFile("bla") == baseStoragePath + "bla", "Test for valid data file path") // Check if libA registered the expected service try { ServiceReferenceU sr1 = mc->GetServiceReference("us::TestModuleAService"); InterfaceMap o1 = mc->GetService(sr1); US_TEST_CONDITION(!o1.empty(), "Test if service object found"); try { US_TEST_CONDITION(mc->UngetService(sr1), "Test if Service UnGet returns true"); } catch (const std::logic_error le) { US_TEST_FAILED_MSG(<< "UnGetService exception: " << le.what()) } // check the listeners for events std::vector pEvts; #ifdef US_BUILD_SHARED_LIBS pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleA)); pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleA)); #endif std::vector seEvts; #ifdef US_BUILD_SHARED_LIBS seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, sr1)); #endif US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events"); } catch (const ServiceException& /*se*/) { US_TEST_FAILED_MSG(<< "test module, expected service not found"); } US_TEST_CONDITION(moduleA->IsLoaded() == true, "Test if loaded correctly"); } // Unload libA and check for correct events void frame030b(ModuleContext* mc, TestModuleListener& listener, SharedLibrary& libA) { Module* moduleA = ModuleRegistry::GetModule("TestModuleA"); - US_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for non-null module") + US_TEST_CONDITION_REQUIRED(moduleA != nullptr, "Test for non-null module") ServiceReferenceU sr1 = mc->GetServiceReference("us::TestModuleAService"); US_TEST_CONDITION(sr1, "Test for valid service reference") try { libA.Unload(); #ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(moduleA->IsLoaded() == false, "Test for unloaded state") #endif } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "UnLoad module exception: " << e.what()) } std::vector pEvts; #ifdef US_BUILD_SHARED_LIBS pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleA)); pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleA)); #endif std::vector seEvts; #ifdef US_BUILD_SHARED_LIBS seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, sr1)); #endif US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events"); } struct LocalListener { void ServiceChanged(const ServiceEvent) {} }; // Add a service listener with a broken LDAP filter to Get an exception void frame045a(ModuleContext* mc) { LocalListener sListen1; std::string brokenFilter = "A broken LDAP filter"; try { mc->AddServiceListener(&sListen1, &LocalListener::ServiceChanged, brokenFilter); } catch (const std::invalid_argument& /*ia*/) { //assertEquals("InvalidSyntaxException.GetFilter should be same as input string", brokenFilter, ise.GetFilter()); } catch (...) { US_TEST_FAILED_MSG(<< "test module, wrong exception on broken LDAP filter:"); } } } // end unnamed namespace int usModuleTest(int /*argc*/, char* /*argv*/[]) { //US_TEST_BEGIN("ModuleTest"); std::vector modules = ModuleRegistry::GetModules(); for (std::vector::iterator iter = modules.begin(), iterEnd = modules.end(); iter != iterEnd; ++iter) { std::cout << "----- " << (*iter)->GetName() << std::endl; } frame01(); frame02a(); ModuleContext* mc = GetModuleContext(); TestModuleListener listener; try { mc->AddModuleListener(&listener, &TestModuleListener::ModuleChanged); } catch (const std::logic_error& ise) { US_TEST_OUTPUT( << "module listener registration failed " << ise.what() ); throw; } try { mc->AddServiceListener(&listener, &TestModuleListener::ServiceChanged); } catch (const std::logic_error& ise) { US_TEST_OUTPUT( << "service listener registration failed " << ise.what() ); throw; } frame005a(mc); frame010a(mc); frame018a(mc); SharedLibrary libA(LIB_PATH, "TestModuleA"); frame020a(mc, listener, libA); frame030b(mc, listener, libA); frame045a(mc); mc->RemoveModuleListener(&listener, &TestModuleListener::ModuleChanged); mc->RemoveServiceListener(&listener, &TestModuleListener::ServiceChanged); //US_TEST_END() return 0; } diff --git a/Modules/CppMicroServices/core/test/usServiceFactoryTest.cpp b/Modules/CppMicroServices/core/test/usServiceFactoryTest.cpp index c6434634fd..cd865d4fd7 100644 --- a/Modules/CppMicroServices/core/test/usServiceFactoryTest.cpp +++ b/Modules/CppMicroServices/core/test/usServiceFactoryTest.cpp @@ -1,231 +1,231 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include "usTestingMacros.h" #include "usTestingConfig.h" US_USE_NAMESPACE namespace { #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif } // end unnamed namespace US_BEGIN_NAMESPACE struct TestModuleH { virtual ~TestModuleH() {} }; struct TestModuleH2 { virtual ~TestModuleH2() {} }; US_END_NAMESPACE void TestServiceFactoryModuleScope() { // Install and start test module H, a service factory and test that the methods // in that interface works. SharedLibrary target(LIB_PATH, "TestModuleH"); #ifdef US_BUILD_SHARED_LIBS try { target.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what()) } #endif Module* moduleH = ModuleRegistry::GetModule("TestModuleH"); - US_TEST_CONDITION_REQUIRED(moduleH != 0, "Test for existing module TestModuleH") + US_TEST_CONDITION_REQUIRED(moduleH != nullptr, "Test for existing module TestModuleH") std::vector registeredRefs = moduleH->GetRegisteredServices(); US_TEST_CONDITION_REQUIRED(registeredRefs.size() == 2, "# of registered services") US_TEST_CONDITION(registeredRefs[0].GetProperty(ServiceConstants::SERVICE_SCOPE()).ToString() == ServiceConstants::SCOPE_MODULE(), "service scope") US_TEST_CONDITION(registeredRefs[1].GetProperty(ServiceConstants::SERVICE_SCOPE()).ToString() == ServiceConstants::SCOPE_PROTOTYPE(), "service scope") ModuleContext* mc = GetModuleContext(); // Check that a service reference exist const ServiceReferenceU sr1 = mc->GetServiceReference("us::TestModuleH"); US_TEST_CONDITION_REQUIRED(sr1, "Service shall be present.") US_TEST_CONDITION(sr1.GetProperty(ServiceConstants::SERVICE_SCOPE()).ToString() == ServiceConstants::SCOPE_MODULE(), "service scope") InterfaceMap service = mc->GetService(sr1); US_TEST_CONDITION_REQUIRED(service.size() >= 1, "GetService()") InterfaceMap::const_iterator serviceIter = service.find("us::TestModuleH"); US_TEST_CONDITION_REQUIRED(serviceIter != service.end(), "GetService()") US_TEST_CONDITION_REQUIRED(serviceIter->second != nullptr, "GetService()") InterfaceMap service2 = mc->GetService(sr1); US_TEST_CONDITION(service == service2, "Same service pointer") std::vector usedRefs = mc->GetModule()->GetServicesInUse(); US_TEST_CONDITION_REQUIRED(usedRefs.size() == 1, "services in use") US_TEST_CONDITION(usedRefs[0] == sr1, "service ref in use") InterfaceMap service3 = moduleH->GetModuleContext()->GetService(sr1); US_TEST_CONDITION(service != service3, "Different service pointer") US_TEST_CONDITION(moduleH->GetModuleContext()->UngetService(sr1), "UngetService()") US_TEST_CONDITION_REQUIRED(mc->UngetService(sr1) == false, "ungetService()") US_TEST_CONDITION_REQUIRED(mc->UngetService(sr1) == true, "ungetService()") target.Unload(); } void TestServiceFactoryPrototypeScope() { // Install and start test module H, a service factory and test that the methods // in that interface works. SharedLibrary target(LIB_PATH, "TestModuleH"); #ifdef US_BUILD_SHARED_LIBS try { target.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what()) } Module* moduleH = ModuleRegistry::GetModule("TestModuleH"); - US_TEST_CONDITION_REQUIRED(moduleH != 0, "Test for existing module TestModuleH") + US_TEST_CONDITION_REQUIRED(moduleH != nullptr, "Test for existing module TestModuleH") #endif ModuleContext* mc = GetModuleContext(); // Check that a service reference exist const ServiceReference sr1 = mc->GetServiceReference(); US_TEST_CONDITION_REQUIRED(sr1, "Service shall be present.") US_TEST_CONDITION(sr1.GetProperty(ServiceConstants::SERVICE_SCOPE()).ToString() == ServiceConstants::SCOPE_PROTOTYPE(), "service scope") ServiceObjects svcObjects = mc->GetServiceObjects(sr1); TestModuleH2* prototypeServiceH2 = svcObjects.GetService(); const ServiceReferenceU sr1void = mc->GetServiceReference(us_service_interface_iid()); ServiceObjects svcObjectsVoid = mc->GetServiceObjects(sr1void); InterfaceMap prototypeServiceH2Void = svcObjectsVoid.GetService(); US_TEST_CONDITION_REQUIRED(prototypeServiceH2Void.find(us_service_interface_iid()) != prototypeServiceH2Void.end(), "ServiceObjects::GetService()") #ifdef US_BUILD_SHARED_LIBS // There should be only one service in use US_TEST_CONDITION_REQUIRED(mc->GetModule()->GetServicesInUse().size() == 1, "services in use") #endif TestModuleH2* moduleScopeService = mc->GetService(sr1); US_TEST_CONDITION_REQUIRED(moduleScopeService && moduleScopeService != prototypeServiceH2, "GetService()") US_TEST_CONDITION_REQUIRED(prototypeServiceH2 != prototypeServiceH2Void.find(us_service_interface_iid())->second, "GetService()") svcObjectsVoid.UngetService(prototypeServiceH2Void); TestModuleH2* moduleScopeService2 = mc->GetService(sr1); US_TEST_CONDITION(moduleScopeService == moduleScopeService2, "Same service pointer") #ifdef US_BUILD_SHARED_LIBS std::vector usedRefs = mc->GetModule()->GetServicesInUse(); US_TEST_CONDITION_REQUIRED(usedRefs.size() == 1, "services in use") US_TEST_CONDITION(usedRefs[0] == sr1, "service ref in use") #endif std::string filter = "(" + ServiceConstants::SERVICE_ID() + "=" + sr1.GetProperty(ServiceConstants::SERVICE_ID()).ToString() + ")"; const ServiceReference sr2 = mc->GetServiceReferences(filter).front(); US_TEST_CONDITION_REQUIRED(sr2, "Service shall be present.") US_TEST_CONDITION(sr2.GetProperty(ServiceConstants::SERVICE_SCOPE()).ToString() == ServiceConstants::SCOPE_PROTOTYPE(), "service scope") US_TEST_CONDITION(any_cast(sr2.GetProperty(ServiceConstants::SERVICE_ID())) == any_cast(sr1.GetProperty(ServiceConstants::SERVICE_ID())), "same service id") try { svcObjects.UngetService(moduleScopeService2); US_TEST_FAILED_MSG(<< "std::invalid_argument exception expected") } catch (const std::invalid_argument&) { // this is expected } #ifdef US_BUILD_SHARED_LIBS // There should still be only one service in use usedRefs = mc->GetModule()->GetServicesInUse(); US_TEST_CONDITION_REQUIRED(usedRefs.size() == 1, "services in use") #endif ServiceObjects svcObjects2 = svcObjects; ServiceObjects svcObjects3 = mc->GetServiceObjects(sr1); try { svcObjects3.UngetService(prototypeServiceH2); US_TEST_FAILED_MSG(<< "std::invalid_argument exception expected") } catch (const std::invalid_argument&) { // this is expected } svcObjects2.UngetService(prototypeServiceH2); prototypeServiceH2 = svcObjects2.GetService(); TestModuleH2* prototypeServiceH2_2 = svcObjects3.GetService(); US_TEST_CONDITION_REQUIRED(prototypeServiceH2_2 && prototypeServiceH2_2 != prototypeServiceH2, "prototype service") svcObjects2.UngetService(prototypeServiceH2); svcObjects3.UngetService(prototypeServiceH2_2); US_TEST_CONDITION_REQUIRED(mc->UngetService(sr1) == false, "ungetService()") US_TEST_CONDITION_REQUIRED(mc->UngetService(sr1) == true, "ungetService()") target.Unload(); } int usServiceFactoryTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ServiceFactoryTest"); TestServiceFactoryModuleScope(); TestServiceFactoryPrototypeScope(); US_TEST_END() } diff --git a/Modules/CppMicroServices/core/test/usServiceTrackerTest.cpp b/Modules/CppMicroServices/core/test/usServiceTrackerTest.cpp index ca689cda42..5cfb67a9c0 100644 --- a/Modules/CppMicroServices/core/test/usServiceTrackerTest.cpp +++ b/Modules/CppMicroServices/core/test/usServiceTrackerTest.cpp @@ -1,287 +1,287 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include #include #include "usServiceControlInterface.h" #include US_USE_NAMESPACE bool CheckConvertibility(const std::vector& refs, std::vector::const_iterator idBegin, std::vector::const_iterator idEnd) { std::vector ids; ids.assign(idBegin, idEnd); for (std::vector::const_iterator sri = refs.begin(); sri != refs.end(); ++sri) { for (std::vector::iterator idIter = ids.begin(); idIter != ids.end(); ++idIter) { if (sri->IsConvertibleTo(*idIter)) { ids.erase(idIter); break; } } } return ids.empty(); } struct MyInterfaceOne { virtual ~MyInterfaceOne() {} }; struct MyInterfaceTwo { virtual ~MyInterfaceTwo() {} }; class MyCustomizer : public us::ServiceTrackerCustomizer { public: MyCustomizer(us::ModuleContext* context) : m_context(context) {} MyInterfaceOne* AddingService(const ServiceReferenceType& reference) override { US_TEST_CONDITION_REQUIRED(reference, "AddingService() valid reference") return m_context->GetService(reference); } void ModifiedService(const ServiceReferenceType& reference, MyInterfaceOne* service) override { US_TEST_CONDITION(reference, "ModifiedService() valid reference") US_TEST_CONDITION(service, "ModifiedService() valid service") } void RemovedService(const ServiceReferenceType& reference, MyInterfaceOne* service) override { US_TEST_CONDITION(reference, "RemovedService() valid reference") US_TEST_CONDITION(service, "RemovedService() valid service") } private: us::ModuleContext* m_context; }; void TestFilterString() { us::ModuleContext* context = us::GetModuleContext(); MyCustomizer customizer(context); us::LDAPFilter filter("(" + us::ServiceConstants::SERVICE_ID() + ">=0)"); us::ServiceTracker tracker(context, filter, &customizer); tracker.Open(); struct MyServiceOne : public MyInterfaceOne {}; struct MyServiceTwo : public MyInterfaceTwo {}; MyServiceOne serviceOne; MyServiceTwo serviceTwo; ServiceRegistration reg1 = context->RegisterService(&serviceOne); ServiceRegistration reg2 = context->RegisterService(&serviceTwo); US_TEST_CONDITION(tracker.GetServiceReferences().size() == 1, "tracking count") reg1.Unregister(); reg2.Unregister(); } void TestServiceTracker() { #ifdef US_PLATFORM_WINDOWS const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif ModuleContext* mc = GetModuleContext(); SharedLibrary libS(LIB_PATH, "TestModuleS"); #ifdef US_BUILD_SHARED_LIBS // Start the test target to get a service published. try { libS.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what() ); } #endif // 1. Create a ServiceTracker with ServiceTrackerCustomizer == null std::string s1("us::TestModuleSService"); ServiceReferenceU servref = mc->GetServiceReference(s1 + "0"); - US_TEST_CONDITION_REQUIRED(servref != 0, "Test if registered service of id us::TestModuleSService0"); + US_TEST_CONDITION_REQUIRED(servref != nullptr, "Test if registered service of id us::TestModuleSService0"); ServiceReference servCtrlRef = mc->GetServiceReference(); - US_TEST_CONDITION_REQUIRED(servCtrlRef != 0, "Test if constrol service was registered"); + US_TEST_CONDITION_REQUIRED(servCtrlRef != nullptr, "Test if constrol service was registered"); ServiceControlInterface* serviceController = mc->GetService(servCtrlRef); - US_TEST_CONDITION_REQUIRED(serviceController != 0, "Test valid service controller"); + US_TEST_CONDITION_REQUIRED(serviceController != nullptr, "Test valid service controller"); std::unique_ptr> st1(new ServiceTracker(mc, servref)); // 2. Check the size method with an unopened service tracker US_TEST_CONDITION_REQUIRED(st1->Size() == 0, "Test if size == 0"); // 3. Open the service tracker and see what it finds, // expect to find one instance of the implementation, // "org.cppmicroservices.TestModuleSService0" st1->Open(); std::vector sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.size() == 1, "Checking ServiceTracker size"); US_TEST_CONDITION_REQUIRED(s1 + "0" == sa2[0].GetInterfaceId(), "Checking service implementation name"); // 5. Close this service tracker st1->Close(); // 6. Check the size method, now when the servicetracker is closed US_TEST_CONDITION_REQUIRED(st1->Size() == 0, "Checking ServiceTracker size"); // 7. Check if we still track anything , we should get null sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.empty(), "Checking ServiceTracker size"); // 8. A new Servicetracker, this time with a filter for the object std::string fs = std::string("(") + ServiceConstants::OBJECTCLASS() + "=" + s1 + "*" + ")"; LDAPFilter f1(fs); st1.reset(new ServiceTracker(mc, f1)); // add a service serviceController->ServiceControl(1, "register", 7); // 9. Open the service tracker and see what it finds, // expect to find two instances of references to // "org.cppmicroservices.TestModuleSService*" // i.e. they refer to the same piece of code std::vector ids; ids.push_back((s1 + "0")); ids.push_back((s1 + "1")); ids.push_back((s1 + "2")); ids.push_back((s1 + "3")); st1->Open(); sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.size() == 2, "Checking service reference count"); US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa2, ids.begin(), ids.begin()+2), "Check for expected interface id [0]"); US_TEST_CONDITION_REQUIRED(sa2[1].IsConvertibleTo(s1 + "1"), "Check for expected interface id [1]"); // 10. Get libTestModuleS to register one more service and see if it appears serviceController->ServiceControl(2, "register", 1); sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.size() == 3, "Checking service reference count"); US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa2, ids.begin(), ids.begin()+3), "Check for expected interface id [2]"); // 11. Get libTestModuleS to register one more service and see if it appears serviceController->ServiceControl(3, "register", 2); sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.size() == 4, "Checking service reference count"); US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa2, ids.begin(), ids.end()), "Check for expected interface id [3]"); // 12. Get libTestModuleS to unregister one service and see if it disappears serviceController->ServiceControl(3, "unregister", 0); sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.size() == 3, "Checking service reference count"); // 13. Get the highest ranking service reference, it should have ranking 7 ServiceReferenceU h1 = st1->GetServiceReference(); int rank = any_cast(h1.GetProperty(ServiceConstants::SERVICE_RANKING())); US_TEST_CONDITION_REQUIRED(rank == 7, "Check service rank"); // 14. Get the service of the highest ranked service reference InterfaceMap o1 = st1->GetService(h1); US_TEST_CONDITION_REQUIRED(!o1.empty(), "Check for non-null service"); // 14a Get the highest ranked service, directly this time InterfaceMap o3 = st1->GetService(); US_TEST_CONDITION_REQUIRED(!o3.empty(), "Check for non-null service"); US_TEST_CONDITION_REQUIRED(o1 == o3, "Check for equal service instances"); // 15. Now release the tracking of that service and then try to get it // from the servicetracker, which should yield a null object serviceController->ServiceControl(1, "unregister", 7); InterfaceMap o2 = st1->GetService(h1); US_TEST_CONDITION_REQUIRED(o2.empty(), "Checkt that service is null"); // 16. Get all service objects this tracker tracks, it should be 2 std::vector ts1 = st1->GetServices(); US_TEST_CONDITION_REQUIRED(ts1.size() == 2, "Check service count"); // 17. Test the remove method. // First register another service, then remove it being tracked serviceController->ServiceControl(1, "register", 7); h1 = st1->GetServiceReference(); std::vector sa3 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa3.size() == 3, "Check service reference count"); US_TEST_CONDITION_REQUIRED(CheckConvertibility(sa3, ids.begin(), ids.begin()+3), "Check for expected interface id [0]"); st1->Remove(h1); // remove tracking on one servref sa2 = st1->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(sa2.size() == 2, "Check service reference count"); // 18. Test the addingService method,add a service reference // 19. Test the removedService method, remove a service reference // 20. Test the waitForService method InterfaceMap o9 = st1->WaitForService(50); US_TEST_CONDITION_REQUIRED(!o9.empty(), "Checking WaitForService method"); } int usServiceTrackerTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ServiceTrackerTest") TestFilterString(); TestServiceTracker(); US_TEST_END() } diff --git a/Modules/CppMicroServices/core/test/usStaticModuleTest.cpp b/Modules/CppMicroServices/core/test/usStaticModuleTest.cpp index 187a8cf642..049606c4b9 100644 --- a/Modules/CppMicroServices/core/test/usStaticModuleTest.cpp +++ b/Modules/CppMicroServices/core/test/usStaticModuleTest.cpp @@ -1,183 +1,183 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include #include #include #include #include #include #include #include #include "usTestUtilModuleListener.h" #include "usTestingMacros.h" #include "usTestingConfig.h" US_USE_NAMESPACE namespace { #ifdef US_PLATFORM_WINDOWS static const std::string LIB_PATH = US_RUNTIME_OUTPUT_DIRECTORY; #else static const std::string LIB_PATH = US_LIBRARY_OUTPUT_DIRECTORY; #endif // Load libTestModuleB and check that it exists and that the service it registers exists, // also check that the expected events occur void frame020a(ModuleContext* mc, TestModuleListener& listener, #ifdef US_BUILD_SHARED_LIBS SharedLibrary& libB) { try { libB.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } #else SharedLibrary& /*libB*/) { #endif Module* moduleB = ModuleRegistry::GetModule("TestModuleB"); - US_TEST_CONDITION_REQUIRED(moduleB != 0, "Test for existing module TestModuleB") + US_TEST_CONDITION_REQUIRED(moduleB != nullptr, "Test for existing module TestModuleB") Module* moduleImportedByB = ModuleRegistry::GetModule("TestModuleImportedByB"); - US_TEST_CONDITION_REQUIRED(moduleImportedByB != 0, "Test for existing module TestModuleImportedByB") + US_TEST_CONDITION_REQUIRED(moduleImportedByB != nullptr, "Test for existing module TestModuleImportedByB") US_TEST_CONDITION(moduleB->GetName() == "TestModuleB", "Test module name") // Check if libB registered the expected service try { std::vector refs = mc->GetServiceReferences("us::TestModuleBService"); US_TEST_CONDITION_REQUIRED(refs.size() == 2, "Test that both the service from the shared and imported library are regsitered"); InterfaceMap o1 = mc->GetService(refs.front()); US_TEST_CONDITION(!o1.empty(), "Test if first service object found"); InterfaceMap o2 = mc->GetService(refs.back()); US_TEST_CONDITION(!o2.empty(), "Test if second service object found"); try { US_TEST_CONDITION(mc->UngetService(refs.front()), "Test if Service UnGet for first service returns true"); US_TEST_CONDITION(mc->UngetService(refs.back()), "Test if Service UnGet for second service returns true"); } catch (const std::logic_error le) { US_TEST_FAILED_MSG(<< "UnGetService exception: " << le.what()) } // check the listeners for events std::vector pEvts; #ifdef US_BUILD_SHARED_LIBS pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleB)); pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleB)); pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleImportedByB)); pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleImportedByB)); #endif std::vector seEvts; #ifdef US_BUILD_SHARED_LIBS seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, refs.back())); seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, refs.front())); #endif US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events"); } catch (const ServiceException& /*se*/) { US_TEST_FAILED_MSG(<< "test module, expected service not found"); } #ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(moduleB->IsLoaded() == true, "Test if loaded correctly"); #endif } // Unload libB and check for correct events void frame030b(ModuleContext* mc, TestModuleListener& listener, SharedLibrary& libB) { Module* moduleB = ModuleRegistry::GetModule("TestModuleB"); - US_TEST_CONDITION_REQUIRED(moduleB != 0, "Test for non-null module") + US_TEST_CONDITION_REQUIRED(moduleB != nullptr, "Test for non-null module") Module* moduleImportedByB = ModuleRegistry::GetModule("TestModuleImportedByB"); - US_TEST_CONDITION_REQUIRED(moduleImportedByB != 0, "Test for non-null module") + US_TEST_CONDITION_REQUIRED(moduleImportedByB != nullptr, "Test for non-null module") std::vector refs = mc->GetServiceReferences("us::TestModuleBService"); US_TEST_CONDITION(refs.front(), "Test for first valid service reference") US_TEST_CONDITION(refs.back(), "Test for second valid service reference") try { libB.Unload(); #ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(moduleB->IsLoaded() == false, "Test for unloaded state") #endif } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "UnLoad module exception: " << e.what()) } std::vector pEvts; #ifdef US_BUILD_SHARED_LIBS pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleImportedByB)); pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleImportedByB)); pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleB)); pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleB)); #endif std::vector seEvts; #ifdef US_BUILD_SHARED_LIBS seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, refs.front())); seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, refs.back())); #endif US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events"); } } // end unnamed namespace int usStaticModuleTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("StaticModuleTest"); ModuleContext* mc = GetModuleContext(); TestModuleListener listener; ModuleListenerRegistrationHelper ml(mc, &listener, &TestModuleListener::ModuleChanged); ServiceListenerRegistrationHelper sl(mc, &listener, &TestModuleListener::ServiceChanged); SharedLibrary libB(LIB_PATH, "TestModuleB"); frame020a(mc, listener, libB); frame030b(mc, listener, libB); US_TEST_END() } diff --git a/Modules/CppMicroServices/core/test/usTestDriverActivator.cpp b/Modules/CppMicroServices/core/test/usTestDriverActivator.cpp index 92d4a3a725..336e00f351 100644 --- a/Modules/CppMicroServices/core/test/usTestDriverActivator.cpp +++ b/Modules/CppMicroServices/core/test/usTestDriverActivator.cpp @@ -1,69 +1,69 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usTestDriverActivator.h" #include "usModuleImport.h" US_BEGIN_NAMESPACE -TestDriverActivator* TestDriverActivator::m_Instance = 0; +TestDriverActivator* TestDriverActivator::m_Instance = nullptr; TestDriverActivator::TestDriverActivator() : m_LoadCalled(false) { } bool TestDriverActivator::LoadCalled() { return m_Instance ? m_Instance->m_LoadCalled : false; } void TestDriverActivator::Load(ModuleContext*) { this->m_Instance = this; this->m_LoadCalled = true; } void TestDriverActivator::Unload(ModuleContext*) { - this->m_Instance = 0; + this->m_Instance = nullptr; } US_END_NAMESPACE US_EXPORT_MODULE_ACTIVATOR(us::TestDriverActivator) #ifndef US_BUILD_SHARED_LIBS US_IMPORT_MODULE(CppMicroServices) US_IMPORT_MODULE(TestModuleA) US_IMPORT_MODULE(TestModuleA2) US_IMPORT_MODULE(TestModuleB) US_IMPORT_MODULE(TestModuleH) US_IMPORT_MODULE(TestModuleM) US_INITIALIZE_STATIC_MODULE(TestModuleR) US_INITIALIZE_STATIC_MODULE(TestModuleRL) US_INITIALIZE_STATIC_MODULE(TestModuleRA) US_IMPORT_MODULE(TestModuleS) US_IMPORT_MODULE(TestModuleSL1) US_IMPORT_MODULE(TestModuleSL3) US_IMPORT_MODULE(TestModuleSL4) US_IMPORT_MODULE(main) #endif diff --git a/Modules/CppMicroServices/third_party/jsoncpp.cpp b/Modules/CppMicroServices/third_party/jsoncpp.cpp index 71e579049d..e4d9ea08a6 100644 --- a/Modules/CppMicroServices/third_party/jsoncpp.cpp +++ b/Modules/CppMicroServices/third_party/jsoncpp.cpp @@ -1,4225 +1,4225 @@ /// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/). /// It is intented to be used with #include // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: LICENSE // ////////////////////////////////////////////////////////////////////// /* The JsonCpp library's source code, including accompanying documentation, tests and demonstration applications, are licensed under the following conditions... The author (Baptiste Lepilleur) explicitly disclaims copyright in all jurisdictions which recognize such a disclaimer. In such jurisdictions, this software is released into the Public Domain. In jurisdictions which do not recognize Public Domain property (e.g. Germany as of 2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is released under the terms of the MIT License (see below). In jurisdictions which recognize Public Domain property, the user of this software may choose to accept it either as 1) Public Domain, 2) under the conditions of the MIT License (see below), or 3) under the terms of dual Public Domain/MIT License conditions described here, as they choose. The MIT License is about as close to Public Domain as a license can get, and is described in clear, concise terms at: http://en.wikipedia.org/wiki/MIT_License The full text of the MIT License follows: ======================================================================== Copyright (c) 2007-2010 Baptiste Lepilleur 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 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. ======================================================================== (END LICENSE TEXT) The MIT license is compatible with both the GPL and commercial software, affording one all of the rights of Public Domain with the minor nuisance of being required to keep the above copyright notice and license text in the source code. Note also that by accepting the Public Domain "license" you can re-license your copy using whatever license you like. */ // ////////////////////////////////////////////////////////////////////// // End of content of file: LICENSE // ////////////////////////////////////////////////////////////////////// #include // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: src/lib_json/json_tool.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED # define LIB_JSONCPP_JSON_TOOL_H_INCLUDED /* This header provides common string manipulation support, such as UTF-8, * portable conversion from/to string... * * It is an internal header that must not be exposed. */ namespace Json { /// Converts a unicode code-point to UTF-8. static inline std::string codePointToUTF8(unsigned int cp) { std::string result; // based on description from http://en.wikipedia.org/wiki/UTF-8 if (cp <= 0x7f) { result.resize(1); result[0] = static_cast(cp); } else if (cp <= 0x7FF) { result.resize(2); result[1] = static_cast(0x80 | (0x3f & cp)); result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); } else if (cp <= 0xFFFF) { result.resize(3); result[2] = static_cast(0x80 | (0x3f & cp)); result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); } else if (cp <= 0x10FFFF) { result.resize(4); result[3] = static_cast(0x80 | (0x3f & cp)); result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); } return result; } /// Returns true if ch is a control character (in range [0,32[). static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; } enum { /// Constant that specify the size of the buffer that must be passed to uintToString. uintToStringBufferSize = 3*sizeof(LargestUInt)+1 }; // Defines a char buffer for use with uintToString(). typedef char UIntToStringBuffer[uintToStringBufferSize]; /** Converts an unsigned integer to string. * @param value Unsigned interger to convert to string * @param current Input/Output string buffer. * Must have at least uintToStringBufferSize chars free. */ static inline void uintToString( LargestUInt value, char *¤t ) { *--current = 0; do { *--current = char(value % 10) + '0'; value /= 10; } while ( value != 0 ); } } // namespace Json { #endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_tool.h // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: src/lib_json/json_reader.cpp // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) # include # include # include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include #include #include #include #if _MSC_VER >= 1400 // VC++ 8.0 #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. #endif namespace Json { // Implementation of class Features // //////////////////////////////// Features::Features() : allowComments_( true ) , strictRoot_( false ) { } Features Features::all() { return Features(); } Features Features::strictMode() { Features features; features.allowComments_ = false; features.strictRoot_ = true; return features; } // Implementation of class Reader // //////////////////////////////// static inline bool in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) { return c == c1 || c == c2 || c == c3 || c == c4; } static inline bool in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) { return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; } static bool containsNewLine( Reader::Location begin, Reader::Location end ) { for ( ;begin < end; ++begin ) if ( *begin == '\n' || *begin == '\r' ) return true; return false; } // Class Reader // ////////////////////////////////////////////////////////////////// Reader::Reader() : features_( Features::all() ) { } Reader::Reader( const Features &features ) : features_( features ) { } bool Reader::parse( const std::string &document, Value &root, bool collectComments ) { document_ = document; const char *begin = document_.c_str(); const char *end = begin + document_.length(); return parse( begin, end, root, collectComments ); } bool Reader::parse( std::istream& sin, Value &root, bool collectComments ) { //std::istream_iterator begin(sin); //std::istream_iterator end; // Those would allow streamed input from a file, if parse() were a // template function. // Since std::string is reference-counted, this at least does not // create an extra copy. std::string doc; std::getline(sin, doc, static_cast(EOF)); return parse( doc, root, collectComments ); } bool Reader::parse( const char *beginDoc, const char *endDoc, Value &root, bool collectComments ) { if ( !features_.allowComments_ ) { collectComments = false; } begin_ = beginDoc; end_ = endDoc; collectComments_ = collectComments; current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; + lastValueEnd_ = nullptr; + lastValue_ = nullptr; commentsBefore_ = ""; errors_.clear(); while ( !nodes_.empty() ) nodes_.pop(); nodes_.push( &root ); bool successful = readValue(); Token token; skipCommentTokens( token ); if ( collectComments_ && !commentsBefore_.empty() ) root.setComment( commentsBefore_, commentAfter ); if ( features_.strictRoot_ ) { if ( !root.isArray() && !root.isObject() ) { // Set error location to start of doc, ideally should be first token found in doc token.type_ = tokenError; token.start_ = beginDoc; token.end_ = endDoc; addError( "A valid JSON document must be either an array or an object value.", token ); return false; } } return successful; } bool Reader::readValue() { Token token; skipCommentTokens( token ); bool successful = true; if ( collectComments_ && !commentsBefore_.empty() ) { currentValue().setComment( commentsBefore_, commentBefore ); commentsBefore_ = ""; } switch ( token.type_ ) { case tokenObjectBegin: successful = readObject( token ); break; case tokenArrayBegin: successful = readArray( token ); break; case tokenNumber: successful = decodeNumber( token ); break; case tokenString: successful = decodeString( token ); break; case tokenTrue: currentValue() = true; break; case tokenFalse: currentValue() = false; break; case tokenNull: currentValue() = Value(); break; default: return addError( "Syntax error: value, object or array expected.", token ); } if ( collectComments_ ) { lastValueEnd_ = current_; lastValue_ = ¤tValue(); } return successful; } void Reader::skipCommentTokens( Token &token ) { if ( features_.allowComments_ ) { do { readToken( token ); } while ( token.type_ == tokenComment ); } else { readToken( token ); } } bool Reader::expectToken( TokenType type, Token &token, const char *message ) { readToken( token ); if ( token.type_ != type ) return addError( message, token ); return true; } bool Reader::readToken( Token &token ) { skipSpaces(); token.start_ = current_; Char c = getNextChar(); bool ok = true; switch ( c ) { case '{': token.type_ = tokenObjectBegin; break; case '}': token.type_ = tokenObjectEnd; break; case '[': token.type_ = tokenArrayBegin; break; case ']': token.type_ = tokenArrayEnd; break; case '"': token.type_ = tokenString; ok = readString(); break; case '/': token.type_ = tokenComment; ok = readComment(); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': token.type_ = tokenNumber; readNumber(); break; case 't': token.type_ = tokenTrue; ok = match( "rue", 3 ); break; case 'f': token.type_ = tokenFalse; ok = match( "alse", 4 ); break; case 'n': token.type_ = tokenNull; ok = match( "ull", 3 ); break; case ',': token.type_ = tokenArraySeparator; break; case ':': token.type_ = tokenMemberSeparator; break; case 0: token.type_ = tokenEndOfStream; break; default: ok = false; break; } if ( !ok ) token.type_ = tokenError; token.end_ = current_; return true; } void Reader::skipSpaces() { while ( current_ != end_ ) { Char c = *current_; if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) ++current_; else break; } } bool Reader::match( Location pattern, int patternLength ) { if ( end_ - current_ < patternLength ) return false; int index = patternLength; while ( index-- ) if ( current_[index] != pattern[index] ) return false; current_ += patternLength; return true; } bool Reader::readComment() { Location commentBegin = current_ - 1; Char c = getNextChar(); bool successful = false; if ( c == '*' ) successful = readCStyleComment(); else if ( c == '/' ) successful = readCppStyleComment(); if ( !successful ) return false; if ( collectComments_ ) { CommentPlacement placement = commentBefore; if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) { if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) placement = commentAfterOnSameLine; } addComment( commentBegin, current_, placement ); } return true; } void Reader::addComment( Location begin, Location end, CommentPlacement placement ) { assert( collectComments_ ); if ( placement == commentAfterOnSameLine ) { - assert( lastValue_ != 0 ); + assert( lastValue_ != nullptr ); lastValue_->setComment( std::string( begin, end ), placement ); } else { if ( !commentsBefore_.empty() ) commentsBefore_ += "\n"; commentsBefore_ += std::string( begin, end ); } } bool Reader::readCStyleComment() { while ( current_ != end_ ) { Char c = getNextChar(); if ( c == '*' && *current_ == '/' ) break; } return getNextChar() == '/'; } bool Reader::readCppStyleComment() { while ( current_ != end_ ) { Char c = getNextChar(); if ( c == '\r' || c == '\n' ) break; } return true; } void Reader::readNumber() { while ( current_ != end_ ) { if ( !(*current_ >= '0' && *current_ <= '9') && !in( *current_, '.', 'e', 'E', '+', '-' ) ) break; ++current_; } } bool Reader::readString() { Char c = 0; while ( current_ != end_ ) { c = getNextChar(); if ( c == '\\' ) getNextChar(); else if ( c == '"' ) break; } return c == '"'; } bool Reader::readObject( Token &/*tokenStart*/ ) { Token tokenName; std::string name; currentValue() = Value( objectValue ); while ( readToken( tokenName ) ) { bool initialTokenOk = true; while ( tokenName.type_ == tokenComment && initialTokenOk ) initialTokenOk = readToken( tokenName ); if ( !initialTokenOk ) break; if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object return true; if ( tokenName.type_ != tokenString ) break; name = ""; if ( !decodeString( tokenName, name ) ) return recoverFromError( tokenObjectEnd ); Token colon; if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) { return addErrorAndRecover( "Missing ':' after object member name", colon, tokenObjectEnd ); } Value &value = currentValue()[ name ]; nodes_.push( &value ); bool ok = readValue(); nodes_.pop(); if ( !ok ) // error already set return recoverFromError( tokenObjectEnd ); Token comma; if ( !readToken( comma ) || ( comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator && comma.type_ != tokenComment ) ) { return addErrorAndRecover( "Missing ',' or '}' in object declaration", comma, tokenObjectEnd ); } bool finalizeTokenOk = true; while ( comma.type_ == tokenComment && finalizeTokenOk ) finalizeTokenOk = readToken( comma ); if ( comma.type_ == tokenObjectEnd ) return true; } return addErrorAndRecover( "Missing '}' or object member name", tokenName, tokenObjectEnd ); } bool Reader::readArray( Token &/*tokenStart*/ ) { currentValue() = Value( arrayValue ); skipSpaces(); if ( *current_ == ']' ) // empty array { Token endArray; readToken( endArray ); return true; } int index = 0; for (;;) { Value &value = currentValue()[ index++ ]; nodes_.push( &value ); bool ok = readValue(); nodes_.pop(); if ( !ok ) // error already set return recoverFromError( tokenArrayEnd ); Token token; // Accept Comment after last item in the array. ok = readToken( token ); while ( token.type_ == tokenComment && ok ) { ok = readToken( token ); } bool badTokenType = ( token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd ); if ( !ok || badTokenType ) { return addErrorAndRecover( "Missing ',' or ']' in array declaration", token, tokenArrayEnd ); } if ( token.type_ == tokenArrayEnd ) break; } return true; } bool Reader::decodeNumber( Token &token ) { bool isDouble = false; for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) { isDouble = isDouble || in( *inspect, '.', 'e', 'E', '+' ) || ( *inspect == '-' && inspect != token.start_ ); } if ( isDouble ) return decodeDouble( token ); // Attempts to parse the number as an integer. If the number is // larger than the maximum supported value of an integer then // we decode the number as a double. Location current = token.start_; bool isNegative = *current == '-'; if ( isNegative ) ++current; Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) : Value::maxLargestUInt; Value::LargestUInt threshold = maxIntegerValue / 10; Value::UInt lastDigitThreshold = Value::UInt( maxIntegerValue % 10 ); assert(/* lastDigitThreshold >=0 &&*/ lastDigitThreshold <= 9 ); Value::LargestUInt value = 0; while ( current < token.end_ ) { Char c = *current++; if ( c < '0' || c > '9' ) return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); Value::UInt digit(c - '0'); if ( value >= threshold ) { // If the current digit is not the last one, or if it is // greater than the last digit of the maximum integer value, // the parse the number as a double. if ( current != token.end_ || digit > lastDigitThreshold ) { return decodeDouble( token ); } } value = value * 10 + digit; } if ( isNegative ) currentValue() = -Value::LargestInt( value ); else if ( value <= Value::LargestUInt(Value::maxInt) ) currentValue() = Value::LargestInt( value ); else currentValue() = value; return true; } bool Reader::decodeDouble( Token &token ) { double value = 0; const int bufferSize = 32; int count; int length = int(token.end_ - token.start_); if ( length <= bufferSize ) { Char buffer[bufferSize+1]; memcpy( buffer, token.start_, length ); buffer[length] = 0; count = sscanf( buffer, "%lf", &value ); } else { std::string buffer( token.start_, token.end_ ); count = sscanf( buffer.c_str(), "%lf", &value ); } if ( count != 1 ) return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); currentValue() = value; return true; } bool Reader::decodeString( Token &token ) { std::string decoded; if ( !decodeString( token, decoded ) ) return false; currentValue() = decoded; return true; } bool Reader::decodeString( Token &token, std::string &decoded ) { decoded.reserve( token.end_ - token.start_ - 2 ); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' while ( current != end ) { Char c = *current++; if ( c == '"' ) break; else if ( c == '\\' ) { if ( current == end ) return addError( "Empty escape sequence in string", token, current ); Char escape = *current++; switch ( escape ) { case '"': decoded += '"'; break; case '/': decoded += '/'; break; case '\\': decoded += '\\'; break; case 'b': decoded += '\b'; break; case 'f': decoded += '\f'; break; case 'n': decoded += '\n'; break; case 'r': decoded += '\r'; break; case 't': decoded += '\t'; break; case 'u': { unsigned int unicode; if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) return false; decoded += codePointToUTF8(unicode); } break; default: return addError( "Bad escape sequence in string", token, current ); } } else { decoded += c; } } return true; } bool Reader::decodeUnicodeCodePoint( Token &token, Location ¤t, Location end, unsigned int &unicode ) { if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) return false; if (unicode >= 0xD800 && unicode <= 0xDBFF) { // surrogate pairs if (end - current < 6) return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); unsigned int surrogatePair; if (*(current++) == '\\' && *(current++)== 'u') { if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) { unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); } else return false; } else return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); } return true; } bool Reader::decodeUnicodeEscapeSequence( Token &token, Location ¤t, Location end, unsigned int &unicode ) { if ( end - current < 4 ) return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); unicode = 0; for ( int index =0; index < 4; ++index ) { Char c = *current++; unicode *= 16; if ( c >= '0' && c <= '9' ) unicode += c - '0'; else if ( c >= 'a' && c <= 'f' ) unicode += c - 'a' + 10; else if ( c >= 'A' && c <= 'F' ) unicode += c - 'A' + 10; else return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); } return true; } bool Reader::addError( const std::string &message, Token &token, Location extra ) { ErrorInfo info; info.token_ = token; info.message_ = message; info.extra_ = extra; errors_.push_back( info ); return false; } bool Reader::recoverFromError( TokenType skipUntilToken ) { int errorCount = int(errors_.size()); Token skip; for (;;) { if ( !readToken(skip) ) errors_.resize( errorCount ); // discard errors caused by recovery if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) break; } errors_.resize( errorCount ); return false; } bool Reader::addErrorAndRecover( const std::string &message, Token &token, TokenType skipUntilToken ) { addError( message, token ); return recoverFromError( skipUntilToken ); } Value & Reader::currentValue() { return *(nodes_.top()); } Reader::Char Reader::getNextChar() { if ( current_ == end_ ) return 0; return *current_++; } void Reader::getLocationLineAndColumn( Location location, int &line, int &column ) const { Location current = begin_; Location lastLineStart = current; line = 0; while ( current < location && current != end_ ) { Char c = *current++; if ( c == '\r' ) { if ( *current == '\n' ) ++current; lastLineStart = current; ++line; } else if ( c == '\n' ) { lastLineStart = current; ++line; } } // column & line start at 1 column = int(location - lastLineStart) + 1; ++line; } std::string Reader::getLocationLineAndColumn( Location location ) const { int line, column; getLocationLineAndColumn( location, line, column ); char buffer[18+16+16+1]; sprintf( buffer, "Line %d, Column %d", line, column ); return buffer; } // Deprecated. Preserved for backward compatibility std::string Reader::getFormatedErrorMessages() const { return getFormattedErrorMessages(); } std::string Reader::getFormattedErrorMessages() const { std::string formattedMessage; for ( Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError ) { const ErrorInfo &error = *itError; formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; formattedMessage += " " + error.message_ + "\n"; if ( error.extra_ ) formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; } return formattedMessage; } std::istream& operator>>( std::istream &sin, Value &root ) { Json::Reader reader; bool ok = reader.parse(sin, root, true); //JSON_ASSERT( ok ); if (!ok) throw std::runtime_error(reader.getFormattedErrorMessages()); return sin; } } // namespace Json // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_reader.cpp // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: src/lib_json/json_batchallocator.h // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED # define JSONCPP_BATCHALLOCATOR_H_INCLUDED # include # include # ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION namespace Json { /* Fast memory allocator. * * This memory allocator allocates memory for a batch of object (specified by * the page size, the number of object in each page). * * It does not allow the destruction of a single object. All the allocated objects * can be destroyed at once. The memory can be either released or reused for future * allocation. * * The in-place new operator must be used to construct the object using the pointer * returned by allocate. */ template class BatchAllocator { public: typedef AllocatedType Type; BatchAllocator( unsigned int objectsPerPage = 255 ) : freeHead_( 0 ) , objectsPerPage_( objectsPerPage ) { // printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. assert( objectsPerPage >= 16 ); batches_ = allocateBatch( 0 ); // allocated a dummy page currentBatch_ = batches_; } ~BatchAllocator() { for ( BatchInfo *batch = batches_; batch; ) { BatchInfo *nextBatch = batch->next_; free( batch ); batch = nextBatch; } } /// allocate space for an array of objectPerAllocation object. /// @warning it is the responsability of the caller to call objects constructors. AllocatedType *allocate() { if ( freeHead_ ) // returns node from free list. { AllocatedType *object = freeHead_; freeHead_ = *static_cast(object); return object; } if ( currentBatch_->used_ == currentBatch_->end_ ) { currentBatch_ = currentBatch_->next_; while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ ) currentBatch_ = currentBatch_->next_; if ( !currentBatch_ ) // no free batch found, allocate a new one { currentBatch_ = allocateBatch( objectsPerPage_ ); currentBatch_->next_ = batches_; // insert at the head of the list batches_ = currentBatch_; } } AllocatedType *allocated = currentBatch_->used_; currentBatch_->used_ += objectPerAllocation; return allocated; } /// Release the object. /// @warning it is the responsability of the caller to actually destruct the object. void release( AllocatedType *object ) { assert( object != 0 ); *static_cast(object) = freeHead_; freeHead_ = object; } private: struct BatchInfo { BatchInfo *next_; AllocatedType *used_; AllocatedType *end_; AllocatedType buffer_[objectPerAllocation]; }; // disabled copy constructor and assignement operator. BatchAllocator( const BatchAllocator & ); void operator =( const BatchAllocator &); static BatchInfo *allocateBatch( unsigned int objectsPerPage ) { const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; BatchInfo *batch = static_cast( malloc( mallocSize ) ); batch->next_ = 0; batch->used_ = batch->buffer_; batch->end_ = batch->buffer_ + objectsPerPage; return batch; } BatchInfo *batches_; BatchInfo *currentBatch_; /// Head of a single linked list within the allocated space of freeed object AllocatedType *freeHead_; unsigned int objectsPerPage_; }; } // namespace Json # endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION #endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_batchallocator.h // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: src/lib_json/json_valueiterator.inl // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE // included by json_value.cpp namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class ValueIteratorBase // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// ValueIteratorBase::ValueIteratorBase() #ifndef JSON_VALUE_USE_INTERNAL_MAP : current_() , isNull_( true ) { } #else : isArray_( true ) , isNull_( true ) { iterator_.array_ = ValueInternalArray::IteratorState(); } #endif #ifndef JSON_VALUE_USE_INTERNAL_MAP ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) : current_( current ) , isNull_( false ) { } #else ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) : isArray_( true ) { iterator_.array_ = state; } ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) : isArray_( false ) { iterator_.map_ = state; } #endif Value & ValueIteratorBase::deref() const { #ifndef JSON_VALUE_USE_INTERNAL_MAP return current_->second; #else if ( isArray_ ) return ValueInternalArray::dereference( iterator_.array_ ); return ValueInternalMap::value( iterator_.map_ ); #endif } void ValueIteratorBase::increment() { #ifndef JSON_VALUE_USE_INTERNAL_MAP ++current_; #else if ( isArray_ ) ValueInternalArray::increment( iterator_.array_ ); ValueInternalMap::increment( iterator_.map_ ); #endif } void ValueIteratorBase::decrement() { #ifndef JSON_VALUE_USE_INTERNAL_MAP --current_; #else if ( isArray_ ) ValueInternalArray::decrement( iterator_.array_ ); ValueInternalMap::decrement( iterator_.map_ ); #endif } ValueIteratorBase::difference_type ValueIteratorBase::computeDistance( const SelfType &other ) const { #ifndef JSON_VALUE_USE_INTERNAL_MAP # ifdef JSON_USE_CPPTL_SMALLMAP return current_ - other.current_; # else // Iterator for null value are initialized using the default // constructor, which initialize current_ to the default // std::map::iterator. As begin() and end() are two instance // of the default std::map::iterator, they can not be compared. // To allow this, we handle this comparison specifically. if ( isNull_ && other.isNull_ ) { return 0; } // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, // which is the one used by default). // Using a portable hand-made version for non random iterator instead: // return difference_type( std::distance( current_, other.current_ ) ); difference_type myDistance = 0; for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it ) { ++myDistance; } return myDistance; # endif #else if ( isArray_ ) return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); #endif } bool ValueIteratorBase::isEqual( const SelfType &other ) const { #ifndef JSON_VALUE_USE_INTERNAL_MAP if ( isNull_ ) { return other.isNull_; } return current_ == other.current_; #else if ( isArray_ ) return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); #endif } void ValueIteratorBase::copy( const SelfType &other ) { #ifndef JSON_VALUE_USE_INTERNAL_MAP current_ = other.current_; #else if ( isArray_ ) iterator_.array_ = other.iterator_.array_; iterator_.map_ = other.iterator_.map_; #endif } Value ValueIteratorBase::key() const { #ifndef JSON_VALUE_USE_INTERNAL_MAP const Value::CZString czstring = (*current_).first; if ( czstring.c_str() ) { if ( czstring.isStaticString() ) return Value( StaticString( czstring.c_str() ) ); return Value( czstring.c_str() ); } return Value( czstring.index() ); #else if ( isArray_ ) return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); bool isStatic; const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); if ( isStatic ) return Value( StaticString( memberName ) ); return Value( memberName ); #endif } UInt ValueIteratorBase::index() const { #ifndef JSON_VALUE_USE_INTERNAL_MAP const Value::CZString czstring = (*current_).first; if ( !czstring.c_str() ) return czstring.index(); return Value::UInt( -1 ); #else if ( isArray_ ) return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); return Value::UInt( -1 ); #endif } const char * ValueIteratorBase::memberName() const { #ifndef JSON_VALUE_USE_INTERNAL_MAP const char *name = (*current_).first.c_str(); return name ? name : ""; #else if ( !isArray_ ) return ValueInternalMap::key( iterator_.map_ ); return ""; #endif } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class ValueConstIterator // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// ValueConstIterator::ValueConstIterator() { } #ifndef JSON_VALUE_USE_INTERNAL_MAP ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) : ValueIteratorBase( current ) { } #else ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) : ValueIteratorBase( state ) { } ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) : ValueIteratorBase( state ) { } #endif ValueConstIterator & ValueConstIterator::operator =( const ValueIteratorBase &other ) { copy( other ); return *this; } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class ValueIterator // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// ValueIterator::ValueIterator() { } #ifndef JSON_VALUE_USE_INTERNAL_MAP ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) : ValueIteratorBase( current ) { } #else ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) : ValueIteratorBase( state ) { } ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) : ValueIteratorBase( state ) { } #endif ValueIterator::ValueIterator( const ValueConstIterator &other ) : ValueIteratorBase( other ) { } ValueIterator::ValueIterator( const ValueIterator &other ) : ValueIteratorBase( other ) { } ValueIterator & ValueIterator::operator =( const SelfType &other ) { copy( other ); return *this; } } // namespace Json // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_valueiterator.inl // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: src/lib_json/json_value.cpp // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) # include # include # ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR # include "json_batchallocator.h" # endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include #include #include #ifdef JSON_USE_CPPTL # include #endif #include // size_t #define JSON_ASSERT_UNREACHABLE assert( false ) #define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw #define JSON_FAIL_MESSAGE( message ) throw std::runtime_error( message ); #define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) JSON_FAIL_MESSAGE( message ) namespace Json { const Value Value::null; const Int Value::minInt = Int( ~(UInt(-1)/2) ); const Int Value::maxInt = Int( UInt(-1)/2 ); const UInt Value::maxUInt = UInt(-1); const Int64 Value::minInt64 = Int64( ~(UInt64(-1)/2) ); const Int64 Value::maxInt64 = Int64( UInt64(-1)/2 ); const UInt64 Value::maxUInt64 = UInt64(-1); const LargestInt Value::minLargestInt = LargestInt( ~(LargestUInt(-1)/2) ); const LargestInt Value::maxLargestInt = LargestInt( LargestUInt(-1)/2 ); const LargestUInt Value::maxLargestUInt = LargestUInt(-1); /// Unknown size marker static const unsigned int unknown = -1; /** Duplicates the specified string value. * @param value Pointer to the string to duplicate. Must be zero-terminated if * length is "unknown". * @param length Length of the value. if equals to unknown, then it will be * computed using strlen(value). * @return Pointer on the duplicate instance of string. */ static inline char * duplicateStringValue( const char *value, unsigned int length = unknown ) { if ( length == unknown ) length = static_cast(strlen(value)); char *newString = static_cast( malloc( length + 1 ) ); - JSON_ASSERT_MESSAGE( newString != 0, "Failed to allocate string value buffer" ); + JSON_ASSERT_MESSAGE( newString != nullptr, "Failed to allocate string value buffer" ); memcpy( newString, value, length ); newString[length] = 0; return newString; } /** Free the string duplicated by duplicateStringValue(). */ static inline void releaseStringValue( char *value ) { if ( value ) free( value ); } } // namespace Json // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ValueInternals... // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// #if !defined(JSON_IS_AMALGAMATION) # ifdef JSON_VALUE_USE_INTERNAL_MAP # include "json_internalarray.inl" # include "json_internalmap.inl" # endif // JSON_VALUE_USE_INTERNAL_MAP # include "json_valueiterator.inl" #endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class Value::CommentInfo // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// Value::CommentInfo::CommentInfo() - : comment_( 0 ) + : comment_( nullptr ) { } Value::CommentInfo::~CommentInfo() { if ( comment_ ) releaseStringValue( comment_ ); } void Value::CommentInfo::setComment( const char *text ) { if ( comment_ ) releaseStringValue( comment_ ); - JSON_ASSERT( text != 0 ); + JSON_ASSERT( text != nullptr ); JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /"); // It seems that /**/ style comments are acceptable as well. comment_ = duplicateStringValue( text ); } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class Value::CZString // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// # ifndef JSON_VALUE_USE_INTERNAL_MAP // Notes: index_ indicates if the string was allocated when // a string is stored. Value::CZString::CZString( ArrayIndex index ) - : cstr_( 0 ) + : cstr_( nullptr ) , index_( index ) { } Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate ) : cstr_( allocate == duplicate ? duplicateStringValue(cstr) : cstr ) , index_( allocate ) { } Value::CZString::CZString( const CZString &other ) -: cstr_( other.index_ != noDuplication && other.cstr_ != 0 +: cstr_( other.index_ != noDuplication && other.cstr_ != nullptr ? duplicateStringValue( other.cstr_ ) : other.cstr_ ) , index_( other.cstr_ ? (other.index_ == noDuplication ? static_cast(noDuplication) : static_cast(duplicate)) : other.index_ ) { } Value::CZString::~CZString() { if ( cstr_ && index_ == duplicate ) releaseStringValue( const_cast( cstr_ ) ); } void Value::CZString::swap( CZString &other ) { std::swap( cstr_, other.cstr_ ); std::swap( index_, other.index_ ); } Value::CZString & Value::CZString::operator =( const CZString &other ) { CZString temp( other ); swap( temp ); return *this; } bool Value::CZString::operator<( const CZString &other ) const { if ( cstr_ ) return strcmp( cstr_, other.cstr_ ) < 0; return index_ < other.index_; } bool Value::CZString::operator==( const CZString &other ) const { if ( cstr_ ) return strcmp( cstr_, other.cstr_ ) == 0; return index_ == other.index_; } ArrayIndex Value::CZString::index() const { return index_; } const char * Value::CZString::c_str() const { return cstr_; } bool Value::CZString::isStaticString() const { return index_ == noDuplication; } #endif // ifndef JSON_VALUE_USE_INTERNAL_MAP // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class Value::Value // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// /*! \internal Default constructor initialization must be equivalent to: * memset( this, 0, sizeof(Value) ) * This optimization is used in ValueInternalMap fast allocator. */ Value::Value( ValueType type ) : type_( type ) , allocated_( 0 ) - , comments_( 0 ) + , comments_( nullptr ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { switch ( type ) { case nullValue: break; case intValue: case uintValue: value_.int_ = 0; break; case realValue: value_.real_ = 0.0; break; case stringValue: - value_.string_ = 0; + value_.string_ = nullptr; break; #ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: value_.map_ = new ObjectValues(); break; #else case arrayValue: value_.array_ = arrayAllocator()->newArray(); break; case objectValue: value_.map_ = mapAllocator()->newMap(); break; #endif case booleanValue: value_.bool_ = false; break; default: JSON_ASSERT_UNREACHABLE; } } #if defined(JSON_HAS_INT64) Value::Value( UInt value ) : type_( uintValue ) - , comments_( 0 ) + , comments_( nullptr ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.uint_ = value; } Value::Value( Int value ) : type_( intValue ) - , comments_( 0 ) + , comments_( nullptr ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.int_ = value; } #endif // if defined(JSON_HAS_INT64) Value::Value( Int64 value ) : type_( intValue ) - , comments_( 0 ) + , comments_( nullptr ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.int_ = value; } Value::Value( UInt64 value ) : type_( uintValue ) - , comments_( 0 ) + , comments_( nullptr ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.uint_ = value; } Value::Value( double value ) : type_( realValue ) - , comments_( 0 ) + , comments_( nullptr ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.real_ = value; } Value::Value( const char *value ) : type_( stringValue ) , allocated_( true ) - , comments_( 0 ) + , comments_( nullptr ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.string_ = duplicateStringValue( value ); } Value::Value( const char *beginValue, const char *endValue ) : type_( stringValue ) , allocated_( true ) - , comments_( 0 ) + , comments_( nullptr ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.string_ = duplicateStringValue( beginValue, static_cast(endValue - beginValue) ); } Value::Value( const std::string &value ) : type_( stringValue ) , allocated_( true ) - , comments_( 0 ) + , comments_( nullptr ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.string_ = duplicateStringValue( value.c_str(), static_cast(value.length()) ); } Value::Value( const StaticString &value ) : type_( stringValue ) , allocated_( false ) - , comments_( 0 ) + , comments_( nullptr ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.string_ = const_cast( value.c_str() ); } # ifdef JSON_USE_CPPTL Value::Value( const CppTL::ConstString &value ) : type_( stringValue ) , allocated_( true ) , comments_( 0 ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.string_ = duplicateStringValue( value, value.length() ); } # endif Value::Value( bool value ) : type_( booleanValue ) - , comments_( 0 ) + , comments_( nullptr ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { value_.bool_ = value; } Value::Value( const Value &other ) : type_( other.type_ ) - , comments_( 0 ) + , comments_( nullptr ) # ifdef JSON_VALUE_USE_INTERNAL_MAP , itemIsUsed_( 0 ) #endif { switch ( type_ ) { case nullValue: case intValue: case uintValue: case realValue: case booleanValue: value_ = other.value_; break; case stringValue: if ( other.value_.string_ ) { value_.string_ = duplicateStringValue( other.value_.string_ ); allocated_ = true; } else - value_.string_ = 0; + value_.string_ = nullptr; break; #ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: value_.map_ = new ObjectValues( *other.value_.map_ ); break; #else case arrayValue: value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ ); break; case objectValue: value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ ); break; #endif default: JSON_ASSERT_UNREACHABLE; } if ( other.comments_ ) { comments_ = new CommentInfo[numberOfCommentPlacement]; for ( int comment =0; comment < numberOfCommentPlacement; ++comment ) { const CommentInfo &otherComment = other.comments_[comment]; if ( otherComment.comment_ ) comments_[comment].setComment( otherComment.comment_ ); } } } Value::~Value() { switch ( type_ ) { case nullValue: case intValue: case uintValue: case realValue: case booleanValue: break; case stringValue: if ( allocated_ ) releaseStringValue( value_.string_ ); break; #ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: delete value_.map_; break; #else case arrayValue: arrayAllocator()->destructArray( value_.array_ ); break; case objectValue: mapAllocator()->destructMap( value_.map_ ); break; #endif default: JSON_ASSERT_UNREACHABLE; } if ( comments_ ) delete[] comments_; } Value & Value::operator=( const Value &other ) { Value temp( other ); swap( temp ); return *this; } void Value::swap( Value &other ) { ValueType temp = type_; type_ = other.type_; other.type_ = temp; std::swap( value_, other.value_ ); int temp2 = allocated_; allocated_ = other.allocated_; other.allocated_ = temp2; } ValueType Value::type() const { return type_; } int Value::compare( const Value &other ) const { if ( *this < other ) return -1; if ( *this > other ) return 1; return 0; } bool Value::operator <( const Value &other ) const { int typeDelta = type_ - other.type_; if ( typeDelta ) return typeDelta < 0 ? true : false; switch ( type_ ) { case nullValue: return false; case intValue: return value_.int_ < other.value_.int_; case uintValue: return value_.uint_ < other.value_.uint_; case realValue: return value_.real_ < other.value_.real_; case booleanValue: return value_.bool_ < other.value_.bool_; case stringValue: - return ( value_.string_ == 0 && other.value_.string_ ) + return ( value_.string_ == nullptr && other.value_.string_ ) || ( other.value_.string_ && value_.string_ && strcmp( value_.string_, other.value_.string_ ) < 0 ); #ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: { int delta = int( value_.map_->size() - other.value_.map_->size() ); if ( delta ) return delta < 0; return (*value_.map_) < (*other.value_.map_); } #else case arrayValue: return value_.array_->compare( *(other.value_.array_) ) < 0; case objectValue: return value_.map_->compare( *(other.value_.map_) ) < 0; #endif default: JSON_ASSERT_UNREACHABLE; } return false; // unreachable } bool Value::operator <=( const Value &other ) const { return !(other < *this); } bool Value::operator >=( const Value &other ) const { return !(*this < other); } bool Value::operator >( const Value &other ) const { return other < *this; } bool Value::operator ==( const Value &other ) const { //if ( type_ != other.type_ ) // GCC 2.95.3 says: // attempt to take address of bit-field structure member `Json::Value::type_' // Beats me, but a temp solves the problem. int temp = other.type_; if ( type_ != temp ) return false; switch ( type_ ) { case nullValue: return true; case intValue: return value_.int_ == other.value_.int_; case uintValue: return value_.uint_ == other.value_.uint_; case realValue: return value_.real_ == other.value_.real_; case booleanValue: return value_.bool_ == other.value_.bool_; case stringValue: return ( value_.string_ == other.value_.string_ ) || ( other.value_.string_ && value_.string_ && strcmp( value_.string_, other.value_.string_ ) == 0 ); #ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: return value_.map_->size() == other.value_.map_->size() && (*value_.map_) == (*other.value_.map_); #else case arrayValue: return value_.array_->compare( *(other.value_.array_) ) == 0; case objectValue: return value_.map_->compare( *(other.value_.map_) ) == 0; #endif default: JSON_ASSERT_UNREACHABLE; } return false; // unreachable } bool Value::operator !=( const Value &other ) const { return !( *this == other ); } const char * Value::asCString() const { JSON_ASSERT( type_ == stringValue ); return value_.string_; } std::string Value::asString() const { switch ( type_ ) { case nullValue: return ""; case stringValue: return value_.string_ ? value_.string_ : ""; case booleanValue: return value_.bool_ ? "true" : "false"; case intValue: case uintValue: case realValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to string" ); default: JSON_ASSERT_UNREACHABLE; } return ""; // unreachable } # ifdef JSON_USE_CPPTL CppTL::ConstString Value::asConstString() const { return CppTL::ConstString( asString().c_str() ); } # endif Value::Int Value::asInt() const { switch ( type_ ) { case nullValue: return 0; case intValue: JSON_ASSERT_MESSAGE( value_.int_ >= minInt && value_.int_ <= maxInt, "unsigned integer out of signed int range" ); return Int(value_.int_); case uintValue: JSON_ASSERT_MESSAGE( value_.uint_ <= UInt(maxInt), "unsigned integer out of signed int range" ); return Int(value_.uint_); case realValue: JSON_ASSERT_MESSAGE( value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range" ); return Int( value_.real_ ); case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to int" ); default: JSON_ASSERT_UNREACHABLE; } return 0; // unreachable; } Value::UInt Value::asUInt() const { switch ( type_ ) { case nullValue: return 0; case intValue: JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to unsigned integer" ); JSON_ASSERT_MESSAGE( value_.int_ <= maxUInt, "signed integer out of UInt range" ); return UInt(value_.int_); case uintValue: JSON_ASSERT_MESSAGE( value_.uint_ <= maxUInt, "unsigned integer out of UInt range" ); return UInt(value_.uint_); case realValue: JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range" ); return UInt( value_.real_ ); case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to uint" ); default: JSON_ASSERT_UNREACHABLE; } return 0; // unreachable; } # if defined(JSON_HAS_INT64) Value::Int64 Value::asInt64() const { switch ( type_ ) { case nullValue: return 0; case intValue: return value_.int_; case uintValue: JSON_ASSERT_MESSAGE( value_.uint_ <= UInt64(maxInt64), "unsigned integer out of Int64 range" ); return value_.uint_; case realValue: JSON_ASSERT_MESSAGE( value_.real_ >= minInt64 && value_.real_ <= maxInt64, "Real out of Int64 range" ); return Int( value_.real_ ); case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to Int64" ); default: JSON_ASSERT_UNREACHABLE; } return 0; // unreachable; } Value::UInt64 Value::asUInt64() const { switch ( type_ ) { case nullValue: return 0; case intValue: JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to UInt64" ); return value_.int_; case uintValue: return value_.uint_; case realValue: JSON_ASSERT_MESSAGE( value_.real_ >= 0 && value_.real_ <= maxUInt64, "Real out of UInt64 range" ); return UInt( value_.real_ ); case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to UInt64" ); default: JSON_ASSERT_UNREACHABLE; } return 0; // unreachable; } # endif // if defined(JSON_HAS_INT64) LargestInt Value::asLargestInt() const { #if defined(JSON_NO_INT64) return asInt(); #else return asInt64(); #endif } LargestUInt Value::asLargestUInt() const { #if defined(JSON_NO_INT64) return asUInt(); #else return asUInt64(); #endif } double Value::asDouble() const { switch ( type_ ) { case nullValue: return 0.0; case intValue: return static_cast( value_.int_ ); case uintValue: #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) return static_cast( value_.uint_ ); #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) return static_cast( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1); #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) case realValue: return value_.real_; case booleanValue: return value_.bool_ ? 1.0 : 0.0; case stringValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to double" ); default: JSON_ASSERT_UNREACHABLE; } return 0; // unreachable; } float Value::asFloat() const { switch ( type_ ) { case nullValue: return 0.0f; case intValue: return static_cast( value_.int_ ); case uintValue: #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) return static_cast( value_.uint_ ); #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) return static_cast( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1); #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) case realValue: return static_cast( value_.real_ ); case booleanValue: return value_.bool_ ? 1.0f : 0.0f; case stringValue: case arrayValue: case objectValue: JSON_FAIL_MESSAGE( "Type is not convertible to float" ); default: JSON_ASSERT_UNREACHABLE; } return 0.0f; // unreachable; } bool Value::asBool() const { switch ( type_ ) { case nullValue: return false; case intValue: case uintValue: return value_.int_ != 0; case realValue: return value_.real_ != 0.0; case booleanValue: return value_.bool_; case stringValue: return value_.string_ && value_.string_[0] != 0; case arrayValue: case objectValue: return value_.map_->size() != 0; default: JSON_ASSERT_UNREACHABLE; } return false; // unreachable; } bool Value::isConvertibleTo( ValueType other ) const { switch ( type_ ) { case nullValue: return true; case intValue: return ( other == nullValue && value_.int_ == 0 ) || other == intValue || ( other == uintValue && value_.int_ >= 0 ) || other == realValue || other == stringValue || other == booleanValue; case uintValue: return ( other == nullValue && value_.uint_ == 0 ) || ( other == intValue && value_.uint_ <= static_cast(maxInt) ) || other == uintValue || other == realValue || other == stringValue || other == booleanValue; case realValue: return ( other == nullValue && value_.real_ == 0.0 ) || ( other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt ) || ( other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt ) || other == realValue || other == stringValue || other == booleanValue; case booleanValue: return ( other == nullValue && value_.bool_ == false ) || other == intValue || other == uintValue || other == realValue || other == stringValue || other == booleanValue; case stringValue: return other == stringValue || ( other == nullValue && (!value_.string_ || value_.string_[0] == 0) ); case arrayValue: return other == arrayValue || ( other == nullValue && value_.map_->size() == 0 ); case objectValue: return other == objectValue || ( other == nullValue && value_.map_->size() == 0 ); default: JSON_ASSERT_UNREACHABLE; } return false; // unreachable; } /// Number of values in array or object ArrayIndex Value::size() const { switch ( type_ ) { case nullValue: case intValue: case uintValue: case realValue: case booleanValue: case stringValue: return 0; #ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: // size of the array is highest index + 1 if ( !value_.map_->empty() ) { ObjectValues::const_iterator itLast = value_.map_->end(); --itLast; return (*itLast).first.index()+1; } return 0; case objectValue: return ArrayIndex( value_.map_->size() ); #else case arrayValue: return Int( value_.array_->size() ); case objectValue: return Int( value_.map_->size() ); #endif default: JSON_ASSERT_UNREACHABLE; } return 0; // unreachable; } bool Value::empty() const { if ( isNull() || isArray() || isObject() ) return size() == 0u; else return false; } bool Value::operator!() const { return isNull(); } void Value::clear() { JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue ); switch ( type_ ) { #ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: value_.map_->clear(); break; #else case arrayValue: value_.array_->clear(); break; case objectValue: value_.map_->clear(); break; #endif default: break; } } void Value::resize( ArrayIndex newSize ) { JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); if ( type_ == nullValue ) *this = Value( arrayValue ); #ifndef JSON_VALUE_USE_INTERNAL_MAP ArrayIndex oldSize = size(); if ( newSize == 0 ) clear(); else if ( newSize > oldSize ) (*this)[ newSize - 1 ]; else { for ( ArrayIndex index = newSize; index < oldSize; ++index ) { value_.map_->erase( index ); } assert( size() == newSize ); } #else value_.array_->resize( newSize ); #endif } Value & Value::operator[]( ArrayIndex index ) { JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); if ( type_ == nullValue ) *this = Value( arrayValue ); #ifndef JSON_VALUE_USE_INTERNAL_MAP CZString key( index ); ObjectValues::iterator it = value_.map_->lower_bound( key ); if ( it != value_.map_->end() && (*it).first == key ) return (*it).second; ObjectValues::value_type defaultValue( key, null ); it = value_.map_->insert( it, defaultValue ); return (*it).second; #else return value_.array_->resolveReference( index ); #endif } Value & Value::operator[]( int index ) { JSON_ASSERT( index >= 0 ); return (*this)[ ArrayIndex(index) ]; } const Value & Value::operator[]( ArrayIndex index ) const { JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); if ( type_ == nullValue ) return null; #ifndef JSON_VALUE_USE_INTERNAL_MAP CZString key( index ); ObjectValues::const_iterator it = value_.map_->find( key ); if ( it == value_.map_->end() ) return null; return (*it).second; #else Value *value = value_.array_->find( index ); return value ? *value : null; #endif } const Value & Value::operator[]( int index ) const { JSON_ASSERT( index >= 0 ); return (*this)[ ArrayIndex(index) ]; } Value & Value::operator[]( const char *key ) { return resolveReference( key, false ); } Value & Value::resolveReference( const char *key, bool isStatic ) { JSON_ASSERT( type_ == nullValue || type_ == objectValue ); if ( type_ == nullValue ) *this = Value( objectValue ); #ifndef JSON_VALUE_USE_INTERNAL_MAP CZString actualKey( key, isStatic ? CZString::noDuplication : CZString::duplicateOnCopy ); ObjectValues::iterator it = value_.map_->lower_bound( actualKey ); if ( it != value_.map_->end() && (*it).first == actualKey ) return (*it).second; ObjectValues::value_type defaultValue( actualKey, null ); it = value_.map_->insert( it, defaultValue ); Value &value = (*it).second; return value; #else return value_.map_->resolveReference( key, isStatic ); #endif } Value Value::get( ArrayIndex index, const Value &defaultValue ) const { const Value *value = &((*this)[index]); return value == &null ? defaultValue : *value; } bool Value::isValidIndex( ArrayIndex index ) const { return index < size(); } const Value & Value::operator[]( const char *key ) const { JSON_ASSERT( type_ == nullValue || type_ == objectValue ); if ( type_ == nullValue ) return null; #ifndef JSON_VALUE_USE_INTERNAL_MAP CZString actualKey( key, CZString::noDuplication ); ObjectValues::const_iterator it = value_.map_->find( actualKey ); if ( it == value_.map_->end() ) return null; return (*it).second; #else const Value *value = value_.map_->find( key ); return value ? *value : null; #endif } Value & Value::operator[]( const std::string &key ) { return (*this)[ key.c_str() ]; } const Value & Value::operator[]( const std::string &key ) const { return (*this)[ key.c_str() ]; } Value & Value::operator[]( const StaticString &key ) { return resolveReference( key, true ); } # ifdef JSON_USE_CPPTL Value & Value::operator[]( const CppTL::ConstString &key ) { return (*this)[ key.c_str() ]; } const Value & Value::operator[]( const CppTL::ConstString &key ) const { return (*this)[ key.c_str() ]; } # endif Value & Value::append( const Value &value ) { return (*this)[size()] = value; } Value Value::get( const char *key, const Value &defaultValue ) const { const Value *value = &((*this)[key]); return value == &null ? defaultValue : *value; } Value Value::get( const std::string &key, const Value &defaultValue ) const { return get( key.c_str(), defaultValue ); } Value Value::removeMember( const char* key ) { JSON_ASSERT( type_ == nullValue || type_ == objectValue ); if ( type_ == nullValue ) return null; #ifndef JSON_VALUE_USE_INTERNAL_MAP CZString actualKey( key, CZString::noDuplication ); ObjectValues::iterator it = value_.map_->find( actualKey ); if ( it == value_.map_->end() ) return null; Value old(it->second); value_.map_->erase(it); return old; #else Value *value = value_.map_->find( key ); if (value){ Value old(*value); value_.map_.remove( key ); return old; } else { return null; } #endif } Value Value::removeMember( const std::string &key ) { return removeMember( key.c_str() ); } # ifdef JSON_USE_CPPTL Value Value::get( const CppTL::ConstString &key, const Value &defaultValue ) const { return get( key.c_str(), defaultValue ); } # endif bool Value::isMember( const char *key ) const { const Value *value = &((*this)[key]); return value != &null; } bool Value::isMember( const std::string &key ) const { return isMember( key.c_str() ); } # ifdef JSON_USE_CPPTL bool Value::isMember( const CppTL::ConstString &key ) const { return isMember( key.c_str() ); } #endif Value::Members Value::getMemberNames() const { JSON_ASSERT( type_ == nullValue || type_ == objectValue ); if ( type_ == nullValue ) return Value::Members(); Members members; members.reserve( value_.map_->size() ); #ifndef JSON_VALUE_USE_INTERNAL_MAP ObjectValues::const_iterator it = value_.map_->begin(); ObjectValues::const_iterator itEnd = value_.map_->end(); for ( ; it != itEnd; ++it ) members.push_back( std::string( (*it).first.c_str() ) ); #else ValueInternalMap::IteratorState it; ValueInternalMap::IteratorState itEnd; value_.map_->makeBeginIterator( it ); value_.map_->makeEndIterator( itEnd ); for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) ) members.push_back( std::string( ValueInternalMap::key( it ) ) ); #endif return members; } // //# ifdef JSON_USE_CPPTL //EnumMemberNames //Value::enumMemberNames() const //{ // if ( type_ == objectValue ) // { // return CppTL::Enum::any( CppTL::Enum::transform( // CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), // MemberNamesTransform() ) ); // } // return EnumMemberNames(); //} // // //EnumValues //Value::enumValues() const //{ // if ( type_ == objectValue || type_ == arrayValue ) // return CppTL::Enum::anyValues( *(value_.map_), // CppTL::Type() ); // return EnumValues(); //} // //# endif bool Value::isNull() const { return type_ == nullValue; } bool Value::isBool() const { return type_ == booleanValue; } bool Value::isInt() const { return type_ == intValue; } bool Value::isUInt() const { return type_ == uintValue; } bool Value::isIntegral() const { return type_ == intValue || type_ == uintValue || type_ == booleanValue; } bool Value::isDouble() const { return type_ == realValue; } bool Value::isNumeric() const { return isIntegral() || isDouble(); } bool Value::isString() const { return type_ == stringValue; } bool Value::isArray() const { return type_ == nullValue || type_ == arrayValue; } bool Value::isObject() const { return type_ == nullValue || type_ == objectValue; } void Value::setComment( const char *comment, CommentPlacement placement ) { if ( !comments_ ) comments_ = new CommentInfo[numberOfCommentPlacement]; comments_[placement].setComment( comment ); } void Value::setComment( const std::string &comment, CommentPlacement placement ) { setComment( comment.c_str(), placement ); } bool Value::hasComment( CommentPlacement placement ) const { - return comments_ != 0 && comments_[placement].comment_ != 0; + return comments_ != nullptr && comments_[placement].comment_ != nullptr; } std::string Value::getComment( CommentPlacement placement ) const { if ( hasComment(placement) ) return comments_[placement].comment_; return ""; } std::string Value::toStyledString() const { StyledWriter writer; return writer.write( *this ); } Value::const_iterator Value::begin() const { switch ( type_ ) { #ifdef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: if ( value_.array_ ) { ValueInternalArray::IteratorState it; value_.array_->makeBeginIterator( it ); return const_iterator( it ); } break; case objectValue: if ( value_.map_ ) { ValueInternalMap::IteratorState it; value_.map_->makeBeginIterator( it ); return const_iterator( it ); } break; #else case arrayValue: case objectValue: if ( value_.map_ ) return const_iterator( value_.map_->begin() ); break; #endif default: break; } return const_iterator(); } Value::const_iterator Value::end() const { switch ( type_ ) { #ifdef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: if ( value_.array_ ) { ValueInternalArray::IteratorState it; value_.array_->makeEndIterator( it ); return const_iterator( it ); } break; case objectValue: if ( value_.map_ ) { ValueInternalMap::IteratorState it; value_.map_->makeEndIterator( it ); return const_iterator( it ); } break; #else case arrayValue: case objectValue: if ( value_.map_ ) return const_iterator( value_.map_->end() ); break; #endif default: break; } return const_iterator(); } Value::iterator Value::begin() { switch ( type_ ) { #ifdef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: if ( value_.array_ ) { ValueInternalArray::IteratorState it; value_.array_->makeBeginIterator( it ); return iterator( it ); } break; case objectValue: if ( value_.map_ ) { ValueInternalMap::IteratorState it; value_.map_->makeBeginIterator( it ); return iterator( it ); } break; #else case arrayValue: case objectValue: if ( value_.map_ ) return iterator( value_.map_->begin() ); break; #endif default: break; } return iterator(); } Value::iterator Value::end() { switch ( type_ ) { #ifdef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: if ( value_.array_ ) { ValueInternalArray::IteratorState it; value_.array_->makeEndIterator( it ); return iterator( it ); } break; case objectValue: if ( value_.map_ ) { ValueInternalMap::IteratorState it; value_.map_->makeEndIterator( it ); return iterator( it ); } break; #else case arrayValue: case objectValue: if ( value_.map_ ) return iterator( value_.map_->end() ); break; #endif default: break; } return iterator(); } // class PathArgument // ////////////////////////////////////////////////////////////////// PathArgument::PathArgument() : kind_( kindNone ) { } PathArgument::PathArgument( ArrayIndex index ) : index_( index ) , kind_( kindIndex ) { } PathArgument::PathArgument( const char *key ) : key_( key ) , kind_( kindKey ) { } PathArgument::PathArgument( const std::string &key ) : key_( key.c_str() ) , kind_( kindKey ) { } // class Path // ////////////////////////////////////////////////////////////////// Path::Path( const std::string &path, const PathArgument &a1, const PathArgument &a2, const PathArgument &a3, const PathArgument &a4, const PathArgument &a5 ) { InArgs in; in.push_back( &a1 ); in.push_back( &a2 ); in.push_back( &a3 ); in.push_back( &a4 ); in.push_back( &a5 ); makePath( path, in ); } void Path::makePath( const std::string &path, const InArgs &in ) { const char *current = path.c_str(); const char *end = current + path.length(); InArgs::const_iterator itInArg = in.begin(); while ( current != end ) { if ( *current == '[' ) { ++current; if ( *current == '%' ) addPathInArg( path, in, itInArg, PathArgument::kindIndex ); else { ArrayIndex index = 0; for ( ; current != end && *current >= '0' && *current <= '9'; ++current ) index = index * 10 + ArrayIndex(*current - '0'); args_.push_back( index ); } if ( current == end || *current++ != ']' ) invalidPath( path, int(current - path.c_str()) ); } else if ( *current == '%' ) { addPathInArg( path, in, itInArg, PathArgument::kindKey ); ++current; } else if ( *current == '.' ) { ++current; } else { const char *beginName = current; while ( current != end && !strchr( "[.", *current ) ) ++current; args_.push_back( std::string( beginName, current ) ); } } } void Path::addPathInArg( const std::string &/*path*/, const InArgs &in, InArgs::const_iterator &itInArg, PathArgument::Kind kind ) { if ( itInArg == in.end() ) { // Error: missing argument %d } else if ( (*itInArg)->kind_ != kind ) { // Error: bad argument type } else { args_.push_back( **itInArg ); } } void Path::invalidPath( const std::string &/*path*/, int /*location*/ ) { // Error: invalid path. } const Value & Path::resolve( const Value &root ) const { const Value *node = &root; for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) { const PathArgument &arg = *it; if ( arg.kind_ == PathArgument::kindIndex ) { if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) { // Error: unable to resolve path (array value expected at position... } node = &((*node)[arg.index_]); } else if ( arg.kind_ == PathArgument::kindKey ) { if ( !node->isObject() ) { // Error: unable to resolve path (object value expected at position...) } node = &((*node)[arg.key_]); if ( node == &Value::null ) { // Error: unable to resolve path (object has no member named '' at position...) } } } return *node; } Value Path::resolve( const Value &root, const Value &defaultValue ) const { const Value *node = &root; for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) { const PathArgument &arg = *it; if ( arg.kind_ == PathArgument::kindIndex ) { if ( !node->isArray() || node->isValidIndex( arg.index_ ) ) return defaultValue; node = &((*node)[arg.index_]); } else if ( arg.kind_ == PathArgument::kindKey ) { if ( !node->isObject() ) return defaultValue; node = &((*node)[arg.key_]); if ( node == &Value::null ) return defaultValue; } } return *node; } Value & Path::make( Value &root ) const { Value *node = &root; for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) { const PathArgument &arg = *it; if ( arg.kind_ == PathArgument::kindIndex ) { if ( !node->isArray() ) { // Error: node is not an array at position ... } node = &((*node)[arg.index_]); } else if ( arg.kind_ == PathArgument::kindKey ) { if ( !node->isObject() ) { // Error: node is not an object at position... } node = &((*node)[arg.key_]); } } return *node; } } // namespace Json // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_value.cpp // ////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////// // Beginning of content of file: src/lib_json/json_writer.cpp // ////////////////////////////////////////////////////////////////////// // Copyright 2007-2010 Baptiste Lepilleur // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE #if !defined(JSON_IS_AMALGAMATION) # include # include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) #include #include #include #include #include #include #include #if _MSC_VER >= 1400 // VC++ 8.0 #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. #endif namespace Json { static bool containsControlCharacter( const char* str ) { while ( *str ) { if ( isControlCharacter( *(str++) ) ) return true; } return false; } std::string valueToString( LargestInt value ) { UIntToStringBuffer buffer; char *current = buffer + sizeof(buffer); bool isNegative = value < 0; if ( isNegative ) value = -value; uintToString( LargestUInt(value), current ); if ( isNegative ) *--current = '-'; assert( current >= buffer ); return current; } std::string valueToString( LargestUInt value ) { UIntToStringBuffer buffer; char *current = buffer + sizeof(buffer); uintToString( value, current ); assert( current >= buffer ); return current; } #if defined(JSON_HAS_INT64) std::string valueToString( Int value ) { return valueToString( LargestInt(value) ); } std::string valueToString( UInt value ) { return valueToString( LargestUInt(value) ); } #endif // # if defined(JSON_HAS_INT64) std::string valueToString( double value ) { char buffer[32]; #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. sprintf_s(buffer, sizeof(buffer), "%#.16g", value); #else sprintf(buffer, "%#.16g", value); #endif char* ch = buffer + strlen(buffer) - 1; if (*ch != '0') return buffer; // nothing to truncate, so save time while(ch > buffer && *ch == '0'){ --ch; } char* last_nonzero = ch; while(ch >= buffer){ switch(*ch){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': --ch; continue; case '.': // Truncate zeroes to save bytes in output, but keep one. *(last_nonzero+2) = '\0'; return buffer; default: return buffer; } } return buffer; } std::string valueToString( bool value ) { return value ? "true" : "false"; } std::string valueToQuotedString( const char *value ) { // Not sure how to handle unicode... if (strpbrk(value, "\"\\\b\f\n\r\t") == nullptr && !containsControlCharacter( value )) return std::string("\"") + value + "\""; // We have to walk value and escape any special characters. // Appending to std::string is not efficient, but this should be rare. // (Note: forward slashes are *not* rare, but I am not escaping them.) std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+nullptr std::string result; result.reserve(maxsize); // to avoid lots of mallocs result += "\""; for (const char* c=value; *c != 0; ++c) { switch(*c) { case '\"': result += "\\\""; break; case '\\': result += "\\\\"; break; case '\b': result += "\\b"; break; case '\f': result += "\\f"; break; case '\n': result += "\\n"; break; case '\r': result += "\\r"; break; case '\t': result += "\\t"; break; //case '/': // Even though \/ is considered a legal escape in JSON, a bare // slash is also legal, so I see no reason to escape it. // (I hope I am not misunderstanding something. // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); result += oss.str(); } else { result += *c; } break; } } result += "\""; return result; } // Class Writer // ////////////////////////////////////////////////////////////////// Writer::~Writer() { } // Class FastWriter // ////////////////////////////////////////////////////////////////// FastWriter::FastWriter() : yamlCompatiblityEnabled_( false ) { } void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; } std::string FastWriter::write( const Value &root ) { document_ = ""; writeValue( root ); document_ += "\n"; return document_; } void FastWriter::writeValue( const Value &value ) { switch ( value.type() ) { case nullValue: document_ += "null"; break; case intValue: document_ += valueToString( value.asLargestInt() ); break; case uintValue: document_ += valueToString( value.asLargestUInt() ); break; case realValue: document_ += valueToString( value.asDouble() ); break; case stringValue: document_ += valueToQuotedString( value.asCString() ); break; case booleanValue: document_ += valueToString( value.asBool() ); break; case arrayValue: { document_ += "["; int size = value.size(); for ( int index =0; index < size; ++index ) { if ( index > 0 ) document_ += ","; writeValue( value[index] ); } document_ += "]"; } break; case objectValue: { Value::Members members( value.getMemberNames() ); document_ += "{"; for ( Value::Members::iterator it = members.begin(); it != members.end(); ++it ) { const std::string &name = *it; if ( it != members.begin() ) document_ += ","; document_ += valueToQuotedString( name.c_str() ); document_ += yamlCompatiblityEnabled_ ? ": " : ":"; writeValue( value[name] ); } document_ += "}"; } break; } } // Class StyledWriter // ////////////////////////////////////////////////////////////////// StyledWriter::StyledWriter() : rightMargin_( 74 ) , indentSize_( 3 ) { } std::string StyledWriter::write( const Value &root ) { document_ = ""; addChildValues_ = false; indentString_ = ""; writeCommentBeforeValue( root ); writeValue( root ); writeCommentAfterValueOnSameLine( root ); document_ += "\n"; return document_; } void StyledWriter::writeValue( const Value &value ) { switch ( value.type() ) { case nullValue: pushValue( "null" ); break; case intValue: pushValue( valueToString( value.asLargestInt() ) ); break; case uintValue: pushValue( valueToString( value.asLargestUInt() ) ); break; case realValue: pushValue( valueToString( value.asDouble() ) ); break; case stringValue: pushValue( valueToQuotedString( value.asCString() ) ); break; case booleanValue: pushValue( valueToString( value.asBool() ) ); break; case arrayValue: writeArrayValue( value); break; case objectValue: { Value::Members members( value.getMemberNames() ); if ( members.empty() ) pushValue( "{}" ); else { writeWithIndent( "{" ); indent(); Value::Members::iterator it = members.begin(); for (;;) { const std::string &name = *it; const Value &childValue = value[name]; writeCommentBeforeValue( childValue ); writeWithIndent( valueToQuotedString( name.c_str() ) ); document_ += " : "; writeValue( childValue ); if ( ++it == members.end() ) { writeCommentAfterValueOnSameLine( childValue ); break; } document_ += ","; writeCommentAfterValueOnSameLine( childValue ); } unindent(); writeWithIndent( "}" ); } } break; } } void StyledWriter::writeArrayValue( const Value &value ) { unsigned size = value.size(); if ( size == 0 ) pushValue( "[]" ); else { bool isArrayMultiLine = isMultineArray( value ); if ( isArrayMultiLine ) { writeWithIndent( "[" ); indent(); bool hasChildValue = !childValues_.empty(); unsigned index =0; for (;;) { const Value &childValue = value[index]; writeCommentBeforeValue( childValue ); if ( hasChildValue ) writeWithIndent( childValues_[index] ); else { writeIndent(); writeValue( childValue ); } if ( ++index == size ) { writeCommentAfterValueOnSameLine( childValue ); break; } document_ += ","; writeCommentAfterValueOnSameLine( childValue ); } unindent(); writeWithIndent( "]" ); } else // output on a single line { assert( childValues_.size() == size ); document_ += "[ "; for ( unsigned index =0; index < size; ++index ) { if ( index > 0 ) document_ += ", "; document_ += childValues_[index]; } document_ += " ]"; } } } bool StyledWriter::isMultineArray( const Value &value ) { int size = value.size(); bool isMultiLine = size*3 >= rightMargin_ ; childValues_.clear(); for ( int index =0; index < size && !isMultiLine; ++index ) { const Value &childValue = value[index]; isMultiLine = isMultiLine || ( (childValue.isArray() || childValue.isObject()) && childValue.size() > 0 ); } if ( !isMultiLine ) // check if line length > max line length { childValues_.reserve( size ); addChildValues_ = true; int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' for ( int index =0; index < size && !isMultiLine; ++index ) { writeValue( value[index] ); lineLength += int( childValues_[index].length() ); isMultiLine = isMultiLine && hasCommentForValue( value[index] ); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; } return isMultiLine; } void StyledWriter::pushValue( const std::string &value ) { if ( addChildValues_ ) childValues_.push_back( value ); else document_ += value; } void StyledWriter::writeIndent() { if ( !document_.empty() ) { char last = document_[document_.length()-1]; if ( last == ' ' ) // already indented return; if ( last != '\n' ) // Comments may add new-line document_ += '\n'; } document_ += indentString_; } void StyledWriter::writeWithIndent( const std::string &value ) { writeIndent(); document_ += value; } void StyledWriter::indent() { indentString_ += std::string( indentSize_, ' ' ); } void StyledWriter::unindent() { assert( int(indentString_.size()) >= indentSize_ ); indentString_.resize( indentString_.size() - indentSize_ ); } void StyledWriter::writeCommentBeforeValue( const Value &root ) { if ( !root.hasComment( commentBefore ) ) return; document_ += normalizeEOL( root.getComment( commentBefore ) ); document_ += "\n"; } void StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) { if ( root.hasComment( commentAfterOnSameLine ) ) document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); if ( root.hasComment( commentAfter ) ) { document_ += "\n"; document_ += normalizeEOL( root.getComment( commentAfter ) ); document_ += "\n"; } } bool StyledWriter::hasCommentForValue( const Value &value ) { return value.hasComment( commentBefore ) || value.hasComment( commentAfterOnSameLine ) || value.hasComment( commentAfter ); } std::string StyledWriter::normalizeEOL( const std::string &text ) { std::string normalized; normalized.reserve( text.length() ); const char *begin = text.c_str(); const char *end = begin + text.length(); const char *current = begin; while ( current != end ) { char c = *current++; if ( c == '\r' ) // mac or dos EOL { if ( *current == '\n' ) // convert dos EOL ++current; normalized += '\n'; } else // handle unix EOL & other char normalized += c; } return normalized; } // Class StyledStreamWriter // ////////////////////////////////////////////////////////////////// StyledStreamWriter::StyledStreamWriter( std::string indentation ) : document_(nullptr) , rightMargin_( 74 ) , indentation_( indentation ) { } void StyledStreamWriter::write( std::ostream &out, const Value &root ) { document_ = &out; addChildValues_ = false; indentString_ = ""; writeCommentBeforeValue( root ); writeValue( root ); writeCommentAfterValueOnSameLine( root ); *document_ << "\n"; document_ = nullptr; // Forget the stream, for safety. } void StyledStreamWriter::writeValue( const Value &value ) { switch ( value.type() ) { case nullValue: pushValue( "null" ); break; case intValue: pushValue( valueToString( value.asLargestInt() ) ); break; case uintValue: pushValue( valueToString( value.asLargestUInt() ) ); break; case realValue: pushValue( valueToString( value.asDouble() ) ); break; case stringValue: pushValue( valueToQuotedString( value.asCString() ) ); break; case booleanValue: pushValue( valueToString( value.asBool() ) ); break; case arrayValue: writeArrayValue( value); break; case objectValue: { Value::Members members( value.getMemberNames() ); if ( members.empty() ) pushValue( "{}" ); else { writeWithIndent( "{" ); indent(); Value::Members::iterator it = members.begin(); for (;;) { const std::string &name = *it; const Value &childValue = value[name]; writeCommentBeforeValue( childValue ); writeWithIndent( valueToQuotedString( name.c_str() ) ); *document_ << " : "; writeValue( childValue ); if ( ++it == members.end() ) { writeCommentAfterValueOnSameLine( childValue ); break; } *document_ << ","; writeCommentAfterValueOnSameLine( childValue ); } unindent(); writeWithIndent( "}" ); } } break; } } void StyledStreamWriter::writeArrayValue( const Value &value ) { unsigned size = value.size(); if ( size == 0 ) pushValue( "[]" ); else { bool isArrayMultiLine = isMultineArray( value ); if ( isArrayMultiLine ) { writeWithIndent( "[" ); indent(); bool hasChildValue = !childValues_.empty(); unsigned index =0; for (;;) { const Value &childValue = value[index]; writeCommentBeforeValue( childValue ); if ( hasChildValue ) writeWithIndent( childValues_[index] ); else { writeIndent(); writeValue( childValue ); } if ( ++index == size ) { writeCommentAfterValueOnSameLine( childValue ); break; } *document_ << ","; writeCommentAfterValueOnSameLine( childValue ); } unindent(); writeWithIndent( "]" ); } else // output on a single line { assert( childValues_.size() == size ); *document_ << "[ "; for ( unsigned index =0; index < size; ++index ) { if ( index > 0 ) *document_ << ", "; *document_ << childValues_[index]; } *document_ << " ]"; } } } bool StyledStreamWriter::isMultineArray( const Value &value ) { int size = value.size(); bool isMultiLine = size*3 >= rightMargin_ ; childValues_.clear(); for ( int index =0; index < size && !isMultiLine; ++index ) { const Value &childValue = value[index]; isMultiLine = isMultiLine || ( (childValue.isArray() || childValue.isObject()) && childValue.size() > 0 ); } if ( !isMultiLine ) // check if line length > max line length { childValues_.reserve( size ); addChildValues_ = true; int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' for ( int index =0; index < size && !isMultiLine; ++index ) { writeValue( value[index] ); lineLength += int( childValues_[index].length() ); isMultiLine = isMultiLine && hasCommentForValue( value[index] ); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; } return isMultiLine; } void StyledStreamWriter::pushValue( const std::string &value ) { if ( addChildValues_ ) childValues_.push_back( value ); else *document_ << value; } void StyledStreamWriter::writeIndent() { /* Some comments in this method would have been nice. ;-) if ( !document_.empty() ) { char last = document_[document_.length()-1]; if ( last == ' ' ) // already indented return; if ( last != '\n' ) // Comments may add new-line *document_ << '\n'; } */ *document_ << '\n' << indentString_; } void StyledStreamWriter::writeWithIndent( const std::string &value ) { writeIndent(); *document_ << value; } void StyledStreamWriter::indent() { indentString_ += indentation_; } void StyledStreamWriter::unindent() { assert( indentString_.size() >= indentation_.size() ); indentString_.resize( indentString_.size() - indentation_.size() ); } void StyledStreamWriter::writeCommentBeforeValue( const Value &root ) { if ( !root.hasComment( commentBefore ) ) return; *document_ << normalizeEOL( root.getComment( commentBefore ) ); *document_ << "\n"; } void StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) { if ( root.hasComment( commentAfterOnSameLine ) ) *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); if ( root.hasComment( commentAfter ) ) { *document_ << "\n"; *document_ << normalizeEOL( root.getComment( commentAfter ) ); *document_ << "\n"; } } bool StyledStreamWriter::hasCommentForValue( const Value &value ) { return value.hasComment( commentBefore ) || value.hasComment( commentAfterOnSameLine ) || value.hasComment( commentAfter ); } std::string StyledStreamWriter::normalizeEOL( const std::string &text ) { std::string normalized; normalized.reserve( text.length() ); const char *begin = text.c_str(); const char *end = begin + text.length(); const char *current = begin; while ( current != end ) { char c = *current++; if ( c == '\r' ) // mac or dos EOL { if ( *current == '\n' ) // convert dos EOL ++current; normalized += '\n'; } else // handle unix EOL & other char normalized += c; } return normalized; } std::ostream& operator<<( std::ostream &sout, const Value &root ) { Json::StyledStreamWriter writer; writer.write(sout, root); return sout; } } // namespace Json // ////////////////////////////////////////////////////////////////////// // End of content of file: src/lib_json/json_writer.cpp // ////////////////////////////////////////////////////////////////////// diff --git a/Modules/LegacyGL/vtkGLMapperProp.cpp b/Modules/LegacyGL/vtkGLMapperProp.cpp index 5cf671c708..f36a787a19 100644 --- a/Modules/LegacyGL/vtkGLMapperProp.cpp +++ b/Modules/LegacyGL/vtkGLMapperProp.cpp @@ -1,68 +1,68 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "vtkGLMapperProp.h" #include vtkStandardNewMacro(vtkGLMapperProp); -vtkGLMapperProp::vtkGLMapperProp() : m_WrappedGLMapper(0), m_BaseRenderer(0) +vtkGLMapperProp::vtkGLMapperProp() : m_WrappedGLMapper(nullptr), m_BaseRenderer(nullptr) { } vtkGLMapperProp::~vtkGLMapperProp() { } int vtkGLMapperProp::RenderOpaqueGeometry(vtkViewport *) { if (!m_WrappedGLMapper || !m_BaseRenderer) return 0; this->m_WrappedGLMapper->MitkRender(m_BaseRenderer, mitk::VtkPropRenderer::Opaque); return 1; } int vtkGLMapperProp::RenderTranslucentPolygonalGeometry(vtkViewport *) { return 0; } int vtkGLMapperProp::RenderVolumetricGeometry(vtkViewport *) { return 0; } int vtkGLMapperProp::RenderOverlay(vtkViewport *) { return 0; } const mitk::GLMapper *vtkGLMapperProp::GetWrappedGLMapper() const { return m_WrappedGLMapper; } void vtkGLMapperProp::SetWrappedGLMapper(mitk::GLMapper *glMapper) { this->m_WrappedGLMapper = glMapper; } void vtkGLMapperProp::SetBaseRenderer(mitk::BaseRenderer *baseRenderer) { this->m_BaseRenderer = baseRenderer; } diff --git a/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp b/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp index 07983f107b..fb12fd9cde 100644 --- a/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp +++ b/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp @@ -1,592 +1,592 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #define GPU_INFO MITK_INFO("mapper.vr") #define GPU_WARN MITK_WARN("mapper.vr") #define GPU_ERROR MITK_ERROR("mapper.vr") #include "mitkGPUVolumeMapper3D.h" #include "mitkDataNode.h" #include "mitkColorProperty.h" #include "mitkColorProperty.h" #include "mitkLevelWindow.h" #include "mitkLevelWindowProperty.h" #include "mitkLookupTableProperty.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkTransferFunctionInitializer.h" #include "mitkTransferFunctionProperty.h" #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vtkMitkGPUVolumeRayCastMapper.h" #include "vtkOpenGLGPUVolumeRayCastMapper.h" const mitk::Image *mitk::GPUVolumeMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } void mitk::GPUVolumeMapper3D::MitkRenderVolumetricGeometry(mitk::BaseRenderer *renderer) { VtkMapper::MitkRenderVolumetricGeometry(renderer); // if(ls->m_gpuInitialized) // ls->m_MapperGPU->UpdateMTime(); } bool mitk::GPUVolumeMapper3D::InitGPU(mitk::BaseRenderer * /*renderer*/) { return false; } void mitk::GPUVolumeMapper3D::InitCPU(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_cpuInitialized) return; ls->m_VtkRenderWindow = renderer->GetVtkRenderer()->GetRenderWindow(); ls->m_MapperCPU = vtkSmartPointer::New(); int numThreads = ls->m_MapperCPU->GetNumberOfThreads(); GPU_INFO << "initializing cpu-raycast-vr (vtkFixedPointVolumeRayCastMapper) (" << numThreads << " threads)"; ls->m_MapperCPU->SetSampleDistance(1.0); // ls->m_MapperCPU->LockSampleDistanceToInputSpacingOn(); ls->m_MapperCPU->SetImageSampleDistance(1.0); ls->m_MapperCPU->IntermixIntersectingGeometryOn(); ls->m_MapperCPU->SetAutoAdjustSampleDistances(0); ls->m_VolumePropertyCPU = vtkSmartPointer::New(); ls->m_VolumePropertyCPU->ShadeOn(); ls->m_VolumePropertyCPU->SetAmbient(0.10f); // 0.05f ls->m_VolumePropertyCPU->SetDiffuse(0.50f); // 0.45f ls->m_VolumePropertyCPU->SetSpecular(0.40f); // 0.50f ls->m_VolumePropertyCPU->SetSpecularPower(16.0f); ls->m_VolumePropertyCPU->SetInterpolationTypeToLinear(); ls->m_VolumeCPU = vtkSmartPointer::New(); ls->m_VolumeCPU->SetMapper(ls->m_MapperCPU); ls->m_VolumeCPU->SetProperty(ls->m_VolumePropertyCPU); ls->m_VolumeCPU->VisibilityOn(); ls->m_MapperCPU->SetInputConnection(m_UnitSpacingImageFilter->GetOutputPort()); // m_Resampler->GetOutput()); ls->m_cpuInitialized = true; } void mitk::GPUVolumeMapper3D::DeinitCPU(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (!ls->m_cpuInitialized) return; GPU_INFO << "deinitializing cpu-raycast-vr"; ls->m_VolumePropertyCPU = nullptr; ls->m_MapperCPU = nullptr; ls->m_VolumeCPU = nullptr; ls->m_cpuInitialized = false; } mitk::GPUVolumeMapper3D::GPUVolumeMapper3D() { - m_Volumenullptr = 0; + m_Volumenullptr = nullptr; m_commonInitialized = false; } mitk::GPUVolumeMapper3D::~GPUVolumeMapper3D() { DeinitCommon(); } void mitk::GPUVolumeMapper3D::InitCommon() { if (m_commonInitialized) return; m_UnitSpacingImageFilter = vtkSmartPointer::New(); m_UnitSpacingImageFilter->SetOutputSpacing(1.0, 1.0, 1.0); CreateDefaultTransferFunctions(); m_commonInitialized = true; } void mitk::GPUVolumeMapper3D::DeinitCommon() { if (!m_commonInitialized) return; m_commonInitialized = false; } bool mitk::GPUVolumeMapper3D::IsRenderable(mitk::BaseRenderer *renderer) { if (!GetDataNode()) return false; DataNode *node = GetDataNode(); bool visible = true; node->GetVisibility(visible, renderer, "visible"); if (!visible) return false; bool value = false; if (!node->GetBoolProperty("volumerendering", value, renderer)) return false; if (!value) return false; mitk::Image *input = const_cast(this->GetInput()); if (!input || !input->IsInitialized()) return false; vtkImageData *inputData = input->GetVtkImageData(this->GetTimestep()); if (inputData == nullptr) return false; return true; } void mitk::GPUVolumeMapper3D::InitVtkMapper(mitk::BaseRenderer *renderer) { if (IsRAYEnabled(renderer)) { DeinitCPU(renderer); if (!InitRAY(renderer)) { GPU_WARN << "hardware renderer can't initialize ... falling back to software renderer"; goto fallback; } } else if (IsGPUEnabled(renderer)) { DeinitCPU(renderer); DeinitRAY(renderer); if (!InitGPU(renderer)) { GPU_WARN << "hardware renderer can't initialize ... falling back to software renderer"; goto fallback; } } else { fallback: DeinitRAY(renderer); InitCPU(renderer); } } vtkProp *mitk::GPUVolumeMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { if (!IsRenderable(renderer)) { if (!m_Volumenullptr) { m_Volumenullptr = vtkSmartPointer::New(); m_Volumenullptr->VisibilityOff(); } return m_Volumenullptr; } InitCommon(); InitVtkMapper(renderer); LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_rayInitialized) return ls->m_VolumeRAY; if (ls->m_gpuInitialized) return ls->m_VolumeGPU; return ls->m_VolumeCPU; } void mitk::GPUVolumeMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { if (!IsRenderable(renderer)) return; InitCommon(); InitVtkMapper(renderer); mitk::Image *input = const_cast(this->GetInput()); vtkImageData *inputData = input->GetVtkImageData(this->GetTimestep()); m_UnitSpacingImageFilter->SetInputData(inputData); LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_rayInitialized) { GenerateDataRAY(renderer); } else if (ls->m_gpuInitialized) { GenerateDataGPU(renderer); } else { GenerateDataCPU(renderer); } // UpdateTransferFunctions UpdateTransferFunctions(renderer); } void mitk::GPUVolumeMapper3D::GenerateDataGPU(mitk::BaseRenderer * /*renderer*/) { } void mitk::GPUVolumeMapper3D::GenerateDataCPU(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); int nextLod = mitk::RenderingManager::GetInstance()->GetNextLOD(renderer); if (IsLODEnabled(renderer) && nextLod == 0) { ls->m_MapperCPU->SetImageSampleDistance(3.5); ls->m_MapperCPU->SetSampleDistance(1.25); ls->m_VolumePropertyCPU->SetInterpolationTypeToNearest(); } else { ls->m_MapperCPU->SetImageSampleDistance(1.0); ls->m_MapperCPU->SetSampleDistance(1.0); ls->m_VolumePropertyCPU->SetInterpolationTypeToLinear(); } // Check raycasting mode if (IsMIPEnabled(renderer)) ls->m_MapperCPU->SetBlendModeToMaximumIntensity(); else ls->m_MapperCPU->SetBlendModeToComposite(); // Updating shadings { float value = 0; if (GetDataNode()->GetFloatProperty("volumerendering.cpu.ambient", value, renderer)) ls->m_VolumePropertyCPU->SetAmbient(value); if (GetDataNode()->GetFloatProperty("volumerendering.cpu.diffuse", value, renderer)) ls->m_VolumePropertyCPU->SetDiffuse(value); if (GetDataNode()->GetFloatProperty("volumerendering.cpu.specular", value, renderer)) ls->m_VolumePropertyCPU->SetSpecular(value); if (GetDataNode()->GetFloatProperty("volumerendering.cpu.specular.power", value, renderer)) ls->m_VolumePropertyCPU->SetSpecularPower(value); } } void mitk::GPUVolumeMapper3D::CreateDefaultTransferFunctions() { m_DefaultOpacityTransferFunction = vtkSmartPointer::New(); m_DefaultOpacityTransferFunction->AddPoint(0.0, 0.0); m_DefaultOpacityTransferFunction->AddPoint(255.0, 0.8); m_DefaultOpacityTransferFunction->ClampingOn(); m_DefaultGradientTransferFunction = vtkSmartPointer::New(); m_DefaultGradientTransferFunction->AddPoint(0.0, 0.0); m_DefaultGradientTransferFunction->AddPoint(255.0, 0.8); m_DefaultGradientTransferFunction->ClampingOn(); m_DefaultColorTransferFunction = vtkSmartPointer::New(); m_DefaultColorTransferFunction->AddRGBPoint(0.0, 0.0, 0.0, 0.0); m_DefaultColorTransferFunction->AddRGBPoint(127.5, 1, 1, 0.0); m_DefaultColorTransferFunction->AddRGBPoint(255.0, 0.8, 0.2, 0); m_DefaultColorTransferFunction->ClampingOn(); m_BinaryOpacityTransferFunction = vtkSmartPointer::New(); m_BinaryOpacityTransferFunction->AddPoint(0, 0.0); m_BinaryOpacityTransferFunction->AddPoint(1, 1.0); m_BinaryGradientTransferFunction = vtkSmartPointer::New(); m_BinaryGradientTransferFunction->AddPoint(0.0, 1.0); m_BinaryColorTransferFunction = vtkSmartPointer::New(); } void mitk::GPUVolumeMapper3D::UpdateTransferFunctions(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); vtkPiecewiseFunction *opacityTransferFunction = m_DefaultOpacityTransferFunction; vtkPiecewiseFunction *gradientTransferFunction = m_DefaultGradientTransferFunction; vtkColorTransferFunction *colorTransferFunction = m_DefaultColorTransferFunction; bool isBinary = false; GetDataNode()->GetBoolProperty("binary", isBinary, renderer); if (isBinary) { opacityTransferFunction = m_BinaryOpacityTransferFunction; gradientTransferFunction = m_BinaryGradientTransferFunction; colorTransferFunction = m_BinaryColorTransferFunction; colorTransferFunction->RemoveAllPoints(); float rgb[3]; if (!GetDataNode()->GetColor(rgb, renderer)) rgb[0] = rgb[1] = rgb[2] = 1; colorTransferFunction->AddRGBPoint(0, rgb[0], rgb[1], rgb[2]); colorTransferFunction->Modified(); } else { mitk::TransferFunctionProperty *transferFunctionProp = dynamic_cast(this->GetDataNode()->GetProperty("TransferFunction", renderer)); if (transferFunctionProp) { opacityTransferFunction = transferFunctionProp->GetValue()->GetScalarOpacityFunction(); gradientTransferFunction = transferFunctionProp->GetValue()->GetGradientOpacityFunction(); colorTransferFunction = transferFunctionProp->GetValue()->GetColorTransferFunction(); } } if (ls->m_gpuInitialized) { ls->m_VolumePropertyGPU->SetColor(colorTransferFunction); ls->m_VolumePropertyGPU->SetScalarOpacity(opacityTransferFunction); ls->m_VolumePropertyGPU->SetGradientOpacity(gradientTransferFunction); } if (ls->m_rayInitialized) { ls->m_VolumePropertyRAY->SetColor(colorTransferFunction); ls->m_VolumePropertyRAY->SetScalarOpacity(opacityTransferFunction); ls->m_VolumePropertyRAY->SetGradientOpacity(gradientTransferFunction); } if (ls->m_cpuInitialized) { ls->m_VolumePropertyCPU->SetColor(colorTransferFunction); ls->m_VolumePropertyCPU->SetScalarOpacity(opacityTransferFunction); ls->m_VolumePropertyCPU->SetGradientOpacity(gradientTransferFunction); } } void mitk::GPUVolumeMapper3D::ApplyProperties(vtkActor * /*actor*/, mitk::BaseRenderer * /*renderer*/) { // GPU_INFO << "ApplyProperties"; } void mitk::GPUVolumeMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { // GPU_INFO << "SetDefaultProperties"; node->AddProperty("volumerendering", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.usemip", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.uselod", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.cpu.ambient", mitk::FloatProperty::New(0.10f), renderer, overwrite); node->AddProperty("volumerendering.cpu.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); node->AddProperty("volumerendering.cpu.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); node->AddProperty("volumerendering.cpu.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); bool usegpu = true; #ifdef __APPLE__ usegpu = false; node->AddProperty("volumerendering.uselod", mitk::BoolProperty::New(true), renderer, overwrite); #endif node->AddProperty("volumerendering.usegpu", mitk::BoolProperty::New(usegpu), renderer, overwrite); node->AddProperty("volumerendering.useray", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.ray.ambient", mitk::FloatProperty::New(0.25f), renderer, overwrite); node->AddProperty("volumerendering.ray.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); node->AddProperty("volumerendering.ray.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); node->AddProperty("volumerendering.ray.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); node->AddProperty("volumerendering.gpu.ambient", mitk::FloatProperty::New(0.25f), renderer, overwrite); node->AddProperty("volumerendering.gpu.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); node->AddProperty("volumerendering.gpu.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); node->AddProperty("volumerendering.gpu.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); node->AddProperty("volumerendering.gpu.usetexturecompression", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.gpu.reducesliceartifacts", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) { mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); mitk::LevelWindow levelwindow; levelwindow.SetAuto(image); levWinProp->SetLevelWindow(levelwindow); node->SetProperty("levelwindow", levWinProp, renderer); } if ((overwrite) || (node->GetProperty("TransferFunction", renderer) == nullptr)) { // add a default transfer function mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); mitk::TransferFunctionInitializer::Pointer tfInit = mitk::TransferFunctionInitializer::New(tf); tfInit->SetTransferFunctionMode(0); node->SetProperty("TransferFunction", mitk::TransferFunctionProperty::New(tf.GetPointer())); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } bool mitk::GPUVolumeMapper3D::IsLODEnabled(mitk::BaseRenderer *renderer) const { bool value = false; return GetDataNode()->GetBoolProperty("volumerendering.uselod", value, renderer) && value; } bool mitk::GPUVolumeMapper3D::IsMIPEnabled(mitk::BaseRenderer *renderer) { bool value = false; return GetDataNode()->GetBoolProperty("volumerendering.usemip", value, renderer) && value; } bool mitk::GPUVolumeMapper3D::IsGPUEnabled(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool value = false; return ls->m_gpuSupported && GetDataNode()->GetBoolProperty("volumerendering.usegpu", value, renderer) && value; } bool mitk::GPUVolumeMapper3D::InitRAY(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_rayInitialized) return ls->m_raySupported; ls->m_VtkRenderWindow = renderer->GetVtkRenderer()->GetRenderWindow(); GPU_INFO << "initializing gpu-raycast-vr (vtkOpenGLGPUVolumeRayCastMapper)"; ls->m_MapperRAY = vtkSmartPointer::New(); ls->m_MapperRAY->SetAutoAdjustSampleDistances(0); ls->m_MapperRAY->SetSampleDistance(1.0); ls->m_VolumePropertyRAY = vtkSmartPointer::New(); ls->m_VolumePropertyRAY->ShadeOn(); ls->m_VolumePropertyRAY->SetAmbient(0.25f); // 0.05f ls->m_VolumePropertyRAY->SetDiffuse(0.50f); // 0.45f ls->m_VolumePropertyRAY->SetSpecular(0.40f); // 0.50f ls->m_VolumePropertyRAY->SetSpecularPower(16.0f); ls->m_VolumePropertyRAY->SetInterpolationTypeToLinear(); ls->m_VolumeRAY = vtkSmartPointer::New(); ls->m_VolumeRAY->SetMapper(ls->m_MapperRAY); ls->m_VolumeRAY->SetProperty(ls->m_VolumePropertyRAY); ls->m_VolumeRAY->VisibilityOn(); ls->m_MapperRAY->SetInputConnection(this->m_UnitSpacingImageFilter->GetOutputPort()); ls->m_raySupported = ls->m_MapperRAY->IsRenderSupported(renderer->GetRenderWindow(), ls->m_VolumePropertyRAY); ls->m_rayInitialized = true; return ls->m_raySupported; } void mitk::GPUVolumeMapper3D::DeinitRAY(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_rayInitialized) { GPU_INFO << "deinitializing gpu-raycast-vr"; ls->m_MapperRAY = nullptr; ls->m_VolumePropertyRAY = nullptr; // Here ReleaseGraphicsResources has to be called to avoid VTK error messages. // This seems like a VTK bug, because ReleaseGraphicsResources() is ment for internal use, // but you cannot just delete the object (last smartpointer reference) without getting the // VTK error. ls->m_VolumeRAY->ReleaseGraphicsResources(renderer->GetVtkRenderer()->GetRenderWindow()); ls->m_VolumeRAY = nullptr; ls->m_rayInitialized = false; } } void mitk::GPUVolumeMapper3D::GenerateDataRAY(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (IsLODEnabled(renderer) && mitk::RenderingManager::GetInstance()->GetNextLOD(renderer) == 0) ls->m_MapperRAY->SetImageSampleDistance(4.0); else ls->m_MapperRAY->SetImageSampleDistance(1.0); // Check raycasting mode if (IsMIPEnabled(renderer)) ls->m_MapperRAY->SetBlendModeToMaximumIntensity(); else ls->m_MapperRAY->SetBlendModeToComposite(); // Updating shadings { float value = 0; if (GetDataNode()->GetFloatProperty("volumerendering.ray.ambient", value, renderer)) ls->m_VolumePropertyRAY->SetAmbient(value); if (GetDataNode()->GetFloatProperty("volumerendering.ray.diffuse", value, renderer)) ls->m_VolumePropertyRAY->SetDiffuse(value); if (GetDataNode()->GetFloatProperty("volumerendering.ray.specular", value, renderer)) ls->m_VolumePropertyRAY->SetSpecular(value); if (GetDataNode()->GetFloatProperty("volumerendering.ray.specular.power", value, renderer)) ls->m_VolumePropertyRAY->SetSpecularPower(value); } } bool mitk::GPUVolumeMapper3D::IsRAYEnabled(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool value = false; return ls->m_raySupported && GetDataNode()->GetBoolProperty("volumerendering.useray", value, renderer) && value; } diff --git a/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp b/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp index e4c848b295..95a64b1d88 100644 --- a/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp +++ b/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp @@ -1,559 +1,559 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkUnstructuredGridMapper2D.h" #include #include "mitkAbstractTransformGeometry.h" #include "mitkBaseRenderer.h" #include "mitkColorProperty.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include "mitkTransferFunction.h" #include "mitkTransferFunctionProperty.h" #include "mitkUnstructuredGrid.h" #include "mitkVtkMapper3D.h" #include "mitkVtkScalarModeProperty.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vtkPointSetSlicer.h" void mitk::UnstructuredGridMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode()); if (needGenerateData) { ls->UpdateGenerateDataTime(); mitk::DataNode::ConstPointer node = this->GetDataNode(); if (node.IsNull()) return; if (!node->GetProperty(m_ScalarMode, "scalar mode")) { m_ScalarMode = mitk::VtkScalarModeProperty::New(0); } if (!node->GetProperty(m_ScalarVisibility, "scalar visibility")) { m_ScalarVisibility = mitk::BoolProperty::New(true); } if (!node->GetProperty(m_Outline, "outline polygons")) { m_Outline = mitk::BoolProperty::New(false); } if (!node->GetProperty(m_Color, "color")) { m_Color = mitk::ColorProperty::New(1.0f, 1.0f, 1.0f); } if (!node->GetProperty(m_LineWidth, "line width")) { m_LineWidth = mitk::IntProperty::New(1); } } mitk::BaseData::Pointer input = const_cast(GetDataNode()->GetData()); assert(input); input->Update(); if (m_VtkPointSet) - m_VtkPointSet->UnRegister(0); + m_VtkPointSet->UnRegister(nullptr); m_VtkPointSet = this->GetVtkPointSet(renderer, this->GetTimestep()); assert(m_VtkPointSet); - m_VtkPointSet->Register(0); + m_VtkPointSet->Register(nullptr); if (m_ScalarVisibility->GetValue()) { mitk::DataNode::ConstPointer node = this->GetDataNode(); mitk::TransferFunctionProperty::Pointer transferFuncProp; node->GetProperty(transferFuncProp, "TransferFunction", renderer); if (transferFuncProp.IsNotNull()) { mitk::TransferFunction::Pointer tf = transferFuncProp->GetValue(); if (m_ScalarsToColors) - m_ScalarsToColors->UnRegister(0); + m_ScalarsToColors->UnRegister(nullptr); m_ScalarsToColors = static_cast(tf->GetColorTransferFunction()); - m_ScalarsToColors->Register(0); + m_ScalarsToColors->Register(nullptr); if (m_ScalarsToOpacity) - m_ScalarsToOpacity->UnRegister(0); + m_ScalarsToOpacity->UnRegister(nullptr); m_ScalarsToOpacity = tf->GetScalarOpacityFunction(); - m_ScalarsToOpacity->Register(0); + m_ScalarsToOpacity->Register(nullptr); } else { if (m_ScalarsToColors) - m_ScalarsToColors->UnRegister(0); + m_ScalarsToColors->UnRegister(nullptr); m_ScalarsToColors = this->GetVtkLUT(renderer); assert(m_ScalarsToColors); - m_ScalarsToColors->Register(0); + m_ScalarsToColors->Register(nullptr); float opacity; node->GetOpacity(opacity, renderer); if (m_ScalarsToOpacity) - m_ScalarsToOpacity->UnRegister(0); + m_ScalarsToOpacity->UnRegister(nullptr); m_ScalarsToOpacity = vtkPiecewiseFunction::New(); double range[2]; m_VtkPointSet->GetScalarRange(range); m_ScalarsToOpacity->AddSegment(range[0], opacity, range[1], opacity); } } } void mitk::UnstructuredGridMapper2D::Paint(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; vtkLinearTransform *vtktransform = GetDataNode()->GetVtkTransform(); vtkLinearTransform *inversetransform = vtktransform->GetLinearInverse(); PlaneGeometry::ConstPointer worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); PlaneGeometry::ConstPointer worldPlaneGeometry = dynamic_cast(worldGeometry.GetPointer()); Point3D point; Vector3D normal; if (worldPlaneGeometry.IsNotNull()) { // set up vtkPlane according to worldGeometry point = worldPlaneGeometry->GetOrigin(); normal = worldPlaneGeometry->GetNormal(); normal.Normalize(); m_Plane->SetTransform((vtkAbstractTransform *)nullptr); } else { //@FIXME: does not work correctly. Does m_Plane->SetTransform really transforms a "plane plane" into a "curved //plane"? return; AbstractTransformGeometry::ConstPointer worldAbstractGeometry = dynamic_cast(renderer->GetCurrentWorldPlaneGeometry()); if (worldAbstractGeometry.IsNotNull()) { // set up vtkPlane according to worldGeometry point = const_cast(worldAbstractGeometry->GetParametricBoundingBox())->GetMinimum(); FillVector3D(normal, 0, 0, 1); m_Plane->SetTransform(worldAbstractGeometry->GetVtkAbstractTransform()->GetInverse()); } else return; } double vp[3], vnormal[3]; vnl2vtk(point.GetVnlVector(), vp); vnl2vtk(normal.GetVnlVector(), vnormal); // normally, we would need to transform the surface and cut the transformed surface with the cutter. // This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead. //@todo It probably does not work for scaling operations yet:scaling operations have to be // dealed with after the cut is performed by scaling the contour. inversetransform->TransformPoint(vp, vp); inversetransform->TransformNormalAtPoint(vp, vnormal, vnormal); m_Plane->SetOrigin(vp); m_Plane->SetNormal(vnormal); // set data into cutter m_Slicer->SetInputData(m_VtkPointSet); // m_Cutter->GenerateCutScalarsOff(); // m_Cutter->SetSortByToSortByCell(); // calculate the cut m_Slicer->Update(); // apply color and opacity read from the PropertyList ApplyColorAndOpacityProperties(renderer); // traverse the cut contour vtkPolyData *contour = m_Slicer->GetOutput(); vtkPoints *vpoints = contour->GetPoints(); vtkCellArray *vlines = contour->GetLines(); vtkCellArray *vpolys = contour->GetPolys(); vtkPointData *vpointdata = contour->GetPointData(); vtkDataArray *vscalars = vpointdata->GetScalars(); vtkCellData *vcelldata = contour->GetCellData(); vtkDataArray *vcellscalars = vcelldata->GetScalars(); const int numberOfLines = contour->GetNumberOfLines(); const int numberOfPolys = contour->GetNumberOfPolys(); const bool useCellData = m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_DEFAULT || m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_CELL_DATA; const bool usePointData = m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_POINT_DATA; Point3D p; Point2D p2d; vlines->InitTraversal(); vpolys->InitTraversal(); mitk::Color outlineColor = m_Color->GetColor(); glLineWidth((float)m_LineWidth->GetValue()); for (int i = 0; i < numberOfLines; ++i) { - vtkIdType *cell(0); + vtkIdType *cell(nullptr); vtkIdType cellSize(0); vlines->GetNextCell(cellSize, cell); float rgba[4] = {outlineColor[0], outlineColor[1], outlineColor[2], 1.0f}; if (m_ScalarVisibility->GetValue() && vcellscalars) { if (useCellData) { // color each cell according to cell data double scalar = vcellscalars->GetComponent(i, 0); double rgb[3] = {1.0f, 1.0f, 1.0f}; m_ScalarsToColors->GetColor(scalar, rgb); rgba[0] = (float)rgb[0]; rgba[1] = (float)rgb[1]; rgba[2] = (float)rgb[2]; rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar); } else if (usePointData) { double scalar = vscalars->GetComponent(i, 0); double rgb[3] = {1.0f, 1.0f, 1.0f}; m_ScalarsToColors->GetColor(scalar, rgb); rgba[0] = (float)rgb[0]; rgba[1] = (float)rgb[1]; rgba[2] = (float)rgb[2]; rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar); } } glColor4fv(rgba); glBegin(GL_LINE_LOOP); for (int j = 0; j < cellSize; ++j) { vpoints->GetPoint(cell[j], vp); // take transformation via vtktransform into account vtktransform->TransformPoint(vp, vp); vtk2itk(vp, p); // convert 3D point (in mm) to display coordinates (units ) renderer->WorldToDisplay(p, p2d); // convert display coordinates ( (0,0) is top-left ) in GL coordinates ( (0,0) is bottom-left ) // p2d[1]=toGL-p2d[1]; // add the current vertex to the line glVertex2f(p2d[0], p2d[1]); } glEnd(); } bool polyOutline = m_Outline->GetValue(); bool scalarVisibility = m_ScalarVisibility->GetValue(); // cache the transformed points // a fixed size array is way faster than 'new' // slices through 3d cells usually do not generated // polygons with more than 6 vertices const int maxPolySize = 10; Point2D *cachedPoints = new Point2D[maxPolySize * numberOfPolys]; glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // only draw polygons if there are cell scalars // or the outline property is set to true if (scalarVisibility && vcellscalars) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); for (int i = 0; i < numberOfPolys; ++i) { - vtkIdType *cell(0); + vtkIdType *cell(nullptr); vtkIdType cellSize(0); vpolys->GetNextCell(cellSize, cell); float rgba[4] = {1.0f, 1.0f, 1.0f, 0}; if (scalarVisibility && vcellscalars) { if (useCellData) { // color each cell according to cell data double scalar = vcellscalars->GetComponent(i + numberOfLines, 0); double rgb[3] = {1.0f, 1.0f, 1.0f}; m_ScalarsToColors->GetColor(scalar, rgb); rgba[0] = (float)rgb[0]; rgba[1] = (float)rgb[1]; rgba[2] = (float)rgb[2]; rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar); } else if (usePointData) { double scalar = vscalars->GetComponent(i, 0); double rgb[3] = {1.0f, 1.0f, 1.0f}; m_ScalarsToColors->GetColor(scalar, rgb); rgba[0] = (float)rgb[0]; rgba[1] = (float)rgb[1]; rgba[2] = (float)rgb[2]; rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar); } } glColor4fv(rgba); glBegin(GL_POLYGON); for (int j = 0; j < cellSize; ++j) { vpoints->GetPoint(cell[j], vp); // take transformation via vtktransform into account vtktransform->TransformPoint(vp, vp); vtk2itk(vp, p); // convert 3D point (in mm) to display coordinates (units ) renderer->WorldToDisplay(p, p2d); // convert display coordinates ( (0,0) is top-left ) in GL coordinates ( (0,0) is bottom-left ) // p2d[1]=toGL-p2d[1]; cachedPoints[i * 10 + j][0] = p2d[0]; cachedPoints[i * 10 + j][1] = p2d[1]; // add the current vertex to the line glVertex2f(p2d[0], p2d[1]); } glEnd(); } if (polyOutline) { vpolys->InitTraversal(); glColor4f(outlineColor[0], outlineColor[1], outlineColor[2], 1.0f); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); for (int i = 0; i < numberOfPolys; ++i) { - vtkIdType *cell(0); + vtkIdType *cell(nullptr); vtkIdType cellSize(0); vpolys->GetNextCell(cellSize, cell); glBegin(GL_POLYGON); // glPolygonOffset(1.0, 1.0); for (int j = 0; j < cellSize; ++j) { // add the current vertex to the line glVertex2f(cachedPoints[i * 10 + j][0], cachedPoints[i * 10 + j][1]); } glEnd(); } } } glDisable(GL_BLEND); delete[] cachedPoints; } vtkAbstractMapper3D *mitk::UnstructuredGridMapper2D::GetVtkAbstractMapper3D(mitk::BaseRenderer *renderer) { // MITK_INFO << "GETVTKABSTRACTMAPPER3D\n"; mitk::DataNode::ConstPointer node = this->GetDataNode(); if (node.IsNull()) - return 0; + return nullptr; mitk::VtkMapper::Pointer mitkMapper = dynamic_cast(node->GetMapper(2)); if (mitkMapper.IsNull()) { - return 0; + return nullptr; } mitkMapper->Update(renderer); vtkAssembly *assembly = dynamic_cast(mitkMapper->GetVtkProp(renderer)); if (assembly) { vtkProp3DCollection *collection = assembly->GetParts(); collection->InitTraversal(); - vtkProp3D *prop3d = 0; + vtkProp3D *prop3d = nullptr; do { prop3d = collection->GetNextProp3D(); vtkActor *actor = dynamic_cast(prop3d); if (actor) { return dynamic_cast(actor->GetMapper()); } vtkVolume *volume = dynamic_cast(prop3d); if (volume) { return dynamic_cast(volume->GetMapper()); } } while (prop3d != collection->GetLastProp3D()); } else { vtkActor *actor = dynamic_cast(mitkMapper->GetVtkProp(renderer)); if (actor) { return dynamic_cast(actor->GetMapper()); } vtkVolume *volume = dynamic_cast(mitkMapper->GetVtkProp(renderer)); if (volume) { return dynamic_cast(volume->GetMapper()); } } - return 0; + return nullptr; } vtkPointSet *mitk::UnstructuredGridMapper2D::GetVtkPointSet(mitk::BaseRenderer *renderer, int time) { // MITK_INFO << "GETVTKPOINTSET\n"; vtkAbstractMapper3D *abstractMapper = GetVtkAbstractMapper3D(renderer); - if (abstractMapper == 0) + if (abstractMapper == nullptr) { // try to get data from the node mitk::DataNode::ConstPointer node = this->GetDataNode(); if (node.IsNull()) - return 0; + return nullptr; mitk::BaseData::Pointer data = node->GetData(); mitk::UnstructuredGrid::Pointer grid = dynamic_cast(data.GetPointer()); if (!grid.IsNull()) return static_cast(grid->GetVtkUnstructuredGrid(time)); - return 0; + return nullptr; } else { vtkMapper *mapper = dynamic_cast(abstractMapper); if (mapper) { return dynamic_cast(mapper->GetInput()); } vtkAbstractVolumeMapper *volMapper = dynamic_cast(abstractMapper); if (volMapper) { return dynamic_cast(volMapper->GetDataSetInput()); } } - return 0; + return nullptr; } vtkScalarsToColors *mitk::UnstructuredGridMapper2D::GetVtkLUT(mitk::BaseRenderer *renderer) { // MITK_INFO << "GETVTKLUT\n"; vtkMapper *mapper = dynamic_cast(GetVtkAbstractMapper3D(renderer)); if (mapper) return mapper->GetLookupTable(); else { mitk::DataNode::ConstPointer node = this->GetDataNode(); if (node.IsNull()) - return 0; + return nullptr; mitk::VtkMapper::Pointer mitkMapper = dynamic_cast(node->GetMapper(2)); if (mitkMapper.IsNull()) { // MITK_INFO << "mitkMapper is null\n"; - return 0; + return nullptr; } mitkMapper->Update(renderer); vtkVolume *volume = dynamic_cast(mitkMapper->GetVtkProp(renderer)); if (volume) { // MITK_INFO << "found volume prop\n"; return static_cast(volume->GetProperty()->GetRGBTransferFunction()); } vtkAssembly *assembly = dynamic_cast(mitkMapper->GetVtkProp(renderer)); if (assembly) { // MITK_INFO << "found assembly prop\n"; mitk::TransferFunctionProperty::Pointer transferFuncProp; - node->GetProperty(transferFuncProp, "TransferFunction", 0); + node->GetProperty(transferFuncProp, "TransferFunction", nullptr); if (transferFuncProp.IsNotNull()) { MITK_INFO << "return colortransferfunction\n"; return static_cast(transferFuncProp->GetValue()->GetColorTransferFunction()); } } - return 0; + return nullptr; } } bool mitk::UnstructuredGridMapper2D::IsConvertibleToVtkPointSet(mitk::BaseRenderer *renderer) { - return (GetVtkPointSet(renderer, this->GetTimestep()) != 0); + return (GetVtkPointSet(renderer, this->GetTimestep()) != nullptr); } mitk::UnstructuredGridMapper2D::UnstructuredGridMapper2D() { m_Plane = vtkPlane::New(); m_Slicer = vtkPointSetSlicer::New(); m_Slicer->SetSlicePlane(m_Plane); - m_ScalarsToColors = 0; - m_ScalarsToOpacity = 0; - m_VtkPointSet = 0; + m_ScalarsToColors = nullptr; + m_ScalarsToOpacity = nullptr; + m_VtkPointSet = nullptr; // m_LUT = vtkLookupTable::New(); // m_LUT->SetTableRange( 0, 255 ); // m_LUT->SetNumberOfColors( 255 ); // m_LUT->SetRampToLinear (); // m_LUT->Build(); } mitk::UnstructuredGridMapper2D::~UnstructuredGridMapper2D() { m_Slicer->Delete(); m_Plane->Delete(); - if (m_ScalarsToOpacity != 0) - m_ScalarsToOpacity->UnRegister(0); - if (m_ScalarsToColors != 0) - m_ScalarsToColors->UnRegister(0); - if (m_VtkPointSet != 0) - m_VtkPointSet->UnRegister(0); + if (m_ScalarsToOpacity != nullptr) + m_ScalarsToOpacity->UnRegister(nullptr); + if (m_ScalarsToColors != nullptr) + m_ScalarsToColors->UnRegister(nullptr); + if (m_VtkPointSet != nullptr) + m_VtkPointSet->UnRegister(nullptr); } diff --git a/Modules/MapperExt/src/mitkUnstructuredGridVtkMapper3D.cpp b/Modules/MapperExt/src/mitkUnstructuredGridVtkMapper3D.cpp index 137b2f46a4..b578230cb4 100644 --- a/Modules/MapperExt/src/mitkUnstructuredGridVtkMapper3D.cpp +++ b/Modules/MapperExt/src/mitkUnstructuredGridVtkMapper3D.cpp @@ -1,417 +1,417 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkUnstructuredGridVtkMapper3D.h" #include "mitkColorProperty.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkTransferFunctionProperty.h" //#include "mitkLookupTableProperty.h" #include "mitkGridRepresentationProperty.h" #include "mitkGridVolumeMapperProperty.h" #include "mitkVtkInterpolationProperty.h" #include "mitkVtkScalarModeProperty.h" #include "mitkDataStorage.h" #include "mitkSurfaceVtkMapper3D.h" #include #include #include #include #include const mitk::UnstructuredGrid *mitk::UnstructuredGridVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } mitk::UnstructuredGridVtkMapper3D::UnstructuredGridVtkMapper3D() { m_VtkTriangleFilter = vtkDataSetTriangleFilter::New(); m_Assembly = vtkAssembly::New(); m_Volume = vtkVolume::New(); m_Actor = vtkActor::New(); m_ActorWireframe = vtkActor::New(); m_VtkDataSetMapper = vtkUnstructuredGridMapper::New(); m_VtkDataSetMapper->SetResolveCoincidentTopologyToPolygonOffset(); m_VtkDataSetMapper->SetResolveCoincidentTopologyPolygonOffsetParameters(0, 1); m_Actor->SetMapper(m_VtkDataSetMapper); m_VtkDataSetMapper2 = vtkUnstructuredGridMapper::New(); m_VtkDataSetMapper2->SetResolveCoincidentTopologyToPolygonOffset(); m_VtkDataSetMapper2->SetResolveCoincidentTopologyPolygonOffsetParameters(1, 1); m_ActorWireframe->SetMapper(m_VtkDataSetMapper2); m_ActorWireframe->GetProperty()->SetRepresentationToWireframe(); m_Assembly->AddPart(m_Actor); m_Assembly->AddPart(m_ActorWireframe); m_Assembly->AddPart(m_Volume); - m_VtkVolumeRayCastMapper = 0; - m_VtkPTMapper = 0; - m_VtkVolumeZSweepMapper = 0; + m_VtkVolumeRayCastMapper = nullptr; + m_VtkPTMapper = nullptr; + m_VtkVolumeZSweepMapper = nullptr; // m_GenerateNormals = false; } mitk::UnstructuredGridVtkMapper3D::~UnstructuredGridVtkMapper3D() { - if (m_VtkTriangleFilter != 0) + if (m_VtkTriangleFilter != nullptr) m_VtkTriangleFilter->Delete(); - if (m_VtkVolumeRayCastMapper != 0) + if (m_VtkVolumeRayCastMapper != nullptr) m_VtkVolumeRayCastMapper->Delete(); - if (m_VtkVolumeZSweepMapper != 0) + if (m_VtkVolumeZSweepMapper != nullptr) m_VtkVolumeZSweepMapper->Delete(); - if (m_VtkPTMapper != 0) + if (m_VtkPTMapper != nullptr) m_VtkPTMapper->Delete(); - if (m_VtkDataSetMapper != 0) + if (m_VtkDataSetMapper != nullptr) m_VtkDataSetMapper->Delete(); - if (m_VtkDataSetMapper2 != 0) + if (m_VtkDataSetMapper2 != nullptr) m_VtkDataSetMapper2->Delete(); - if (m_Assembly != 0) + if (m_Assembly != nullptr) m_Assembly->Delete(); - if (m_Actor != 0) + if (m_Actor != nullptr) m_Actor->Delete(); - if (m_ActorWireframe != 0) + if (m_ActorWireframe != nullptr) m_ActorWireframe->Delete(); - if (m_Volume != 0) + if (m_Volume != nullptr) m_Volume->Delete(); } vtkProp *mitk::UnstructuredGridVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { return m_Assembly; } void mitk::UnstructuredGridVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { mitk::DataNode::ConstPointer node = this->GetDataNode(); BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode()); if (needGenerateData) { ls->UpdateGenerateDataTime(); m_Assembly->VisibilityOn(); m_ActorWireframe->GetProperty()->SetAmbient(1.0); m_ActorWireframe->GetProperty()->SetDiffuse(0.0); m_ActorWireframe->GetProperty()->SetSpecular(0.0); mitk::TransferFunctionProperty::Pointer transferFuncProp; if (node->GetProperty(transferFuncProp, "TransferFunction")) { mitk::TransferFunction::Pointer transferFunction = transferFuncProp->GetValue(); if (transferFunction->GetColorTransferFunction()->GetSize() < 2) { mitk::UnstructuredGrid::Pointer input = const_cast(this->GetInput()); if (input.IsNull()) return; vtkUnstructuredGrid *grid = input->GetVtkUnstructuredGrid(this->GetTimestep()); - if (grid == 0) + if (grid == nullptr) return; double *scalarRange = grid->GetScalarRange(); vtkColorTransferFunction *colorFunc = transferFunction->GetColorTransferFunction(); colorFunc->RemoveAllPoints(); colorFunc->AddRGBPoint(scalarRange[0], 1, 0, 0); colorFunc->AddRGBPoint((scalarRange[0] + scalarRange[1]) / 2.0, 0, 1, 0); colorFunc->AddRGBPoint(scalarRange[1], 0, 0, 1); } } } bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { m_Assembly->VisibilityOff(); return; } // // get the TimeGeometry of the input object // mitk::UnstructuredGrid::Pointer input = const_cast(this->GetInput()); // // set the input-object at time t for the mapper // vtkUnstructuredGrid *grid = input->GetVtkUnstructuredGrid(this->GetTimestep()); - if (grid == 0) + if (grid == nullptr) { m_Assembly->VisibilityOff(); return; } m_Assembly->VisibilityOn(); m_VtkTriangleFilter->SetInputData(grid); m_VtkDataSetMapper->SetInput(grid); m_VtkDataSetMapper2->SetInput(grid); bool clip = false; node->GetBoolProperty("enable clipping", clip); mitk::DataNode::Pointer bbNode = renderer->GetDataStorage()->GetNamedDerivedNode("Clipping Bounding Object", node); if (clip && bbNode.IsNotNull()) { m_VtkDataSetMapper->SetBoundingObject(dynamic_cast(bbNode->GetData())); m_VtkDataSetMapper2->SetBoundingObject(dynamic_cast(bbNode->GetData())); } else { - m_VtkDataSetMapper->SetBoundingObject(0); - m_VtkDataSetMapper2->SetBoundingObject(0); + m_VtkDataSetMapper->SetBoundingObject(nullptr); + m_VtkDataSetMapper2->SetBoundingObject(nullptr); } // // apply properties read from the PropertyList // - ApplyProperties(0, renderer); + ApplyProperties(nullptr, renderer); } void mitk::UnstructuredGridVtkMapper3D::ResetMapper(BaseRenderer * /*renderer*/) { m_Assembly->VisibilityOff(); } void mitk::UnstructuredGridVtkMapper3D::ApplyProperties(vtkActor * /*actor*/, mitk::BaseRenderer *renderer) { mitk::DataNode::Pointer node = this->GetDataNode(); ApplyColorAndOpacityProperties(renderer, m_Actor); ApplyColorAndOpacityProperties(renderer, m_ActorWireframe); vtkVolumeProperty *volProp = m_Volume->GetProperty(); vtkProperty *property = m_Actor->GetProperty(); vtkProperty *wireframeProp = m_ActorWireframe->GetProperty(); mitk::SurfaceVtkMapper3D::ApplyMitkPropertiesToVtkProperty(node, property, renderer); mitk::SurfaceVtkMapper3D::ApplyMitkPropertiesToVtkProperty(node, wireframeProp, renderer); mitk::TransferFunctionProperty::Pointer transferFuncProp; if (node->GetProperty(transferFuncProp, "TransferFunction", renderer)) { mitk::TransferFunction::Pointer transferFunction = transferFuncProp->GetValue(); volProp->SetColor(transferFunction->GetColorTransferFunction()); volProp->SetScalarOpacity(transferFunction->GetScalarOpacityFunction()); volProp->SetGradientOpacity(transferFunction->GetGradientOpacityFunction()); m_VtkDataSetMapper->SetLookupTable(transferFunction->GetColorTransferFunction()); m_VtkDataSetMapper2->SetLookupTable(transferFunction->GetColorTransferFunction()); } bool isVolumeRenderingOn = false; node->GetBoolProperty("volumerendering", isVolumeRenderingOn, renderer); if (isVolumeRenderingOn) { m_Assembly->RemovePart(m_Actor); m_Assembly->RemovePart(m_ActorWireframe); m_Assembly->AddPart(m_Volume); mitk::GridVolumeMapperProperty::Pointer mapperProp; if (node->GetProperty(mapperProp, "volumerendering.mapper", renderer)) { mitk::GridVolumeMapperProperty::IdType type = mapperProp->GetValueAsId(); switch (type) { case mitk::GridVolumeMapperProperty::RAYCAST: - if (m_VtkVolumeRayCastMapper == 0) + if (m_VtkVolumeRayCastMapper == nullptr) { m_VtkVolumeRayCastMapper = vtkUnstructuredGridVolumeRayCastMapper::New(); m_VtkVolumeRayCastMapper->SetInputConnection(m_VtkTriangleFilter->GetOutputPort()); } m_Volume->SetMapper(m_VtkVolumeRayCastMapper); break; case mitk::GridVolumeMapperProperty::PT: - if (m_VtkPTMapper == 0) + if (m_VtkPTMapper == nullptr) { m_VtkPTMapper = vtkProjectedTetrahedraMapper::New(); m_VtkPTMapper->SetInputConnection(m_VtkTriangleFilter->GetOutputPort()); } m_Volume->SetMapper(m_VtkPTMapper); break; case mitk::GridVolumeMapperProperty::ZSWEEP: - if (m_VtkVolumeZSweepMapper == 0) + if (m_VtkVolumeZSweepMapper == nullptr) { m_VtkVolumeZSweepMapper = vtkUnstructuredGridVolumeZSweepMapper::New(); m_VtkVolumeZSweepMapper->SetInputConnection(m_VtkTriangleFilter->GetOutputPort()); } m_Volume->SetMapper(m_VtkVolumeZSweepMapper); break; } } } else { m_Assembly->RemovePart(m_Volume); m_Assembly->AddPart(m_Actor); m_Assembly->RemovePart(m_ActorWireframe); mitk::GridRepresentationProperty::Pointer gridRepProp; if (node->GetProperty(gridRepProp, "grid representation", renderer)) { mitk::GridRepresentationProperty::IdType type = gridRepProp->GetValueAsId(); switch (type) { case mitk::GridRepresentationProperty::POINTS: property->SetRepresentationToPoints(); break; case mitk::GridRepresentationProperty::WIREFRAME: property->SetRepresentationToWireframe(); break; case mitk::GridRepresentationProperty::SURFACE: property->SetRepresentationToSurface(); break; } // if (type == mitk::GridRepresentationProperty::WIREFRAME_SURFACE) // { // m_Assembly->AddPart(m_ActorWireframe); // } } } // mitk::LevelWindow levelWindow; // if(node->GetLevelWindow(levelWindow, renderer, "levelWindow")) // { // m_VtkVolumeRayCastMapper->SetScalarRange(levelWindow.GetMin(),levelWindow.GetMax()); // } // else // if(node->GetLevelWindow(levelWindow, renderer)) // { // m_VtkVolumeRayCastMapper->SetScalarRange(levelWindow.GetMin(),levelWindow.GetMax()); // } // // mitk::VtkRepresentationProperty* representationProperty; // node->GetProperty(representationProperty, "material.representation", renderer); // if ( representationProperty != nullptr ) // m_Volume->GetProperty()->SetRepresentation( representationProperty->GetVtkRepresentation() ); // // mitk::VtkInterpolationProperty* interpolationProperty; // node->GetProperty(interpolationProperty, "material.interpolation", renderer); // if ( interpolationProperty != nullptr ) // m_Volume->GetProperty()->SetInterpolation( interpolationProperty->GetVtkInterpolation() ); // - mitk::VtkScalarModeProperty *scalarMode = 0; + mitk::VtkScalarModeProperty *scalarMode = nullptr; if (node->GetProperty(scalarMode, "scalar mode", renderer)) { if (m_VtkVolumeRayCastMapper) m_VtkVolumeRayCastMapper->SetScalarMode(scalarMode->GetVtkScalarMode()); if (m_VtkPTMapper) m_VtkPTMapper->SetScalarMode(scalarMode->GetVtkScalarMode()); if (m_VtkVolumeZSweepMapper) m_VtkVolumeZSweepMapper->SetScalarMode(scalarMode->GetVtkScalarMode()); m_VtkDataSetMapper->SetScalarMode(scalarMode->GetVtkScalarMode()); m_VtkDataSetMapper2->SetScalarMode(scalarMode->GetVtkScalarMode()); } else { if (m_VtkVolumeRayCastMapper) m_VtkVolumeRayCastMapper->SetScalarModeToDefault(); if (m_VtkPTMapper) m_VtkPTMapper->SetScalarModeToDefault(); if (m_VtkVolumeZSweepMapper) m_VtkVolumeZSweepMapper->SetScalarModeToDefault(); m_VtkDataSetMapper->SetScalarModeToDefault(); m_VtkDataSetMapper2->SetScalarModeToDefault(); } bool scalarVisibility = true; node->GetBoolProperty("scalar visibility", scalarVisibility, renderer); m_VtkDataSetMapper->SetScalarVisibility(scalarVisibility ? 1 : 0); m_VtkDataSetMapper2->SetScalarVisibility(scalarVisibility ? 1 : 0); // double scalarRangeLower = std::numeric_limits::min(); // double scalarRangeUpper = std::numeric_limits::max(); // mitk::DoubleProperty* lowerRange = 0; // if (node->GetProperty(lowerRange, "scalar range min", renderer)) // { // scalarRangeLower = lowerRange->GetValue(); // } // mitk::DoubleProperty* upperRange = 0; // if (node->GetProperty(upperRange, "scalar range max", renderer)) // { // scalarRangeUpper = upperRange->GetValue(); // } // m_VtkDataSetMapper->SetScalarRange(scalarRangeLower, scalarRangeUpper); // m_VtkDataSetMapper2->SetScalarRange(scalarRangeLower, scalarRangeUpper); // bool colorMode = false; // node->GetBoolProperty("color mode", colorMode); // m_VtkVolumeRayCastMapper->SetColorMode( (colorMode ? 1 : 0) ); // double scalarsMin = 0; // node->GetDoubleProperty("ScalarsRangeMinimum", scalarsMin, renderer); // double scalarsMax = 1.0; // node->GetProperty("ScalarsRangeMaximum", scalarsMax, renderer); // m_VtkVolumeRayCastMapper->SetScalarRange(scalarsMin,scalarsMax); } void mitk::UnstructuredGridVtkMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { SurfaceVtkMapper3D::SetDefaultPropertiesForVtkProperty(node, renderer, overwrite); node->AddProperty("grid representation", GridRepresentationProperty::New(), renderer, overwrite); node->AddProperty("volumerendering", BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.mapper", GridVolumeMapperProperty::New(), renderer, overwrite); node->AddProperty("scalar mode", VtkScalarModeProperty::New(0), renderer, overwrite); node->AddProperty("scalar visibility", BoolProperty::New(true), renderer, overwrite); // node->AddProperty("scalar range min", DoubleProperty::New(std::numeric_limits::min()), renderer, // overwrite); // node->AddProperty("scalar range max", DoubleProperty::New(std::numeric_limits::max()), renderer, // overwrite); node->AddProperty("outline polygons", BoolProperty::New(false), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0f, 1.0f, 1.0f), renderer, overwrite); node->AddProperty("line width", IntProperty::New(1), renderer, overwrite); - if (overwrite || node->GetProperty("TransferFunction", renderer) == 0) + if (overwrite || node->GetProperty("TransferFunction", renderer) == nullptr) { // add a default transfer function mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); // tf->GetColorTransferFunction()->RemoveAllPoints(); node->SetProperty("TransferFunction", mitk::TransferFunctionProperty::New(tf.GetPointer())); } Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/MapperExt/src/vtkPointSetSlicer.cxx b/Modules/MapperExt/src/vtkPointSetSlicer.cxx index 8c6930f5be..5918dbbea9 100644 --- a/Modules/MapperExt/src/vtkPointSetSlicer.cxx +++ b/Modules/MapperExt/src/vtkPointSetSlicer.cxx @@ -1,638 +1,638 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "vtkPointSetSlicer.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkCutter.h" #include "vtkDataSet.h" #include "vtkDoubleArray.h" #include "vtkFloatArray.h" #include "vtkGenericCell.h" #include "vtkMergePoints.h" #include "vtkObjectFactory.h" #include "vtkPlane.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkUnstructuredGrid.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkStreamingDemandDrivenPipeline.h" vtkStandardNewMacro(vtkPointSetSlicer); // Construct with user-specified implicit function; initial value of 0.0; and // generating cut scalars turned off. vtkPointSetSlicer::vtkPointSetSlicer(vtkPlane *cf) { this->SlicePlane = cf; this->GenerateCutScalars = 0; - this->Locator = 0; + this->Locator = nullptr; this->Cutter = vtkCutter::New(); this->Cutter->GenerateValues(1, 0, 1); } vtkPointSetSlicer::~vtkPointSetSlicer() { - this->SetSlicePlane(0); + this->SetSlicePlane(nullptr); if (this->Locator) { this->Locator->UnRegister(this); - this->Locator = NULL; + this->Locator = nullptr; } this->Cutter->Delete(); } void vtkPointSetSlicer::SetSlicePlane(vtkPlane *plane) { if (this->SlicePlane == plane) { return; } if (this->SlicePlane) { this->SlicePlane->UnRegister(this); - this->SlicePlane = 0; + this->SlicePlane = nullptr; } if (plane) { plane->Register(this); this->Cutter->SetCutFunction(plane); } this->SlicePlane = plane; this->Modified(); } // Overload standard modified time function. If cut functions is modified, // or contour values modified, then this object is modified as well. vtkMTimeType vtkPointSetSlicer::GetMTime() { vtkMTimeType mTime = this->Superclass::GetMTime(); vtkMTimeType time; - if (this->SlicePlane != 0) + if (this->SlicePlane != nullptr) { time = this->SlicePlane->GetMTime(); mTime = (time > mTime ? time : mTime); } - if (this->Locator != 0) + if (this->Locator != nullptr) { time = this->Locator->GetMTime(); mTime = (time > mTime ? time : mTime); } return mTime; } int vtkPointSetSlicer::RequestData(vtkInformation * /*request*/, vtkInformationVector **inputVector, vtkInformationVector *outputVector) { // get the info objects vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); vtkInformation *outInfo = outputVector->GetInformationObject(0); // get the input and ouptut vtkDataSet *input = vtkDataSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT())); vtkPolyData *output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); vtkDebugMacro(<< "Executing cutter"); if (!this->SlicePlane) { vtkErrorMacro("No slice plane specified"); return 0; } if (input->GetNumberOfPoints() < 1) { return 1; } if (input->GetDataObjectType() == VTK_STRUCTURED_POINTS || input->GetDataObjectType() == VTK_IMAGE_DATA) { if (input->GetCell(0) && input->GetCell(0)->GetCellDimension() >= 3) { // this->StructuredPointsCutter(input, output, request, inputVector, outputVector); return 1; } } if (input->GetDataObjectType() == VTK_STRUCTURED_GRID) { if (input->GetCell(0)) { int dim = input->GetCell(0)->GetCellDimension(); // only do 3D structured grids (to be extended in the future) if (dim >= 3) { // this->StructuredGridCutter(input, output); return 1; } } } if (input->GetDataObjectType() == VTK_RECTILINEAR_GRID) { // this->RectilinearGridCutter(input, output); return 1; } if (input->GetDataObjectType() == VTK_UNSTRUCTURED_GRID) { vtkDebugMacro(<< "Executing Unstructured Grid Cutter"); this->UnstructuredGridCutter(input, output); } else { vtkDebugMacro(<< "Executing DataSet Cutter"); // this->DataSetCutter(input, output); } return 1; } int vtkPointSetSlicer::RequestUpdateExtent(vtkInformation *, vtkInformationVector **inputVector, vtkInformationVector *) { vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); inInfo->Set(vtkStreamingDemandDrivenPipeline::EXACT_EXTENT(), 1); return 1; } int vtkPointSetSlicer::FillInputPortInformation(int, vtkInformation *info) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet"); return 1; } void vtkPointSetSlicer::UnstructuredGridCutter(vtkDataSet *input, vtkPolyData *output) { vtkIdType cellId, i; vtkDoubleArray *cellScalars; vtkCellArray *newVerts, *newLines, *newPolys; vtkPoints *newPoints; vtkDoubleArray *cutScalars; double s; vtkIdType estimatedSize, numCells = input->GetNumberOfCells(); vtkIdType numPts = input->GetNumberOfPoints(); vtkIdType cellArrayIt = 0; int numCellPts; vtkPointData *inPD, *outPD; vtkCellData *inCD = input->GetCellData(), *outCD = output->GetCellData(); vtkIdList *cellIds; int abortExecute = 0; double range[2]; // Create objects to hold output of contour operation // estimatedSize = (vtkIdType)pow((double)numCells, .75); estimatedSize = estimatedSize / 1024 * 1024; // multiple of 1024 if (estimatedSize < 1024) { estimatedSize = 1024; } newPoints = vtkPoints::New(); newPoints->Allocate(estimatedSize, estimatedSize / 2); newVerts = vtkCellArray::New(); newVerts->Allocate(estimatedSize, estimatedSize / 2); newLines = vtkCellArray::New(); newLines->Allocate(estimatedSize, estimatedSize / 2); newPolys = vtkCellArray::New(); newPolys->Allocate(estimatedSize, estimatedSize / 2); cutScalars = vtkDoubleArray::New(); cutScalars->SetNumberOfTuples(numPts); // Interpolate data along edge. If generating cut scalars, do necessary setup if (this->GenerateCutScalars) { inPD = vtkPointData::New(); inPD->ShallowCopy(input->GetPointData()); // copies original attributes inPD->SetScalars(cutScalars); } else { inPD = input->GetPointData(); } outPD = output->GetPointData(); outPD->InterpolateAllocate(inPD, estimatedSize, estimatedSize / 2); outCD->CopyAllocate(inCD, estimatedSize, estimatedSize / 2); // locator used to merge potentially duplicate points - if (this->Locator == NULL) + if (this->Locator == nullptr) { this->CreateDefaultLocator(); } this->Locator->InitPointInsertion(newPoints, input->GetBounds()); // Loop over all points evaluating scalar function at each point // for (i = 0; i < numPts; i++) { s = this->SlicePlane->FunctionValue(input->GetPoint(i)); cutScalars->SetComponent(i, 0, s); } // Compute some information for progress methods // vtkIdType numCuts = numCells; vtkIdType progressInterval = numCuts / 20 + 1; int cut = 0; vtkUnstructuredGrid *grid = (vtkUnstructuredGrid *)input; vtkIdType *cellArrayPtr = grid->GetCells()->GetPointer(); double *scalarArrayPtr = cutScalars->GetPointer(0); double tempScalar; cellScalars = cutScalars->NewInstance(); cellScalars->SetNumberOfComponents(cutScalars->GetNumberOfComponents()); cellScalars->Allocate(VTK_CELL_SIZE * cutScalars->GetNumberOfComponents()); // Three passes over the cells to process lower dimensional cells first. // For poly data output cells need to be added in the order: // verts, lines and then polys, or cell data gets mixed up. // A better solution is to have an unstructured grid output. // I create a table that maps cell type to cell dimensionality, // because I need a fast way to get cell dimensionality. // This assumes GetCell is slow and GetCellType is fast. // I do not like hard coding a list of cell types here, // but I do not want to add GetCellDimension(vtkIdType cellId) // to the vtkDataSet API. Since I anticipate that the output // will change to vtkUnstructuredGrid. This temporary solution // is acceptable. // int cellType; unsigned char cellTypeDimensions[VTK_NUMBER_OF_CELL_TYPES]; vtkCutter::GetCellTypeDimensions(cellTypeDimensions); int dimensionality; // We skip 0d cells (points), because they cannot be cut (generate no data). for (dimensionality = 1; dimensionality <= 3; ++dimensionality) { // Loop over all cells; get scalar values for all cell points // and process each cell. // cellArrayIt = 0; for (cellId = 0; cellId < numCells && !abortExecute; cellId++) { numCellPts = cellArrayPtr[cellArrayIt]; // I assume that "GetCellType" is fast. cellType = input->GetCellType(cellId); if (cellType >= VTK_NUMBER_OF_CELL_TYPES) { // Protect against new cell types added. vtkErrorMacro("Unknown cell type " << cellType); cellArrayIt += 1 + numCellPts; continue; } if (cellTypeDimensions[cellType] != dimensionality) { cellArrayIt += 1 + numCellPts; continue; } cellArrayIt++; // find min and max values in scalar data range[0] = scalarArrayPtr[cellArrayPtr[cellArrayIt]]; range[1] = scalarArrayPtr[cellArrayPtr[cellArrayIt]]; cellArrayIt++; for (i = 1; i < numCellPts; i++) { tempScalar = scalarArrayPtr[cellArrayPtr[cellArrayIt]]; cellArrayIt++; if (tempScalar <= range[0]) { range[0] = tempScalar; } // if tempScalar <= min range value if (tempScalar >= range[1]) { range[1] = tempScalar; } // if tempScalar >= max range value } // for all points in this cell int needCell = 0; if (0.0 >= range[0] && 0.0 <= range[1]) { needCell = 1; } if (needCell) { vtkCell *cell = input->GetCell(cellId); cellIds = cell->GetPointIds(); cutScalars->GetTuples(cellIds, cellScalars); // Loop over all contour values. if (dimensionality == 3 && !(++cut % progressInterval)) { vtkDebugMacro(<< "Cutting #" << cut); this->UpdateProgress(static_cast(cut) / numCuts); abortExecute = this->GetAbortExecute(); } this->ContourUnstructuredGridCell( cell, cellScalars, this->Locator, newVerts, newLines, newPolys, inPD, outPD, inCD, cellId, outCD); } // if need cell } // for all cells } // for all dimensions (1,2,3). // Update ourselves. Because we don't know upfront how many verts, lines, // polys we've created, take care to reclaim memory. // cellScalars->Delete(); cutScalars->Delete(); if (this->GenerateCutScalars) { inPD->Delete(); } output->SetPoints(newPoints); newPoints->Delete(); if (newVerts->GetNumberOfCells()) { output->SetVerts(newVerts); } newVerts->Delete(); if (newLines->GetNumberOfCells()) { output->SetLines(newLines); } newLines->Delete(); if (newPolys->GetNumberOfCells()) { output->SetPolys(newPolys); } newPolys->Delete(); this->Locator->Initialize(); // release any extra memory output->Squeeze(); } void vtkPointSetSlicer::ContourUnstructuredGridCell(vtkCell *cell, vtkDataArray *cellScalars, vtkPointLocator *locator, vtkCellArray *verts, vtkCellArray *lines, vtkCellArray *polys, vtkPointData *inPd, vtkPointData *outPd, vtkCellData *inCd, vtkIdType cellId, vtkCellData *outCd) { if (cell->GetCellType() == VTK_HEXAHEDRON) { static int CASE_MASK[8] = {1, 2, 4, 8, 16, 32, 64, 128}; POLY_CASES *polyCase; EDGE_LIST *edge; int i, j, index, *vert; volatile int pnum; int v1, v2, newCellId; double t, x1[3], x2[3], x[3], deltaScalar; vtkIdType offset = verts->GetNumberOfCells() + lines->GetNumberOfCells(); // Build the case table for (i = 0, index = 0; i < 8; i++) { if (cellScalars->GetComponent(i, 0) >= 0) { index |= CASE_MASK[i]; } } polyCase = polyCases + index; edge = polyCase->edges; // get the point number of the polygon pnum = 0; for (i = 0; i < 8; i++) if (edge[i] > -1) pnum++; else break; vtkIdType *pts = new vtkIdType[pnum]; for (i = 0; i < pnum; i++) // insert polygon { vert = edges[edge[i]]; // calculate a preferred interpolation direction deltaScalar = (cellScalars->GetComponent(vert[1], 0) - cellScalars->GetComponent(vert[0], 0)); if (deltaScalar > 0) { v1 = vert[0]; v2 = vert[1]; } else { v1 = vert[1]; v2 = vert[0]; deltaScalar = -deltaScalar; } // linear interpolation t = (deltaScalar == 0.0 ? 0.0 : (-cellScalars->GetComponent(v1, 0)) / deltaScalar); cell->GetPoints()->GetPoint(v1, x1); cell->GetPoints()->GetPoint(v2, x2); for (j = 0; j < 3; j++) { x[j] = x1[j] + t * (x2[j] - x1[j]); } if (locator->InsertUniquePoint(x, pts[i])) { if (outPd) { vtkIdType p1 = cell->GetPointIds()->GetId(v1); vtkIdType p2 = cell->GetPointIds()->GetId(v2); outPd->InterpolateEdge(inPd, pts[i], p1, p2, t); } } } // check for degenerate polygon std::vector pset; for (i = 0; i < pnum; i++) { if (std::find(pset.begin(), pset.end(), pts[i]) == pset.end()) pset.push_back(pts[i]); } if (pset.size() > 2) { i = 0; for (std::vector::iterator iter = pset.begin(); iter != pset.end(); iter++) { pts[i] = *iter; i++; } newCellId = offset + polys->InsertNextCell(pset.size(), pts); outCd->CopyData(inCd, cellId, newCellId); } delete[] pts; } else { cell->Contour(0, cellScalars, locator, verts, lines, polys, inPd, outPd, inCd, cellId, outCd); } } // Specify a spatial locator for merging points. By default, // an instance of vtkMergePoints is used. void vtkPointSetSlicer::SetLocator(vtkPointLocator *locator) { if (this->Locator == locator) { return; } if (this->Locator) { this->Locator->UnRegister(this); - this->Locator = 0; + this->Locator = nullptr; } if (locator) { locator->Register(this); } this->Locator = locator; this->Modified(); } void vtkPointSetSlicer::CreateDefaultLocator() { - if (this->Locator == 0) + if (this->Locator == nullptr) { this->Locator = vtkMergePoints::New(); this->Locator->Register(this); this->Locator->Delete(); } } void vtkPointSetSlicer::PrintSelf(std::ostream &os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Slice Plane: " << this->SlicePlane << "\n"; if (this->Locator) { os << indent << "Locator: " << this->Locator << "\n"; } else { os << indent << "Locator: (none)\n"; } os << indent << "Generate Cut Scalars: " << (this->GenerateCutScalars ? "On\n" : "Off\n"); } int vtkPointSetSlicer::edges[12][2] = { {0, 1}, {1, 2}, {3, 2}, {0, 3}, {4, 5}, {5, 6}, {7, 6}, {4, 7}, {0, 4}, {1, 5}, {2, 6}, {3, 7}}; vtkPointSetSlicer::POLY_CASES vtkPointSetSlicer::polyCases[256] = { {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{0, 3, 8, -1, -1, -1, -1, -1}}, {{1, 0, 9, -1, -1, -1, -1, -1}}, {{1, 3, 8, 9, -1, -1, -1, -1}}, {{2, 1, 10, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{2, 0, 9, 10, -1, -1, -1, -1}}, {{2, 10, 9, 8, 3, -1, -1, -1}}, {{3, 2, 11, -1, -1, -1, -1, -1}}, {{0, 2, 11, 8, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{1, 9, 8, 11, 2, -1, -1, -1}}, {{3, 1, 10, 11, -1, -1, -1, -1}}, {{0, 8, 11, 10, 1, -1, -1, -1}}, {{3, 11, 10, 9, 0, -1, -1, -1}}, {{8, 9, 10, 11, -1, -1, -1, -1}}, {{4, 7, 8, -1, -1, -1, -1, -1}}, {{3, 7, 4, 0, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{9, 1, 3, 7, 4, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{11, 2, 0, 4, 7, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{1, 2, 11, 7, 4, 9, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{4, 7, 11, 10, 9, -1, -1, -1}}, {{5, 4, 9, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{0, 4, 5, 1, -1, -1, -1, -1}}, {{8, 3, 1, 5, 4, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{10, 2, 0, 4, 5, -1, -1, -1}}, {{2, 3, 8, 4, 5, 10, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{5, 4, 8, 11, 10, -1, -1, -1}}, {{5, 7, 8, 9, -1, -1, -1, -1}}, {{9, 5, 7, 3, 0, -1, -1, -1}}, {{8, 7, 5, 1, 0, -1, -1, -1}}, {{1, 3, 7, 5, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{2, 10, 5, 7, 3, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{2, 11, 7, 5, 1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{5, 7, 11, 10, -1, -1, -1, -1}}, {{6, 5, 10, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{1, 5, 6, 2, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{9, 0, 2, 6, 5, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{11, 3, 1, 5, 6, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{3, 0, 9, 5, 6, 11, -1, -1}}, {{6, 5, 9, 8, 11, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{6, 4, 9, 10, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{10, 6, 4, 0, 1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{9, 4, 6, 2, 1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{2, 0, 4, 6, -1, -1, -1, -1}}, {{3, 8, 4, 6, 2, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{3, 11, 6, 4, 0, -1, -1, -1}}, {{6, 4, 8, 11, -1, -1, -1, -1}}, {{6, 10, 9, 8, 7, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{6, 7, 8, 0, 1, 10, -1, -1}}, {{6, 10, 1, 3, 7, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{7, 8, 0, 2, 6, -1, -1, -1}}, {{2, 6, 7, 3, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{6, 7, 11, -1, -1, -1, -1, -1}}, {{7, 6, 11, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{2, 6, 7, 3, -1, -1, -1, -1}}, {{8, 0, 2, 6, 7, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{10, 1, 3, 7, 6, -1, -1, -1}}, {{0, 1, 10, 6, 7, 8, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{7, 6, 10, 9, 8, -1, -1, -1}}, {{4, 6, 11, 8, -1, -1, -1, -1}}, {{11, 6, 4, 0, 3, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{8, 4, 6, 2, 3, -1, -1, -1}}, {{0, 2, 6, 4, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{1, 9, 4, 6, 2, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{1, 10, 6, 4, 0, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{4, 6, 10, 9, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{5, 9, 8, 11, 6, -1, -1, -1}}, {{5, 6, 11, 3, 0, 9, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{6, 11, 3, 1, 5, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{5, 9, 0, 2, 6, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{1, 5, 6, 2, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{5, 6, 10, -1, -1, -1, -1, -1}}, {{7, 5, 10, 11, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{11, 7, 5, 1, 2, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{10, 5, 7, 3, 2, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{3, 1, 5, 7, -1, -1, -1, -1}}, {{0, 8, 7, 5, 1, -1, -1, -1}}, {{0, 9, 5, 7, 3, -1, -1, -1}}, {{7, 5, 9, 8, -1, -1, -1, -1}}, {{4, 8, 11, 10, 5, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{4, 5, 10, 2, 3, 8, -1, -1}}, {{5, 10, 2, 0, 4, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{4, 8, 3, 1, 5, -1, -1, -1}}, {{0, 4, 5, 1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{4, 5, 9, -1, -1, -1, -1, -1}}, {{7, 11, 10, 9, 4, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{7, 4, 9, 1, 2, 11, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{7, 11, 2, 0, 4, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{4, 9, 1, 3, 7, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{3, 7, 4, 0, -1, -1, -1, -1}}, {{7, 4, 8, -1, -1, -1, -1, -1}}, {{10, 11, 8, 9, -1, -1, -1, -1}}, {{0, 3, 11, 10, 9, -1, -1, -1}}, {{1, 0, 8, 11, 10, -1, -1, -1}}, {{1, 3, 11, 10, -1, -1, -1, -1}}, {{2, 1, 9, 8, 11, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{2, 0, 8, 11, -1, -1, -1, -1}}, {{2, 3, 11, -1, -1, -1, -1, -1}}, {{3, 2, 10, 9, 8, -1, -1, -1}}, {{0, 2, 10, 9, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}, {{1, 2, 10, -1, -1, -1, -1, -1}}, {{3, 1, 9, 8, -1, -1, -1, -1}}, {{0, 1, 9, -1, -1, -1, -1, -1}}, {{3, 0, 8, -1, -1, -1, -1, -1}}, {{-1, -1, -1, -1, -1, -1, -1, -1}}}; diff --git a/Modules/MapperExt/src/vtkUnstructuredGridMapper.cpp b/Modules/MapperExt/src/vtkUnstructuredGridMapper.cpp index 27fff10ce5..8ecc1f8357 100644 --- a/Modules/MapperExt/src/vtkUnstructuredGridMapper.cpp +++ b/Modules/MapperExt/src/vtkUnstructuredGridMapper.cpp @@ -1,223 +1,223 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "vtkUnstructuredGridMapper.h" #include "vtkExecutive.h" #include "vtkGarbageCollector.h" #include "vtkGeometryFilter.h" #include "vtkInformation.h" #include "vtkObjectFactory.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkScalarsToColors.h" #include "vtkUnstructuredGrid.h" vtkStandardNewMacro(vtkUnstructuredGridMapper); //---------------------------------------------------------------------------- vtkUnstructuredGridMapper::vtkUnstructuredGridMapper() { - this->GeometryExtractor = 0; - this->PolyDataMapper = 0; + this->GeometryExtractor = nullptr; + this->PolyDataMapper = nullptr; } //---------------------------------------------------------------------------- vtkUnstructuredGridMapper::~vtkUnstructuredGridMapper() { // delete internally created objects. if (this->GeometryExtractor) { this->GeometryExtractor->Delete(); } if (this->PolyDataMapper) { this->PolyDataMapper->Delete(); } } void vtkUnstructuredGridMapper::SetBoundingObject(mitk::BoundingObject *bo) { m_BoundingObject = bo; } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::SetInput(vtkUnstructuredGrid *input) { this->SetInputDataObject(input); } //---------------------------------------------------------------------------- vtkUnstructuredGrid *vtkUnstructuredGridMapper::GetInput() { // return this->Superclass::GetInputAsDataSet(); return vtkUnstructuredGrid::SafeDownCast(this->GetExecutive()->GetInputData(0, 0)); } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::ReleaseGraphicsResources(vtkWindow *renWin) { if (this->PolyDataMapper) { this->PolyDataMapper->ReleaseGraphicsResources(renWin); } } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::ReleaseGraphicsResources(mitk::BaseRenderer *renderer) { if (this->PolyDataMapper) { this->PolyDataMapper->ReleaseGraphicsResources(renderer->GetVtkRenderer()->GetRenderWindow()); } } //---------------------------------------------------------------------------- // Receives from Actor -> maps data to primitives // void vtkUnstructuredGridMapper::Render(vtkRenderer *ren, vtkActor *act) { // make sure that we've been properly initialized // if (!this->GetInput()) { vtkErrorMacro(<< "No input!\n"); return; } // Need a lookup table // - if (this->LookupTable == 0) + if (this->LookupTable == nullptr) { this->CreateDefaultLookupTable(); } this->LookupTable->Build(); // Now can create appropriate mapper // - if (this->PolyDataMapper == 0) + if (this->PolyDataMapper == nullptr) { vtkGeometryFilter *gf = vtkGeometryFilter::New(); vtkPolyDataMapper *pm = vtkPolyDataMapper::New(); pm->SetInputConnection(gf->GetOutputPort()); this->GeometryExtractor = gf; this->PolyDataMapper = pm; } // share clipping planes with the PolyDataMapper // if (this->ClippingPlanes != this->PolyDataMapper->GetClippingPlanes()) { this->PolyDataMapper->SetClippingPlanes(this->ClippingPlanes); } if (this->m_BoundingObject) { mitk::BoundingBox::BoundsArrayType bounds = - this->m_BoundingObject->GetGeometry()->CalculateBoundingBoxRelativeToTransform(0)->GetBounds(); + this->m_BoundingObject->GetGeometry()->CalculateBoundingBoxRelativeToTransform(nullptr)->GetBounds(); this->GeometryExtractor->SetExtent(bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]); this->GeometryExtractor->ExtentClippingOn(); } else { this->GeometryExtractor->ExtentClippingOff(); } this->GeometryExtractor->SetInputData(this->GetInput()); this->PolyDataMapper->SetInputConnection(this->GeometryExtractor->GetOutputPort()); // update ourselves in case something has changed this->PolyDataMapper->SetLookupTable(this->GetLookupTable()); this->PolyDataMapper->SetScalarVisibility(this->GetScalarVisibility()); this->PolyDataMapper->SetUseLookupTableScalarRange(this->GetUseLookupTableScalarRange()); this->PolyDataMapper->SetScalarRange(this->GetScalarRange()); this->PolyDataMapper->SetImmediateModeRendering(this->GetImmediateModeRendering()); this->PolyDataMapper->SetColorMode(this->GetColorMode()); this->PolyDataMapper->SetInterpolateScalarsBeforeMapping(this->GetInterpolateScalarsBeforeMapping()); this->PolyDataMapper->SetScalarMode(this->GetScalarMode()); if (this->ScalarMode == VTK_SCALAR_MODE_USE_POINT_FIELD_DATA || this->ScalarMode == VTK_SCALAR_MODE_USE_CELL_FIELD_DATA) { if (this->ArrayAccessMode == VTK_GET_ARRAY_BY_ID) { this->PolyDataMapper->ColorByArrayComponent(this->ArrayId, ArrayComponent); } else { this->PolyDataMapper->ColorByArrayComponent(this->ArrayName, ArrayComponent); } } this->PolyDataMapper->Render(ren, act); this->TimeToDraw = this->PolyDataMapper->GetTimeToDraw(); } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::PrintSelf(ostream &os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); if (this->PolyDataMapper) { os << indent << "Poly Mapper: (" << this->PolyDataMapper << ")\n"; } else { os << indent << "Poly Mapper: (none)\n"; } if (this->GeometryExtractor) { os << indent << "Geometry Extractor: (" << this->GeometryExtractor << ")\n"; } else { os << indent << "Geometry Extractor: (none)\n"; } } //---------------------------------------------------------------------------- vtkMTimeType vtkUnstructuredGridMapper::GetMTime() { vtkMTimeType mTime = this->vtkMapper::GetMTime(); vtkMTimeType time; if (this->LookupTable != nullptr) { time = this->LookupTable->GetMTime(); mTime = (time > mTime ? time : mTime); } return mTime; } //---------------------------------------------------------------------------- int vtkUnstructuredGridMapper::FillInputPortInformation(int vtkNotUsed(port), vtkInformation *info) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid"); return 1; } //---------------------------------------------------------------------------- void vtkUnstructuredGridMapper::ReportReferences(vtkGarbageCollector *collector) { this->Superclass::ReportReferences(collector); // These filters share our input and are therefore involved in a // reference loop. vtkGarbageCollectorReport(collector, this->GeometryExtractor, "GeometryExtractor"); vtkGarbageCollectorReport(collector, this->PolyDataMapper, "PolyDataMapper"); } diff --git a/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp b/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp index 1ba307d4fc..a8491085cb 100644 --- a/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp +++ b/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp @@ -1,139 +1,139 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include std::string pathToImage; class mitkLabelSetImageIOTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLabelSetImageIOTestSuite); MITK_TEST(TestReadWrite3DLabelSetImage); MITK_TEST(TestReadWrite3DplusTLabelSetImage); CPPUNIT_TEST_SUITE_END(); private: mitk::Image::Pointer regularImage; mitk::LabelSetImage::Pointer multilabelImage; public: void setUp() override { regularImage = mitk::Image::New(); } void tearDown() override { - regularImage = 0; - multilabelImage = 0; + regularImage = nullptr; + multilabelImage = nullptr; } void TestReadWrite3DLabelSetImage() { unsigned int dimensions[3] = {256, 256, 312}; regularImage->Initialize(mitk::MakeScalarPixelType(), 3, dimensions); multilabelImage = mitk::LabelSetImage::New(); multilabelImage->Initialize(regularImage); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); newlayer->SetLayer(1); mitk::Label::Pointer label0 = mitk::Label::New(); label0->SetName("Background"); label0->SetValue(0); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label0); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); multilabelImage->AddLayer(newlayer); pathToImage = mitk::IOUtil::CreateTemporaryDirectory(); pathToImage.append("/LabelSetTestImage3D.nrrd"); mitk::IOUtil::Save(multilabelImage, pathToImage); mitk::LabelSetImage::Pointer loadedImage = dynamic_cast(mitk::IOUtil::Load(pathToImage)[0].GetPointer()); // This information is currently not serialized but also checked within the Equals function loadedImage->SetActiveLayer(multilabelImage->GetActiveLayer()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", loadedImage.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", mitk::Equal(*multilabelImage, *loadedImage, 0.0001, true)); itksys::SystemTools::RemoveFile(pathToImage); } void TestReadWrite3DplusTLabelSetImage() { unsigned int dimensions[4] = {256, 256, 312, 10}; regularImage->Initialize(mitk::MakeScalarPixelType(), 4, dimensions); multilabelImage = mitk::LabelSetImage::New(); multilabelImage->Initialize(regularImage); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); newlayer->SetLayer(1); mitk::Label::Pointer label0 = mitk::Label::New(); label0->SetName("Background"); label0->SetValue(0); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label0); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); multilabelImage->AddLayer(newlayer); pathToImage = mitk::IOUtil::CreateTemporaryDirectory(); pathToImage.append("/LabelSetTestImage3DplusT.nrrd"); mitk::IOUtil::Save(multilabelImage, pathToImage); mitk::LabelSetImage::Pointer loadedImage = dynamic_cast(mitk::IOUtil::Load(pathToImage)[0].GetPointer()); // This information is currently not serialized but also checked within the Equals function loadedImage->SetActiveLayer(multilabelImage->GetActiveLayer()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", loadedImage.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", mitk::Equal(*multilabelImage, *loadedImage, 0.0001, true)); itksys::SystemTools::RemoveFile(pathToImage); } }; MITK_TEST_SUITE_REGISTRATION(mitkLabelSetImageIO) diff --git a/Modules/Multilabel/Testing/mitkLabelSetImageSurfaceStampFilterTest.cpp b/Modules/Multilabel/Testing/mitkLabelSetImageSurfaceStampFilterTest.cpp index 007a184129..64debf1156 100644 --- a/Modules/Multilabel/Testing/mitkLabelSetImageSurfaceStampFilterTest.cpp +++ b/Modules/Multilabel/Testing/mitkLabelSetImageSurfaceStampFilterTest.cpp @@ -1,88 +1,88 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include class mitkLabelSetImageSurfaceStampFilterTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLabelSetImageSurfaceStampFilterTestSuite); MITK_TEST(Result_Match_Expectation); CPPUNIT_TEST_SUITE_END(); private: mitk::LabelSetImage::Pointer m_LabelSetImage; mitk::Surface::Pointer m_Surface; public: void setUp() override { mitk::Image::Pointer regularImage = mitk::ImageGenerator::GenerateRandomImage(50, 50, 50, 1, 1, 1, 1, 0.3, 0.2); m_LabelSetImage = dynamic_cast( mitk::IOUtil::Load(GetTestDataFilePath("Multilabel/EmptyMultiLabelSegmentation.nrrd"))[0].GetPointer()); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); mitk::Label::PixelType value1 = 1; label1->SetValue(value1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); mitk::Label::PixelType value2 = 2; label2->SetValue(value2); m_Surface = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("BallBinary30x30x30Reference.vtp"))[0].GetPointer()); } void tearDown() override { // Delete LabelSetImage - m_LabelSetImage = 0; + m_LabelSetImage = nullptr; } void Result_Match_Expectation() { mitk::LabelSetImageSurfaceStampFilter::Pointer filter = mitk::LabelSetImageSurfaceStampFilter::New(); filter->SetSurface(m_Surface); filter->SetForceOverwrite(true); filter->SetInput(m_LabelSetImage); filter->Update(); mitk::LabelSetImage::Pointer result = dynamic_cast( m_LabelSetImage.GetPointer()); // dynamic_cast(filter->GetOutput()); // result->DisconnectPipeline(); // mitk::LabelSetImage::Pointer result // =dynamic_cast(m_LabelSetImage->Clone().GetPointer());//dynamic_cast(filter->GetOutput()); mitk::LabelSetImage::Pointer expectedResult = dynamic_cast( mitk::IOUtil::Load(GetTestDataFilePath("Multilabel/StampResultBasedOnEmptyML.nrrd"))[0].GetPointer()); MITK_ASSERT_EQUAL(result, expectedResult, "Result after stamping should be equal to the saved version"); } // Reduce contours with nth point void TestGetActiveLabel() {} }; MITK_TEST_SUITE_REGISTRATION(mitkLabelSetImageSurfaceStampFilter) diff --git a/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp b/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp index 88e407ee8f..f585cbd720 100644 --- a/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp +++ b/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp @@ -1,448 +1,448 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include class mitkLabelSetImageTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLabelSetImageTestSuite); MITK_TEST(TestInitialize); MITK_TEST(TestAddLayer); MITK_TEST(TestGetActiveLabelSet); MITK_TEST(TestGetActiveLabel); MITK_TEST(TestInitializeByLabeledImage); MITK_TEST(TestGetLabelSet); MITK_TEST(TestGetLabel); MITK_TEST(TestSetExteriorLabel); MITK_TEST(TestGetTotalNumberOfLabels); MITK_TEST(TestExistsLabel); MITK_TEST(TestExistsLabelSet); MITK_TEST(TestSetActiveLayer); MITK_TEST(TestRemoveLayer); MITK_TEST(TestRemoveLabels); MITK_TEST(TestMergeLabel); // TODO check it these functionalities can be moved into a process object // MITK_TEST(TestMergeLabels); // MITK_TEST(TestConcatenate); // MITK_TEST(TestClearBuffer); // MITK_TEST(TestUpdateCenterOfMass); // MITK_TEST(TestGetVectorImage); // MITK_TEST(TestSetVectorImage); // MITK_TEST(TestGetLayerImage); CPPUNIT_TEST_SUITE_END(); private: mitk::LabelSetImage::Pointer m_LabelSetImage; public: void setUp() override { // Create a new labelset image m_LabelSetImage = mitk::LabelSetImage::New(); mitk::Image::Pointer regularImage = mitk::Image::New(); unsigned int dimensions[3] = {256, 256, 312}; regularImage->Initialize(mitk::MakeScalarPixelType(), 3, dimensions); m_LabelSetImage->Initialize(regularImage); } void tearDown() override { // Delete LabelSetImage - m_LabelSetImage = 0; + m_LabelSetImage = nullptr; } // Reduce contours with nth point void TestInitialize() { // LabelSet image should always has the pixel type mitk::Label::PixelType CPPUNIT_ASSERT_MESSAGE("LabelSetImage has wrong pixel type", m_LabelSetImage->GetPixelType() == mitk::MakeScalarPixelType()); mitk::Image::Pointer regularImage = mitk::Image::New(); unsigned int dimensions[3] = {256, 256, 312}; regularImage->Initialize(mitk::MakeScalarPixelType(), 3, dimensions); mitk::BaseGeometry::Pointer regularImageGeo = regularImage->GetGeometry(); mitk::BaseGeometry::Pointer labelImageGeo = m_LabelSetImage->GetGeometry(); MITK_ASSERT_EQUAL(labelImageGeo, regularImageGeo, "LabelSetImage has wrong geometry"); // By default one layer containing the exterior label should be added CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - number of layers is not one", m_LabelSetImage->GetNumberOfLayers() == 1); CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - active layer has wrong ID", m_LabelSetImage->GetActiveLayer() == 0); CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - active label is not the exterior label", m_LabelSetImage->GetActiveLabel()->GetValue() == 0); } void TestAddLayer() { CPPUNIT_ASSERT_MESSAGE("Number of layers is not zero", m_LabelSetImage->GetNumberOfLayers() == 1); m_LabelSetImage->AddLayer(); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - number of layers is not one", m_LabelSetImage->GetNumberOfLayers() == 2); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active layer has wrong ID", m_LabelSetImage->GetActiveLayer() == 1); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is not the exterior label", m_LabelSetImage->GetActiveLabel()->GetValue() == 0); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); unsigned int layerID = m_LabelSetImage->AddLayer(newlayer); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - number of layers is not two", m_LabelSetImage->GetNumberOfLayers() == 3); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active layer has wrong ID", m_LabelSetImage->GetActiveLayer() == layerID); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong", m_LabelSetImage->GetActiveLabel(layerID)->GetValue() == 200); } void TestGetActiveLabelSet() { mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); unsigned int layerID = m_LabelSetImage->AddLayer(newlayer); mitk::LabelSet::Pointer activeLayer = m_LabelSetImage->GetActiveLabelSet(); CPPUNIT_ASSERT_MESSAGE("Wrong layer ID was returned", layerID == 1); CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(*newlayer, *activeLayer, 0.00001, true)); } void TestGetActiveLabel() { mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); mitk::Label::PixelType value1 = 1; label1->SetValue(value1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); mitk::Label::PixelType value2 = 200; label2->SetValue(value2); m_LabelSetImage->GetActiveLabelSet()->AddLabel(label1); m_LabelSetImage->GetActiveLabelSet()->AddLabel(label2); m_LabelSetImage->GetActiveLabelSet()->SetActiveLabel(1); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong", m_LabelSetImage->GetActiveLabel()->GetValue() == value1); m_LabelSetImage->GetActiveLabelSet()->SetActiveLabel(value2); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong", m_LabelSetImage->GetActiveLabel()->GetValue() == value2); } void TestInitializeByLabeledImage() { mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"))[0].GetPointer()); m_LabelSetImage->InitializeByLabeledImage(image); CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels() == 6); } void TestGetLabelSet() { // Test get non existing lset mitk::LabelSet::ConstPointer lset = m_LabelSetImage->GetLabelSet(10000); CPPUNIT_ASSERT_MESSAGE("Non existing labelset is not nullptr", lset.IsNull()); lset = m_LabelSetImage->GetLabelSet(0); CPPUNIT_ASSERT_MESSAGE("Existing labelset is nullptr", lset.IsNotNull()); } void TestGetLabel() { mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); mitk::Label::PixelType value1 = 1; label1->SetValue(value1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); mitk::Label::PixelType value2 = 200; label2->SetValue(value2); m_LabelSetImage->GetActiveLabelSet()->AddLabel(label1); m_LabelSetImage->AddLayer(); m_LabelSetImage->GetLabelSet(1)->AddLabel(label2); CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for active layer", mitk::Equal(*m_LabelSetImage->GetLabel(1), *label1, 0.0001, true)); CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for layer 1", mitk::Equal(*m_LabelSetImage->GetLabel(200, 1), *label2, 0.0001, true)); // Try to get a non existing label mitk::Label *label3 = m_LabelSetImage->GetLabel(1000); CPPUNIT_ASSERT_MESSAGE("Non existing label should be nullptr", label3 == nullptr); // Try to get a label from a non existing layer label3 = m_LabelSetImage->GetLabel(200, 1000); CPPUNIT_ASSERT_MESSAGE("Label from non existing layer should be nullptr", label3 == nullptr); } void TestSetExteriorLabel() { mitk::Label::Pointer exteriorLabel = mitk::Label::New(); exteriorLabel->SetName("MyExteriorSpecialLabel"); mitk::Label::PixelType value1 = 10000; exteriorLabel->SetValue(value1); m_LabelSetImage->SetExteriorLabel(exteriorLabel); CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for layer 1", mitk::Equal(*m_LabelSetImage->GetExteriorLabel(), *exteriorLabel, 0.0001, true)); // Exterior label should be set automatically for each new layer m_LabelSetImage->AddLayer(); CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for layer 1", mitk::Equal(*m_LabelSetImage->GetLabel(10000, 1), *exteriorLabel, 0.0001, true)); } void TestGetTotalNumberOfLabels() { mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); mitk::Label::PixelType value1 = 1; label1->SetValue(value1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); mitk::Label::PixelType value2 = 200; label2->SetValue(value2); m_LabelSetImage->GetActiveLabelSet()->AddLabel(label1); m_LabelSetImage->AddLayer(); m_LabelSetImage->GetLabelSet(1)->AddLabel(label2); CPPUNIT_ASSERT_MESSAGE( "Wrong total number of labels", m_LabelSetImage->GetTotalNumberOfLabels() == 4); // added 2 labels + 2 exterior default labels } void TestExistsLabel() { mitk::Label::Pointer label = mitk::Label::New(); label->SetName("Label2"); mitk::Label::PixelType value = 200; label->SetValue(value); m_LabelSetImage->AddLayer(); m_LabelSetImage->GetLabelSet(1)->AddLabel(label); m_LabelSetImage->SetActiveLayer(0); CPPUNIT_ASSERT_MESSAGE("Existing label was not found", m_LabelSetImage->ExistLabel(value) == true); CPPUNIT_ASSERT_MESSAGE("Non existing label was found", m_LabelSetImage->ExistLabel(10000) == false); } void TestExistsLabelSet() { // Cache active layer mitk::LabelSet::ConstPointer activeLayer = m_LabelSetImage->GetActiveLabelSet(); // Add new layer mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); m_LabelSetImage->AddLayer(newlayer); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(0) == true); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(1) == true); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(20) == false); } void TestSetActiveLayer() { // Cache active layer mitk::LabelSet::ConstPointer activeLayer = m_LabelSetImage->GetActiveLabelSet(); // Add new layer mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); unsigned int layerID = m_LabelSetImage->AddLayer(newlayer); // Set initial layer as active layer m_LabelSetImage->SetActiveLayer(0); CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(*activeLayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true)); // Set previously added layer as active layer m_LabelSetImage->SetActiveLayer(layerID); CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(*newlayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true)); // Set a non existing layer as active layer - nothing should change m_LabelSetImage->SetActiveLayer(10000); CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(*newlayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true)); } void TestRemoveLayer() { // Cache active layer mitk::LabelSet::ConstPointer activeLayer = m_LabelSetImage->GetActiveLabelSet(); // Add new layers m_LabelSetImage->AddLayer(); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); m_LabelSetImage->AddLayer(newlayer); CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(*newlayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true)); m_LabelSetImage->RemoveLayer(); CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed", m_LabelSetImage->GetNumberOfLayers() == 2); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(2) == false); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(1) == true); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(0) == true); m_LabelSetImage->RemoveLayer(); CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed", m_LabelSetImage->GetNumberOfLayers() == 1); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(1) == false); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(0) == true); CPPUNIT_ASSERT_MESSAGE("Wrong active layer", mitk::Equal(*activeLayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true)); m_LabelSetImage->RemoveLayer(); CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed", m_LabelSetImage->GetNumberOfLayers() == 0); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed", m_LabelSetImage->ExistLabelSet(0) == false); CPPUNIT_ASSERT_MESSAGE("Active layers is not nullptr although all layer have been removed", - m_LabelSetImage->GetActiveLabelSet() == 0); + m_LabelSetImage->GetActiveLabelSet() == nullptr); } void TestRemoveLabels() { mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"))[0].GetPointer()); m_LabelSetImage->InitializeByLabeledImage(image); CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels() == 6); // 2ndMin because of the exterior label = 0 CPPUNIT_ASSERT_MESSAGE("Labels with value 1 and 3 was not remove from the image", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 1); CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not remove from the image", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7); CPPUNIT_ASSERT_MESSAGE("Label with ID 3 does not exists after initialization", m_LabelSetImage->ExistLabel(3) == true); CPPUNIT_ASSERT_MESSAGE("Label with ID 7 does not exists after initialization", m_LabelSetImage->ExistLabel(7) == true); std::vector labelsToBeRemoved; labelsToBeRemoved.push_back(1); labelsToBeRemoved.push_back(3); labelsToBeRemoved.push_back(7); m_LabelSetImage->RemoveLabels(labelsToBeRemoved); CPPUNIT_ASSERT_MESSAGE("Wrong number of labels after some have been removed", m_LabelSetImage->GetNumberOfLabels() == 3); // Values within the image are 0, 1, 3, 5, 6, 7 - New Min/Max value should be 5 / 6 // 2ndMin because of the exterior label = 0 CPPUNIT_ASSERT_MESSAGE("Labels with value 1 and 3 was not remove from the image", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 5); CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not remove from the image", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6); } void TestMergeLabel() { mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd"))[0].GetPointer()); - m_LabelSetImage = 0; + m_LabelSetImage = nullptr; m_LabelSetImage = mitk::LabelSetImage::New(); m_LabelSetImage->InitializeByLabeledImage(image); CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6", m_LabelSetImage->GetNumberOfLabels() == 6); // 2ndMin because of the exterior label = 0 CPPUNIT_ASSERT_MESSAGE("Wrong MIN value", m_LabelSetImage->GetStatistics()->GetScalarValueMin() == 0); CPPUNIT_ASSERT_MESSAGE("Wrong MAX value", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7); m_LabelSetImage->GetActiveLabelSet()->SetActiveLabel(6); // Merge label 7 with label 0. Result should be that label 7 is not present any more m_LabelSetImage->MergeLabel(6, 7); CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not remove from the image", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6); m_LabelSetImage->GetStatistics()->GetScalarValue2ndMax(); // Count all pixels with value 7 = 823 // Count all pixels with value 6 = 507 // Check if merge label has 507 + 823 = 1330 pixels CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not remove from the image", m_LabelSetImage->GetStatistics()->GetCountOfMaxValuedVoxels() == 1330); } }; MITK_TEST_SUITE_REGISTRATION(mitkLabelSetImage) diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp index df011a4100..a925ca69c9 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp @@ -1,456 +1,456 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkLabelSetImageWriter__cpp #define __mitkLabelSetImageWriter__cpp #include "mitkLabelSetImageIO.h" #include "mitkBasePropertySerializer.h" #include "mitkIOMimeTypes.h" #include "mitkImageAccessByItk.h" #include "mitkLabelSetIOHelper.h" #include "mitkLabelSetImageConverter.h" #include // itk #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" namespace mitk { LabelSetImageIO::LabelSetImageIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Image") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); } IFileIO::ConfidenceLevel LabelSetImageIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const LabelSetImage *input = static_cast(this->GetInput()); if (input) return Supported; else return Unsupported; } void LabelSetImageIO::Write() { ValidateOutputLocation(); auto input = dynamic_cast(this->GetInput()); mitk::LocaleSwitch localeSwitch("C"); mitk::Image::Pointer inputVector = mitk::ConvertLabelSetImageToImage(input); // image write if (inputVector.IsNull()) { mitkThrow() << "Cannot write non-image data"; } itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New(); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = inputVector->GetGeometry()->Clone(); // Check if geometry information will be lost if (inputVector->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = inputVector->GetDimension(); const unsigned int *const dimensions = inputVector->GetDimensions(); const mitk::PixelType pixelType = inputVector->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO nrrdImageIo->SetNumberOfDimensions(dimension); nrrdImageIo->SetPixelType(pixelType.GetPixelType()); nrrdImageIo->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); nrrdImageIo->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { nrrdImageIo->SetDimensions(i, dimensions[i]); nrrdImageIo->SetSpacing(i, spacing4D[i]); nrrdImageIo->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } nrrdImageIo->SetDirection(i, axisDirection); ioRegion.SetSize(i, inputVector->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, inputVector->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available nrrdImageIo->UseCompressionOn(); nrrdImageIo->SetIORegion(ioRegion); nrrdImageIo->SetFileName(path); // label set specific meta data char keybuffer[512]; char valbuffer[512]; sprintf(keybuffer, "modality"); sprintf(valbuffer, "org.mitk.image.multilabel"); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); sprintf(keybuffer, "layers"); sprintf(valbuffer, "%1d", input->GetNumberOfLayers()); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); for (unsigned int layerIdx = 0; layerIdx < input->GetNumberOfLayers(); layerIdx++) { sprintf(keybuffer, "layer_%03d", layerIdx); // layer idx sprintf(valbuffer, "%1d", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); mitk::LabelSet::LabelContainerConstIteratorType iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); unsigned int count(0); while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd()) { std::unique_ptr document; document.reset(new TiXmlDocument()); TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc.... document->LinkEndChild(decl); TiXmlElement *labelElem = mitk::LabelSetIOHelper::GetLabelAsTiXmlElement(iter->second); document->LinkEndChild(labelElem); TiXmlPrinter printer; printer.SetIndent(""); printer.SetLineBreak(""); document->Accept(&printer); sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.Str()); ++iter; ++count; } } // end label set specific meta data ImageReadAccessor imageAccess(inputVector); nrrdImageIo->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } // end image write } IFileIO::ConfidenceLevel LabelSetImageIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetFileName(fileName); io->ReadImageInformation(); itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary(); std::string value(""); itk::ExposeMetaData(imgMetaDataDictionary, "modality", value); if (value.compare("org.mitk.image.multilabel") == 0) { return Supported; } else return Unsupported; } std::vector LabelSetImageIO::Read() { mitk::LocaleSwitch localeSwitch("C"); // begin regular image loading, adapted from mitkItkImageIO itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New(); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. nrrdImageIO->SetFileName(path); nrrdImageIO->ReadImageInformation(); unsigned int ndim = nrrdImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = nrrdImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = nrrdImageIO->GetDimensions(i); spacing[i] = nrrdImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = nrrdImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; nrrdImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[nrrdImageIO->GetImageSizeInBytes()]; nrrdImageIO->Read(buffer); image->Initialize(MakePixelType(nrrdImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = nrrdImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents() << std::endl; const itk::MetaDataDictionary &dictionary = nrrdImageIO->GetMetaDataDictionary(); for (itk::MetaDataDictionary::ConstIterator iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { std::string key = std::string("meta.") + iter->first; if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { std::string value = dynamic_cast *>(iter->second.GetPointer())->GetMetaDataObjectValue(); image->SetProperty(key.c_str(), mitk::StringProperty::New(value)); } } // end regular image loading LabelSetImage::Pointer output = ConvertImageToLabelSetImage(image); // get labels and add them as properties to the image char keybuffer[256]; unsigned int numberOfLayers = GetIntByKey(dictionary, "layers"); std::string _xmlStr; mitk::Label::Pointer label; for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) { sprintf(keybuffer, "layer_%03d", layerIdx); int numberOfLabels = GetIntByKey(dictionary, keybuffer); mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) { TiXmlDocument doc; sprintf(keybuffer, "label_%03d_%05d", layerIdx, labelIdx); _xmlStr = GetStringByKey(dictionary, keybuffer); doc.Parse(_xmlStr.c_str()); TiXmlElement *labelElem = doc.FirstChildElement("Label"); - if (labelElem == 0) + if (labelElem == nullptr) mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; label = mitk::LabelSetIOHelper::LoadLabelFromTiXmlDocument(labelElem); if (label->GetValue() == 0) // set exterior label is needed to hold exterior information output->SetExteriorLabel(label); labelSet->AddLabel(label); labelSet->SetLayer(layerIdx); } output->AddLabelSetToLayer(layerIdx, labelSet); } MITK_INFO << "...finished!" << std::endl; std::vector result; result.push_back(output.GetPointer()); return result; } int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return atoi(metaString.c_str()); } } return 0; } std::string LabelSetImageIO::GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return metaString; } } return metaString; } LabelSetImageIO *LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); } } // namespace #endif //__mitkLabelSetImageWriter__cpp diff --git a/Modules/Persistence/mitkPersistenceActivator.cpp b/Modules/Persistence/mitkPersistenceActivator.cpp index 6703945bc6..bdbce41943 100644 --- a/Modules/Persistence/mitkPersistenceActivator.cpp +++ b/Modules/Persistence/mitkPersistenceActivator.cpp @@ -1,73 +1,73 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPersistenceActivator.h" mitk::PersistenceActivator::PersistenceActivator() { } void mitk::PersistenceActivator::Load(us::ModuleContext *context) { MITK_DEBUG << "PersistenceActivator::Load"; - mitk::IPersistenceService *persistenceService = 0; + mitk::IPersistenceService *persistenceService = nullptr; us::ServiceReference persistenceServiceRef = context->GetServiceReference(); if (persistenceServiceRef) { persistenceService = dynamic_cast(context->GetService(persistenceServiceRef)); } - bool instanceAlreadyAdded = persistenceService != 0; + bool instanceAlreadyAdded = persistenceService != nullptr; if (instanceAlreadyAdded) - instanceAlreadyAdded = dynamic_cast(persistenceService) != 0; + instanceAlreadyAdded = dynamic_cast(persistenceService) != nullptr; if (instanceAlreadyAdded == false) { // Registering PersistenceService as MicroService m_PersistenceService = itk::SmartPointer(new PersistenceService()); us::ServiceProperties _PersistenceServiceProps; _PersistenceServiceProps["Name"] = std::string("PersistenceService"); m_PersistenceServiceRegistration = context->RegisterService(m_PersistenceService, _PersistenceServiceProps); } else { MITK_ERROR << "Another Persistence instance already installed. Library was loaded twice. Please check configuration!"; } } void mitk::PersistenceActivator::Unload(us::ModuleContext *) { if (m_PersistenceService.IsNull()) return; MITK_DEBUG("PersistenceActivator") << "PersistenceActivator::Unload"; MITK_DEBUG("PersistenceActivator") << "m_PersistenceService GetReferenceCount " << m_PersistenceService->GetReferenceCount(); m_PersistenceServiceRegistration.Unregister(); m_PersistenceService->Unitialize(); m_PersistenceService->Delete(); } mitk::PersistenceActivator::~PersistenceActivator() { } diff --git a/Modules/Persistence/mitkPersistenceService.cpp b/Modules/Persistence/mitkPersistenceService.cpp index 8269d7acf2..2011d9dc2c 100644 --- a/Modules/Persistence/mitkPersistenceService.cpp +++ b/Modules/Persistence/mitkPersistenceService.cpp @@ -1,426 +1,426 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPersistenceService.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include "mitkStandaloneDataStorage.h" #include "mitkUIDGenerator.h" #include "usGetModuleContext.h" #include "usModuleContext.h" #include std::string mitk::PersistenceService::GetPersistencePropertyName() { return "PersistenceNode"; } std::string mitk::PersistenceService::GetPersistencePropertyListName() { return "PersistenceService"; } mitk::PersistenceService::PersistenceService() : m_AutoLoadAndSave(true), m_Initialized(false), m_InInitialized(false) { } void mitk::PersistenceService::Clear() { this->Initialize(); m_PropertyLists.clear(); m_FileNamesToModifiedTimes.clear(); } mitk::PersistenceService::~PersistenceService() { MITK_DEBUG("mitk::PersistenceService") << "destructing PersistenceService"; } std::string mitk::PersistenceService::GetDefaultPersistenceFile() { this->Initialize(); std::string file = "PersistentData.xml"; us::ModuleContext *context = us::GetModuleContext(); std::string contextDataFile = context->GetDataFile(file); if (!contextDataFile.empty()) { file = contextDataFile; } return file; } mitk::PropertyList::Pointer mitk::PersistenceService::GetPropertyList(std::string &id, bool *existed) { this->Initialize(); mitk::PropertyList::Pointer propList; if (id.empty()) { UIDGenerator uidGen; id = uidGen.GetUID(); } std::map::iterator it = m_PropertyLists.find(id); if (it == m_PropertyLists.end()) { propList = PropertyList::New(); m_PropertyLists[id] = propList; if (existed) *existed = false; } else { propList = (*it).second; if (existed) *existed = true; } return propList; } void mitk::PersistenceService::ClonePropertyList(mitk::PropertyList *from, mitk::PropertyList *to) const { to->Clear(); const std::map *propMap = from->GetMap(); std::map::const_iterator propMapIt = propMap->begin(); while (propMapIt != propMap->end()) { mitk::BaseProperty::Pointer clonedProp = (*propMapIt).second->Clone(); to->SetProperty((*propMapIt).first, clonedProp); ++propMapIt; } } bool mitk::PersistenceService::Save(const std::string &fileName, bool appendChanges) { this->Initialize(); bool save = false; std::string theFile = fileName; if (theFile.empty()) theFile = PersistenceService::GetDefaultPersistenceFile(); std::string thePath = itksys::SystemTools::GetFilenamePath(theFile.c_str()); if (!thePath.empty() && !itksys::SystemTools::FileExists(thePath.c_str())) { if (!itksys::SystemTools::MakeDirectory(thePath.c_str())) { MITK_ERROR("PersistenceService") << "Could not create " << thePath; return false; } } bool createFile = !itksys::SystemTools::FileExists(theFile.c_str()); if (!itksys::SystemTools::Touch(theFile.c_str(), createFile)) { MITK_ERROR("PersistenceService") << "Could not create or write to " << theFile; return false; } bool xmlFile = false; if (itksys::SystemTools::GetFilenameLastExtension(theFile.c_str()) == ".xml") xmlFile = true; mitk::DataStorage::Pointer tempDs; if (appendChanges) { if (xmlFile == false) { if (itksys::SystemTools::FileExists(theFile.c_str())) { bool load = false; DataStorage::Pointer ds = m_SceneIO->LoadScene(theFile); - load = (m_SceneIO->GetFailedNodes() == 0 || m_SceneIO->GetFailedNodes()->size() == 0) && - (m_SceneIO->GetFailedNodes() == 0 || m_SceneIO->GetFailedProperties()->IsEmpty()); + load = (m_SceneIO->GetFailedNodes() == nullptr || m_SceneIO->GetFailedNodes()->size() == 0) && + (m_SceneIO->GetFailedNodes() == nullptr || m_SceneIO->GetFailedProperties()->IsEmpty()); if (!load) return false; tempDs = ds; } } else { tempDs = mitk::StandaloneDataStorage::New(); if (xmlFile && appendChanges && itksys::SystemTools::FileExists(theFile.c_str())) { if (!m_PropertyListsXmlFileReaderAndWriter->ReadLists(theFile, m_PropertyLists)) return false; } } this->RestorePropertyListsFromPersistentDataNodes(tempDs); } else if (xmlFile == false) { tempDs = mitk::StandaloneDataStorage::New(); } if (xmlFile) { save = m_PropertyListsXmlFileReaderAndWriter->WriteLists(theFile, m_PropertyLists); } else { DataStorage::SetOfObjects::Pointer sceneNodes = this->GetDataNodes(tempDs); if (m_SceneIO.IsNull()) { m_SceneIO = mitk::SceneIO::New(); } save = m_SceneIO->SaveScene(sceneNodes.GetPointer(), tempDs, theFile); } if (save) { long int currentModifiedTime = itksys::SystemTools::ModifiedTime(theFile.c_str()); m_FileNamesToModifiedTimes[theFile] = currentModifiedTime; } return save; } bool mitk::PersistenceService::Load(const std::string &fileName, bool enforceReload) { this->Initialize(); bool load = false; std::string theFile = fileName; if (theFile.empty()) theFile = PersistenceService::GetDefaultPersistenceFile(); MITK_DEBUG << "Load persistence data from file: " << theFile; if (!itksys::SystemTools::FileExists(theFile.c_str())) return false; bool xmlFile = false; if (itksys::SystemTools::GetFilenameLastExtension(theFile.c_str()) == ".xml") xmlFile = true; if (enforceReload == false) { bool loadTheFile = true; std::map::iterator it = m_FileNamesToModifiedTimes.find(theFile); long int currentModifiedTime = itksys::SystemTools::ModifiedTime(theFile.c_str()); if (it != m_FileNamesToModifiedTimes.end()) { long int knownModifiedTime = (*it).second; if (knownModifiedTime >= currentModifiedTime) { loadTheFile = false; } } if (loadTheFile) m_FileNamesToModifiedTimes[theFile] = currentModifiedTime; else return true; } if (xmlFile) { load = m_PropertyListsXmlFileReaderAndWriter->ReadLists(theFile, m_PropertyLists); } else { if (m_SceneIO.IsNull()) { m_SceneIO = mitk::SceneIO::New(); } DataStorage::Pointer ds = m_SceneIO->LoadScene(theFile); - load = (m_SceneIO->GetFailedNodes() == 0 || m_SceneIO->GetFailedNodes()->size() == 0) && - (m_SceneIO->GetFailedNodes() == 0 || m_SceneIO->GetFailedProperties()->IsEmpty()); + load = (m_SceneIO->GetFailedNodes() == nullptr || m_SceneIO->GetFailedNodes()->size() == 0) && + (m_SceneIO->GetFailedNodes() == nullptr || m_SceneIO->GetFailedProperties()->IsEmpty()); if (load) { this->RestorePropertyListsFromPersistentDataNodes(ds); } } if (!load) { MITK_DEBUG("mitk::PersistenceService") << "loading of scene files failed"; return load; } return load; } void mitk::PersistenceService::SetAutoLoadAndSave(bool autoLoadAndSave) { this->Initialize(); m_AutoLoadAndSave = autoLoadAndSave; // std::string id = PERSISTENCE_PROPERTYLIST_NAME; //see bug 17729 std::string id = GetPersistencePropertyListName(); mitk::PropertyList::Pointer propList = this->GetPropertyList(id); propList->Set("m_AutoLoadAndSave", m_AutoLoadAndSave); this->Save(); } void mitk::PersistenceService::AddPropertyListReplacedObserver(PropertyListReplacedObserver *observer) { this->Initialize(); m_PropertyListReplacedObserver.insert(observer); } void mitk::PersistenceService::RemovePropertyListReplacedObserver(PropertyListReplacedObserver *observer) { this->Initialize(); m_PropertyListReplacedObserver.erase(observer); } void mitk::PersistenceService::LoadModule() { MITK_DEBUG << "Persistence Module loaded."; } us::ModuleContext *mitk::PersistenceService::GetModuleContext() { return us::GetModuleContext(); } std::string mitk::PersistenceService::GetPersistenceNodePropertyName() { this->Initialize(); // return PERSISTENCE_PROPERTY_NAME; //see bug 17729 return GetPersistencePropertyName(); } mitk::DataStorage::SetOfObjects::Pointer mitk::PersistenceService::GetDataNodes(mitk::DataStorage *ds) { this->Initialize(); DataStorage::SetOfObjects::Pointer set = DataStorage::SetOfObjects::New(); std::map::const_iterator it = m_PropertyLists.begin(); while (it != m_PropertyLists.end()) { mitk::DataNode::Pointer node = mitk::DataNode::New(); const std::string &name = (*it).first; this->ClonePropertyList((*it).second, node->GetPropertyList()); node->SetName(name); MITK_DEBUG << "Persistence Property Name: " << GetPersistencePropertyName().c_str(); // node->SetBoolProperty( PERSISTENCE_PROPERTY_NAME.c_str() , true ); //see bug 17729 node->SetBoolProperty(GetPersistencePropertyName().c_str(), true); ds->Add(node); set->push_back(node); ++it; } return set; } bool mitk::PersistenceService::RestorePropertyListsFromPersistentDataNodes(const DataStorage *storage) { this->Initialize(); bool oneFound = false; DataStorage::SetOfObjects::ConstPointer allNodes = storage->GetAll(); for (mitk::DataStorage::SetOfObjects::const_iterator sourceIter = allNodes->begin(); sourceIter != allNodes->end(); ++sourceIter) { mitk::DataNode *node = *sourceIter; bool isPersistenceNode = false; // node->GetBoolProperty( PERSISTENCE_PROPERTY_NAME.c_str(), isPersistenceNode ); //see bug 17729 node->GetBoolProperty(GetPersistencePropertyName().c_str(), isPersistenceNode); if (isPersistenceNode) { oneFound = true; MITK_DEBUG("mitk::PersistenceService") << "isPersistenceNode was true"; std::string name = node->GetName(); bool existed = false; mitk::PropertyList::Pointer propList = this->GetPropertyList(name, &existed); if (existed) { MITK_DEBUG("mitk::PersistenceService") << "calling replace observer before replacing values"; std::set::iterator it = m_PropertyListReplacedObserver.begin(); while (it != m_PropertyListReplacedObserver.end()) { (*it)->BeforePropertyListReplaced(name, propList); ++it; } } // if( existed ) MITK_DEBUG("mitk::PersistenceService") << "replacing values"; this->ClonePropertyList(node->GetPropertyList(), propList); if (existed) { MITK_DEBUG("mitk::PersistenceService") << "calling replace observer before replacing values"; std::set::iterator it = m_PropertyListReplacedObserver.begin(); while (it != m_PropertyListReplacedObserver.end()) { (*it)->AfterPropertyListReplaced(name, propList); ++it; } } // if( existed ) } // if( isPersistenceNode ) } // for ( mitk::DataStorage::SetOfObjects::const_iterator sourceIter = allNodes->begin(); ... return oneFound; } bool mitk::PersistenceService::GetAutoLoadAndSave() { this->Initialize(); return m_AutoLoadAndSave; } bool mitk::PersistenceService::RemovePropertyList(std::string &id) { this->Initialize(); std::map::iterator it = m_PropertyLists.find(id); if (it != m_PropertyLists.end()) { m_PropertyLists.erase(it); return true; } return false; } void mitk::PersistenceService::Initialize() { if (m_Initialized || m_InInitialized) return; m_InInitialized = true; m_PropertyListsXmlFileReaderAndWriter = PropertyListsXmlFileReaderAndWriter::New(); // Load Default File in any case this->Load(); // std::string id = mitk::PersistenceService::PERSISTENCE_PROPERTYLIST_NAME; //see bug 17729 std::string id = GetPersistencePropertyListName(); mitk::PropertyList::Pointer propList = this->GetPropertyList(id); bool autoLoadAndSave = true; propList->GetBoolProperty("m_AutoLoadAndSave", autoLoadAndSave); if (autoLoadAndSave == false) { MITK_DEBUG("mitk::PersistenceService") << "autoloading was not wished. clearing data we got so far."; this->SetAutoLoadAndSave(false); this->Clear(); } m_InInitialized = false; m_Initialized = true; } void mitk::PersistenceService::Unitialize() { if (this->GetAutoLoadAndSave()) this->Save(); } diff --git a/Modules/PhotoacousticsLib/MitkMCxyz/MitkMCxyz.cpp b/Modules/PhotoacousticsLib/MitkMCxyz/MitkMCxyz.cpp index fc2fe8b125..0eadad971c 100644 --- a/Modules/PhotoacousticsLib/MitkMCxyz/MitkMCxyz.cpp +++ b/Modules/PhotoacousticsLib/MitkMCxyz/MitkMCxyz.cpp @@ -1,1472 +1,1472 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Please retain the following copyright notice /****************************************************************** * based on mcxyz.c Oct2014 * * mcxyz.c, in ANSI Standard C programing language * * created 2010, 2012 by * Steven L. JACQUES * Ting LI * Oregon Health & Science University * *******************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "mitkCommandLineParser.h" #include "mitkIOUtil.h" #include "mitkImageCast.h" #include #include #include #include #include #include #include #include #ifdef __linux__ #include #include #else #include #endif #define ls 1.0E-7 /* Moving photon a little bit off the voxel face */ #define PI 3.1415926 #define ALIVE 1 /* if photon not yet terminated */ #define DEAD 0 /* if photon is to be terminated */ #define THRESHOLD 0.01 /* used in roulette */ #define CHANCE 0.1 /* used in roulette */ #define SQR(x) (x*x) #define SIGN(x) ((x)>=0 ? 1:-1) #define ONE_MINUS_COSZERO 1.0E-12 /* If 1-cos(theta) <= ONE_MINUS_COSZERO, fabs(theta) <= 1e-6 rad. */ /* If 1+cos(theta) <= ONE_MINUS_COSZERO, fabs(PI-theta) <= 1e-6 rad. */ /* Struct for storing x,y and z coordinates */ struct Location { int x, y, z; double absorb; }; struct Location initLocation(int x, int y, int z, double absorb) { struct Location loc; loc.x = x; loc.y = y; loc.z = z; loc.absorb = absorb; return loc; } class DetectorVoxel { public: Location location; std::vector* recordedPhotonRoute = new std::vector(); double* fluenceContribution; double m_PhotonNormalizationValue; long m_NumberPhotonsCurrent; DetectorVoxel(Location location, long totalNumberOfVoxels, double photonNormalizationValue) { this->location = location; this->fluenceContribution = (double *)malloc(totalNumberOfVoxels * sizeof(double)); for (int j = 0; j < totalNumberOfVoxels; j++) fluenceContribution[j] = 0; // ensure fluenceContribution[] starts empty. m_NumberPhotonsCurrent = 0; m_PhotonNormalizationValue = photonNormalizationValue; } }; bool verbose(false); class InputValues { private: std::string inputFilename; int tissueIterator; long long ix, iy, iz; public: int mcflag, launchflag, boundaryflag; double xfocus, yfocus, zfocus; double ux0, uy0, uz0; double radius; double waist; double xs, ys, zs; /* launch position */ int Nx, Ny, Nz, numberOfTissueTypes; /* # of bins */ char* tissueType; double* muaVector; double* musVector; double* gVector; double* normalizationVector; double xSpacing, ySpacing, zSpacing; double simulationTimeFromFile; long long Nphotons; long totalNumberOfVoxels; double* totalFluence; std::string myname; DetectorVoxel* detectorVoxel; mitk::Image::Pointer m_inputImage; mitk::Image::Pointer m_normalizationImage; InputValues() { tissueType = nullptr; muaVector = nullptr; musVector = nullptr; gVector = nullptr; detectorVoxel = nullptr; normalizationVector = nullptr; mcflag = 0; launchflag = 0; boundaryflag = 0; } double GetNormalizationValue(int x, int y, int z) { if (normalizationVector) return normalizationVector[z*Ny*Nx + x*Ny + y]; else return 1; } void LoadValues(std::string localInputFilename, float yOffset, std::string normalizationFilename, bool simulatePVFC) { inputFilename = localInputFilename; try { m_inputImage = mitk::IOUtil::LoadImage(inputFilename); } catch (...) { if (verbose) std::cout << "No .nrrd file found ... switching to legacy mode." << std::endl; } try { if (simulatePVFC && !normalizationFilename.empty()) m_normalizationImage = mitk::IOUtil::LoadImage(normalizationFilename); } catch (...) { if (verbose) std::cout << "No normalization .nrrd file found ... will not normalize PVFC" << std::endl; } if (m_normalizationImage.IsNotNull()) { mitk::ImageReadAccessor readAccess3(m_normalizationImage, m_normalizationImage->GetVolumeData(0)); normalizationVector = (double *)readAccess3.GetData(); } if (m_inputImage.IsNotNull()) // load stuff from nrrd file { simulationTimeFromFile = 0; Nx = m_inputImage->GetDimensions()[1]; Ny = m_inputImage->GetDimensions()[0]; Nz = m_inputImage->GetDimensions()[2]; xSpacing = m_inputImage->GetGeometry(0)->GetSpacing()[0]; ySpacing = m_inputImage->GetGeometry(0)->GetSpacing()[1]; zSpacing = m_inputImage->GetGeometry(0)->GetSpacing()[2]; mcflag = std::stoi(m_inputImage->GetProperty("mcflag")->GetValueAsString().c_str()); // mcflag, 0 = uniform, 1 = Gaussian, 2 = iso-pt, 4 = monospectral fraunhofer setup launchflag = std::stoi(m_inputImage->GetProperty("launchflag")->GetValueAsString().c_str());// 0 = let mcxyz calculate trajectory, 1 = manually set launch vector boundaryflag = std::stoi(m_inputImage->GetProperty("boundaryflag")->GetValueAsString().c_str());// 0 = no boundaries, 1 = escape at boundaries, 2 = escape at surface only xs = std::stod(m_inputImage->GetProperty("launchPointX")->GetValueAsString().c_str()); ys = std::stod(m_inputImage->GetProperty("launchPointY")->GetValueAsString().c_str()) + yOffset; zs = std::stod(m_inputImage->GetProperty("launchPointZ")->GetValueAsString().c_str()); xfocus = std::stod(m_inputImage->GetProperty("focusPointX")->GetValueAsString().c_str()); yfocus = std::stod(m_inputImage->GetProperty("focusPointY")->GetValueAsString().c_str()); zfocus = std::stod(m_inputImage->GetProperty("focusPointZ")->GetValueAsString().c_str()); ux0 = std::stod(m_inputImage->GetProperty("trajectoryVectorX")->GetValueAsString().c_str()); uy0 = std::stod(m_inputImage->GetProperty("trajectoryVectorY")->GetValueAsString().c_str()); uz0 = std::stod(m_inputImage->GetProperty("trajectoryVectorZ")->GetValueAsString().c_str()); radius = std::stod(m_inputImage->GetProperty("radius")->GetValueAsString().c_str()); waist = std::stod(m_inputImage->GetProperty("waist")->GetValueAsString().c_str()); totalNumberOfVoxels = Nx*Ny*Nz; if (verbose) std::cout << totalNumberOfVoxels << " = sizeof totalNumberOfVoxels" << std::endl; muaVector = (double *)malloc(totalNumberOfVoxels * sizeof(double)); /* tissue structure */ musVector = (double *)malloc(totalNumberOfVoxels * sizeof(double)); /* tissue structure */ gVector = (double *)malloc(totalNumberOfVoxels * sizeof(double)); /* tissue structure */ mitk::ImageReadAccessor readAccess0(m_inputImage, m_inputImage->GetVolumeData(0)); muaVector = (double *)readAccess0.GetData(); mitk::ImageReadAccessor readAccess1(m_inputImage, m_inputImage->GetVolumeData(1)); musVector = (double *)readAccess1.GetData(); mitk::ImageReadAccessor readAccess2(m_inputImage, m_inputImage->GetVolumeData(2)); gVector = (double *)readAccess2.GetData(); } else { mitkThrow() << "No longer support loading of binary tissue files."; } } }; class ReturnValues { private: long i1 = 0, i2 = 31; // used Random Generator long ma[56]; // used Random Generator /* ma[0] is not used. */ long mj, mk; short i, ii; public: long long Nphotons; double* totalFluence; std::string myname; DetectorVoxel* detectorVoxel; ReturnValues() { detectorVoxel = nullptr; Nphotons = 0; totalFluence = nullptr; } /* SUBROUTINES */ /************************************************************************** * RandomGen * A random number generator that generates uniformly * distributed random numbers between 0 and 1 inclusive. * The algorithm is based on: * W.H. Press, S.A. Teukolsky, W.T. Vetterling, and B.P. * Flannery, "Numerical Recipes in C," Cambridge University * Press, 2nd edition, (1992). * and * D.E. Knuth, "Seminumerical Algorithms," 2nd edition, vol. 2 * of "The Art of Computer Programming", Addison-Wesley, (1981). * * When Type is 0, sets Seed as the seed. Make sure 0 b) m = a; else m = b; return m; } /*********************************************************** * min2 ****/ double min2(double a, double b) { double m; if (a >= b) m = b; else m = a; return m; } /*********************************************************** * min3 ****/ double min3(double a, double b, double c) { double m; if (a <= min2(b, c)) m = a; else if (b <= min2(a, c)) m = b; else m = c; return m; } /******************** * my version of FindVoxelFace for no scattering. * s = ls + FindVoxelFace2(x,y,z, tempx, tempy, tempz, dx, dy, dz, ux, uy, uz); ****/ double FindVoxelFace2(double x1, double y1, double z1, double /*x2*/, double /*y2*/, double /*z2*/, double dx, double dy, double dz, double ux, double uy, double uz) { int ix1 = floor(x1 / dx); int iy1 = floor(y1 / dy); int iz1 = floor(z1 / dz); int ix2, iy2, iz2; if (ux >= 0) ix2 = ix1 + 1; else ix2 = ix1; if (uy >= 0) iy2 = iy1 + 1; else iy2 = iy1; if (uz >= 0) iz2 = iz1 + 1; else iz2 = iz1; double xs = fabs((ix2*dx - x1) / ux); double ys = fabs((iy2*dy - y1) / uy); double zs = fabs((iz2*dz - z1) / uz); double s = min3(xs, ys, zs); return s; } /*********************************************************** * FRESNEL REFLECTANCE * Computes reflectance as photon passes from medium 1 to * medium 2 with refractive indices n1,n2. Incident * angle a1 is specified by cosine value ca1 = cos(a1). * Program returns value of transmitted angle a1 as * value in *ca2_Ptr = cos(a2). ****/ double RFresnel(double n1, /* incident refractive index.*/ double n2, /* transmit refractive index.*/ double ca1, /* cosine of the incident */ /* angle a1, 00. */ { double r; if (n1 == n2) { /** matched boundary. **/ *ca2_Ptr = ca1; r = 0.0; } else if (ca1 > (1.0 - 1.0e-12)) { /** normal incidence. **/ *ca2_Ptr = ca1; r = (n2 - n1) / (n2 + n1); r *= r; } else if (ca1 < 1.0e-6) { /** very slanted. **/ *ca2_Ptr = 0.0; r = 1.0; } else { /** general. **/ double sa1, sa2; /* sine of incident and transmission angles. */ double ca2; /* cosine of transmission angle. */ sa1 = sqrt(1 - ca1*ca1); sa2 = n1*sa1 / n2; if (sa2 >= 1.0) { /* double check for total internal reflection. */ *ca2_Ptr = 0.0; r = 1.0; } else { double cap, cam; /* cosines of sum ap or diff am of the two */ /* angles: ap = a1 + a2, am = a1 - a2. */ double sap, sam; /* sines. */ *ca2_Ptr = ca2 = sqrt(1 - sa2*sa2); cap = ca1*ca2 - sa1*sa2; /* c+ = cc - ss. */ cam = ca1*ca2 + sa1*sa2; /* c- = cc + ss. */ sap = sa1*ca2 + ca1*sa2; /* s+ = sc + cs. */ sam = sa1*ca2 - ca1*sa2; /* s- = sc - cs. */ r = 0.5*sam*sam*(cam*cam + cap*cap) / (sap*sap*cam*cam); /* rearranged for speed. */ } } return(r); } /******** END SUBROUTINE **********/ /*********************************************************** * the boundary is the face of some voxel * find the the photon's hitting position on the nearest face of the voxel and update the step size. ****/ double FindVoxelFace(double x1, double y1, double z1, double x2, double y2, double z2, double dx, double dy, double dz, double ux, double uy, double uz) { double x_1 = x1 / dx; double y_1 = y1 / dy; double z_1 = z1 / dz; double x_2 = x2 / dx; double y_2 = y2 / dy; double z_2 = z2 / dz; double fx_1 = floor(x_1); double fy_1 = floor(y_1); double fz_1 = floor(z_1); double fx_2 = floor(x_2); double fy_2 = floor(y_2); double fz_2 = floor(z_2); double x = 0, y = 0, z = 0, x0 = 0, y0 = 0, z0 = 0, s = 0; if ((fx_1 != fx_2) && (fy_1 != fy_2) && (fz_1 != fz_2)) { //#10 fx_2 = fx_1 + SIGN(fx_2 - fx_1);//added fy_2 = fy_1 + SIGN(fy_2 - fy_1);//added fz_2 = fz_1 + SIGN(fz_2 - fz_1);//added x = (max2(fx_1, fx_2) - x_1) / ux; y = (max2(fy_1, fy_2) - y_1) / uy; z = (max2(fz_1, fz_2) - z_1) / uz; if (x == min3(x, y, z)) { x0 = max2(fx_1, fx_2); y0 = (x0 - x_1) / ux*uy + y_1; z0 = (x0 - x_1) / ux*uz + z_1; } else if (y == min3(x, y, z)) { y0 = max2(fy_1, fy_2); x0 = (y0 - y_1) / uy*ux + x_1; z0 = (y0 - y_1) / uy*uz + z_1; } else { z0 = max2(fz_1, fz_2); y0 = (z0 - z_1) / uz*uy + y_1; x0 = (z0 - z_1) / uz*ux + x_1; } } else if ((fx_1 != fx_2) && (fy_1 != fy_2)) { //#2 fx_2 = fx_1 + SIGN(fx_2 - fx_1);//added fy_2 = fy_1 + SIGN(fy_2 - fy_1);//added x = (max2(fx_1, fx_2) - x_1) / ux; y = (max2(fy_1, fy_2) - y_1) / uy; if (x == min2(x, y)) { x0 = max2(fx_1, fx_2); y0 = (x0 - x_1) / ux*uy + y_1; z0 = (x0 - x_1) / ux*uz + z_1; } else { y0 = max2(fy_1, fy_2); x0 = (y0 - y_1) / uy*ux + x_1; z0 = (y0 - y_1) / uy*uz + z_1; } } else if ((fy_1 != fy_2) && (fz_1 != fz_2)) { //#3 fy_2 = fy_1 + SIGN(fy_2 - fy_1);//added fz_2 = fz_1 + SIGN(fz_2 - fz_1);//added y = (max2(fy_1, fy_2) - y_1) / uy; z = (max2(fz_1, fz_2) - z_1) / uz; if (y == min2(y, z)) { y0 = max2(fy_1, fy_2); x0 = (y0 - y_1) / uy*ux + x_1; z0 = (y0 - y_1) / uy*uz + z_1; } else { z0 = max2(fz_1, fz_2); x0 = (z0 - z_1) / uz*ux + x_1; y0 = (z0 - z_1) / uz*uy + y_1; } } else if ((fx_1 != fx_2) && (fz_1 != fz_2)) { //#4 fx_2 = fx_1 + SIGN(fx_2 - fx_1);//added fz_2 = fz_1 + SIGN(fz_2 - fz_1);//added x = (max2(fx_1, fx_2) - x_1) / ux; z = (max2(fz_1, fz_2) - z_1) / uz; if (x == min2(x, z)) { x0 = max2(fx_1, fx_2); y0 = (x0 - x_1) / ux*uy + y_1; z0 = (x0 - x_1) / ux*uz + z_1; } else { z0 = max2(fz_1, fz_2); x0 = (z0 - z_1) / uz*ux + x_1; y0 = (z0 - z_1) / uz*uy + y_1; } } else if (fx_1 != fx_2) { //#5 fx_2 = fx_1 + SIGN(fx_2 - fx_1);//added x0 = max2(fx_1, fx_2); y0 = (x0 - x_1) / ux*uy + y_1; z0 = (x0 - x_1) / ux*uz + z_1; } else if (fy_1 != fy_2) { //#6 fy_2 = fy_1 + SIGN(fy_2 - fy_1);//added y0 = max2(fy_1, fy_2); x0 = (y0 - y_1) / uy*ux + x_1; z0 = (y0 - y_1) / uy*uz + z_1; } else { //#7 z0 = max2(fz_1, fz_2); fz_2 = fz_1 + SIGN(fz_2 - fz_1);//added x0 = (z0 - z_1) / uz*ux + x_1; y0 = (z0 - z_1) / uz*uy + y_1; } //s = ( (x0-fx_1)*dx + (y0-fy_1)*dy + (z0-fz_1)*dz )/3; //s = sqrt( SQR((x0-x_1)*dx) + SQR((y0-y_1)*dy) + SQR((z0-z_1)*dz) ); //s = sqrt(SQR(x0-x_1)*SQR(dx) + SQR(y0-y_1)*SQR(dy) + SQR(z0-z_1)*SQR(dz)); s = sqrt(SQR((x0 - x_1)*dx) + SQR((y0 - y_1)*dy) + SQR((z0 - z_1)*dz)); return (s); } }; /* DECLARE FUNCTIONS */ void runMonteCarlo(InputValues* inputValues, ReturnValues* returnValue, int thread, mitk::pa::MonteCarloThreadHandler::Pointer threadHandler); int detector_x = -1; int detector_z = -1; bool interpretAsTime = true; bool simulatePVFC = false; int requestedNumberOfPhotons = 100000; float requestedSimulationTime = 0; // in minutes int concurentThreadsSupported = -1; float yOffset = 0; // in mm bool saveLegacy = false; std::string normalizationFilename; std::string inputFilename; std::string outputFilename; mitk::pa::Probe::Pointer m_PhotoacousticProbe; int main(int argc, char * argv[]) { mitkCommandLineParser parser; // set general information parser.setCategory("MITK-Photoacoustics"); parser.setTitle("Mitk MCxyz"); parser.setDescription("Runs Monte Carlo simulations on inputed tissues."); parser.setContributor("CAI, DKFZ based on code by Jacques and Li"); // how should arguments be prefixed parser.setArgumentPrefix("--", "-"); // add each argument, unless specified otherwise each argument is optional // see mitkCommandLineParser::addArgument for more information parser.beginGroup("Required I/O parameters"); parser.addArgument( "input", "i", mitkCommandLineParser::InputFile, "Input tissue file", "input tissue file (*.nrrd)", us::Any(), false); parser.addArgument( "output", "o", mitkCommandLineParser::OutputFile, "Output fluence file", "where to save the simulated fluence (*.nrrd)", us::Any(), false); parser.endGroup(); parser.beginGroup("Optional parameters"); parser.addArgument( "verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose, or rather debug output"); parser.addArgument( "detector-x", "dx", mitkCommandLineParser::Int, "Detector voxel x position", "Determines the x position of the detector voxel (default: -1 = dont use detector voxel)", -1); parser.addArgument( "detector-z", "dz", mitkCommandLineParser::Int, "Detector voxel z position", "Determines the z position of the detector voxel (default: -1 = dont use detector voxel)", -1); parser.addArgument( "number-of-photons", "n", mitkCommandLineParser::Int, "Number of photons", "Specifies the number of photons (default: 100000). Simulation stops after that number. Use -t --timer to define a timer instead"); parser.addArgument( "timer", "t", mitkCommandLineParser::Float, "Simulation time in min", "Specifies the amount of time for simutation (default: 0). Simulation stops after that number of minutes. -n --number-of-photons is the override and default behavior and defines the maximum number of photons instead. If no simulation time or number of photons is specified the file time is taken."); parser.addArgument( "y-offset", "yo", mitkCommandLineParser::Float, "Probe Y-Offset in mm", "Specifies an offset of the photoacoustic probe in the y direction depending on the initial probe position (default: 0) in mm."); parser.addArgument( "jobs", "j", mitkCommandLineParser::Int, "Number of jobs", "Specifies the number of jobs for simutation (default: -1 which starts as many jobs as supported)."); parser.addArgument( "probe-xml", "p", mitkCommandLineParser::InputFile, "Xml definition of the probe", "Specifies the absolute path of the location of the xml definition file of the probe design."); parser.addArgument("normalization-file", "nf", mitkCommandLineParser::InputFile, "Input normalization file", "The input normalization file is used for normalization of the number of photons in the PVFC calculations."); parser.endGroup(); // parse arguments, this method returns a mapping of long argument names and their values std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size() == 0) return EXIT_FAILURE; // parse, cast and set required arguments inputFilename = us::any_cast(parsedArgs["input"]); // strip ending inputFilename = inputFilename.substr(0, inputFilename.find("_H.mci")); inputFilename = inputFilename.substr(0, inputFilename.find("_T.bin")); outputFilename = us::any_cast(parsedArgs["output"]); // add .nrrd if not there std::string suffix = ".nrrd"; if (outputFilename.compare(outputFilename.size() - suffix.size(), suffix.size(), suffix) != 0) outputFilename = outputFilename + suffix; // default values for optional arguments // parse, cast and set optional arguments if given if (parsedArgs.count("verbose")) { verbose = us::any_cast(parsedArgs["verbose"]); } if (parsedArgs.count("detector-x")) { detector_x = us::any_cast(parsedArgs["detector-x"]); } if (parsedArgs.count("detector-z")) { detector_z = us::any_cast(parsedArgs["detector-z"]); } if (parsedArgs.count("timer")) { requestedSimulationTime = us::any_cast(parsedArgs["timer"]); if (requestedSimulationTime > 0) interpretAsTime = true; } if (parsedArgs.count("y-offset")) { yOffset = us::any_cast(parsedArgs["y-offset"]); } if (parsedArgs.count("number-of-photons")) { requestedNumberOfPhotons = us::any_cast(parsedArgs["number-of-photons"]); if (requestedNumberOfPhotons > 0) interpretAsTime = false; } if (parsedArgs.count("jobs")) { concurentThreadsSupported = us::any_cast(parsedArgs["jobs"]); } if (parsedArgs.count("probe-xml")) { std::string inputXmlProbeDesign = us::any_cast(parsedArgs["probe-xml"]); m_PhotoacousticProbe = mitk::pa::Probe::New(inputXmlProbeDesign, verbose); if (!m_PhotoacousticProbe->IsValid()) { std::cerr << "Xml File was not valid. Simulation failed." << std::endl; return EXIT_FAILURE; } } if (parsedArgs.count("normalization-file")) { normalizationFilename = us::any_cast(parsedArgs["normalization-file"]); } if (concurentThreadsSupported == 0 || concurentThreadsSupported == -1) { concurentThreadsSupported = std::thread::hardware_concurrency(); if (concurentThreadsSupported == 0) { std::cout << "Could not determine number of available threads. Launching only one." << std::endl; concurentThreadsSupported = 1; } } if (detector_x != -1 && detector_z != -1) { if (verbose) std::cout << "Performing PVFC calculation for x=" << detector_x << " and z=" << detector_z << std::endl; simulatePVFC = true; } else { if (verbose) std::cout << "Will not perform PVFC calculation due to x=" << detector_x << " and/or z=" << detector_z << std::endl; } InputValues allInput = InputValues(); allInput.LoadValues(inputFilename, yOffset, normalizationFilename, simulatePVFC); std::vector allValues(concurentThreadsSupported); std::thread* threads = new std::thread[concurentThreadsSupported]; for (long i = 0; i < concurentThreadsSupported; i++) { ReturnValues* tmp = new ReturnValues(); allValues.push_back(*tmp); } if (verbose) std::cout << "Initializing MonteCarloThreadHandler" << std::endl; long timeMetric; if (interpretAsTime) { if (requestedSimulationTime < mitk::eps) requestedSimulationTime = allInput.simulationTimeFromFile; timeMetric = requestedSimulationTime * 60 * 1000; } else { timeMetric = requestedNumberOfPhotons; } mitk::pa::MonteCarloThreadHandler::Pointer threadHandler = mitk::pa::MonteCarloThreadHandler::New(timeMetric, interpretAsTime); if (simulatePVFC) threadHandler->SetPackageSize(1000); if (verbose) std::cout << "\nStarting simulation ...\n" << std::endl; auto simulationStartTime = std::chrono::system_clock::now(); for (int i = 0; i < concurentThreadsSupported; i++) { threads[i] = std::thread(runMonteCarlo, &allInput, &allValues[i], (i + 1), threadHandler); } for (int i = 0; i < concurentThreadsSupported; i++) { threads[i].join(); } auto simulationFinishTime = std::chrono::system_clock::now(); auto simulationTimeElapsed = simulationFinishTime - simulationStartTime; if (verbose) std::cout << "\n\nFinished simulation\n\n" << std::endl; std::cout << "total time for simulation: " << (int)std::chrono::duration_cast(simulationTimeElapsed).count() << "sec " << std::endl; /**** SAVE Convert data to relative fluence rate [cm^-2] and save. *****/ if (!simulatePVFC) { if (verbose) std::cout << "Allocating memory for normal simulation result ... "; double* finalTotalFluence = (double *)malloc(allInput.totalNumberOfVoxels * sizeof(double)); if (verbose) std::cout << "[OK]" << std::endl; if (verbose) std::cout << "Cleaning memory for normal simulation result ..."; for (int i = 0; i < allInput.totalNumberOfVoxels; i++) { finalTotalFluence[i] = 0; } if (verbose) std::cout << "[OK]" << std::endl; if (verbose) std::cout << "Calculating resulting fluence ... "; double tdx = 0, tdy = 0, tdz = 0; long long tNphotons = 0; for (int t = 0; t < concurentThreadsSupported; t++) { tdx = allInput.xSpacing; tdy = allInput.ySpacing; tdz = allInput.zSpacing; tNphotons += allValues[t].Nphotons; for (int voxelNumber = 0; voxelNumber < allInput.totalNumberOfVoxels; voxelNumber++) { finalTotalFluence[voxelNumber] += allValues[t].totalFluence[voxelNumber]; } } if (verbose) std::cout << "[OK]" << std::endl; std::cout << "total number of photons simulated: " << tNphotons << std::endl; // Normalize deposition (A) to yield fluence rate (F). double temp = tdx*tdy*tdz*tNphotons; for (int i = 0; i < allInput.totalNumberOfVoxels; i++) { finalTotalFluence[i] /= temp*allInput.muaVector[i]; } if (verbose) std::cout << "Saving normal simulated fluence result to " << outputFilename << " ... "; mitk::Image::Pointer resultImage = mitk::Image::New(); mitk::PixelType TPixel = mitk::MakeScalarPixelType(); unsigned int* dimensionsOfImage = new unsigned int[3]; // Copy dimensions dimensionsOfImage[0] = allInput.Ny; dimensionsOfImage[1] = allInput.Nx; dimensionsOfImage[2] = allInput.Nz; resultImage->Initialize(TPixel, 3, dimensionsOfImage); mitk::Vector3D spacing; spacing[0] = allInput.ySpacing; spacing[1] = allInput.xSpacing; spacing[2] = allInput.zSpacing; resultImage->SetSpacing(spacing); resultImage->SetImportVolume(finalTotalFluence, 0, 0, mitk::Image::CopyMemory); resultImage->GetPropertyList()->SetFloatProperty("y-offset", yOffset); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("y-offset")); mitk::IOUtil::Save(resultImage, outputFilename); if (verbose) std::cout << "[OK]" << std::endl; if (verbose) { std::cout << "x spacing = " << tdx << std::endl; std::cout << "y spacing = " << tdy << std::endl; std::cout << "z spacing = " << tdz << std::endl; std::cout << "total number of voxels = " << allInput.totalNumberOfVoxels << std::endl; std::cout << "number of photons = " << (int)tNphotons << std::endl; } } else // if simulate PVFC { if (verbose) std::cout << "Allocating memory for PVFC simulation result ... "; double* detectorFluence = ((double*)malloc(allInput.totalNumberOfVoxels * sizeof(double))); if (verbose) std::cout << "[OK]" << std::endl; if (verbose) std::cout << "Cleaning memory for PVFC simulation result ..."; for (int i = 0; i < allInput.totalNumberOfVoxels; i++) { detectorFluence[i] = 0; } if (verbose) std::cout << "[OK]" << std::endl; if (verbose) std::cout << "Calculating resulting PVFC fluence ... "; double tdx = 0, tdy = 0, tdz = 0; long long tNphotons = 0; long pvfcPhotons = 0; for (int t = 0; t < concurentThreadsSupported; t++) { tdx = allInput.xSpacing; tdy = allInput.ySpacing; tdz = allInput.zSpacing; tNphotons += allValues[t].Nphotons; pvfcPhotons += allValues[t].detectorVoxel->m_NumberPhotonsCurrent; for (int voxelNumber = 0; voxelNumber < allInput.totalNumberOfVoxels; voxelNumber++) { detectorFluence[voxelNumber] += allValues[t].detectorVoxel->fluenceContribution[voxelNumber]; } } if (verbose) std::cout << "[OK]" << std::endl; std::cout << "total number of photons simulated: " << tNphotons << std::endl; // Normalize deposition (A) to yield fluence rate (F). double temp = tdx*tdy*tdz*tNphotons; for (int i = 0; i < allInput.totalNumberOfVoxels; i++) { detectorFluence[i] /= temp*allInput.muaVector[i]; } if (verbose) std::cout << "Saving PVFC ..."; std::stringstream detectorname(""); double detectorX = allValues[0].detectorVoxel->location.x; double detectorY = allValues[0].detectorVoxel->location.y; double detectorZ = allValues[0].detectorVoxel->location.z; detectorname << detectorX << "," << detectorY << "," << detectorZ << "FluenceContribution.nrrd"; // Save the binary file std::string outputFileBase = outputFilename.substr(0, outputFilename.find(".nrrd")); outputFilename = outputFileBase + "_p" + detectorname.str().c_str(); mitk::Image::Pointer pvfcImage = mitk::Image::New(); unsigned int* dimensionsOfPvfcImage = new unsigned int[3]; // Copy dimensions dimensionsOfPvfcImage[0] = allInput.Ny; dimensionsOfPvfcImage[1] = allInput.Nx; dimensionsOfPvfcImage[2] = allInput.Nz; pvfcImage->Initialize(mitk::MakeScalarPixelType(), 3, dimensionsOfPvfcImage); mitk::Vector3D pvfcSpacing; pvfcSpacing[0] = allInput.ySpacing; pvfcSpacing[1] = allInput.xSpacing; pvfcSpacing[2] = allInput.zSpacing; pvfcImage->SetSpacing(pvfcSpacing); pvfcImage->SetImportVolume(detectorFluence, 0, 0, mitk::Image::CopyMemory); pvfcImage->GetPropertyList()->SetFloatProperty("detector-x", detectorX); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("detector-x")); pvfcImage->GetPropertyList()->SetFloatProperty("detector-y", detectorY); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("detector-y")); pvfcImage->GetPropertyList()->SetFloatProperty("detector-z", detectorZ); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("detector-z")); pvfcImage->GetPropertyList()->SetFloatProperty("normalization-factor", allValues[0].detectorVoxel->m_PhotonNormalizationValue); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("normalization-factor")); pvfcImage->GetPropertyList()->SetFloatProperty("simulated-photons", pvfcPhotons); mitk::CoreServices::GetPropertyPersistence()->AddInfo(mitk::PropertyPersistenceInfo::New("simulated-photons")); mitk::IOUtil::Save(pvfcImage, outputFilename); if (verbose) std::cout << "[OK]" << std::endl; if (verbose) { std::cout << "x spacing = " << tdx << std::endl; std::cout << "y spacing = " << tdy << std::endl; std::cout << "z spacing = " << tdz << std::endl; std::cout << "total number of voxels = " << allInput.totalNumberOfVoxels << std::endl; std::cout << "number of photons = " << (int)tNphotons << std::endl; } } exit(EXIT_SUCCESS); } /* end of main */ /* CORE FUNCTION */ void runMonteCarlo(InputValues* inputValues, ReturnValues* returnValue, int thread, mitk::pa::MonteCarloThreadHandler::Pointer threadHandler) { if (verbose) std::cout << "Thread " << thread << ": Locking Mutex ..." << std::endl; if (verbose) std::cout << "[OK]" << std::endl; if (verbose) std::cout << "Initializing ... "; /* Propagation parameters */ double x, y, z; /* photon position */ double ux, uy, uz; /* photon trajectory as cosines */ double uxx, uyy, uzz; /* temporary values used during SPIN */ double s; /* step sizes. s = -log(RND)/mus [cm] */ double sleft; /* dimensionless */ double costheta; /* cos(theta) */ double sintheta; /* sin(theta) */ double cospsi; /* cos(psi) */ double sinpsi; /* sin(psi) */ double psi; /* azimuthal angle */ long photonIterator = 0; /* current photon */ double W; /* photon weight */ double absorb; /* weighted deposited in a step due to absorption */ short photon_status; /* flag = ALIVE=1 or DEAD=0 */ bool sv; /* Are they in the same voxel? */ /* dummy variables */ double rnd; /* assigned random value 0-1 */ double r, phi; /* dummy values */ long i, j; /* dummy indices */ double tempx, tempy, tempz; /* temporary variables, used during photon step. */ int ix, iy, iz; /* Added. Used to track photons */ double temp; /* dummy variable */ int bflag; /* boundary flag: 0 = photon inside volume. 1 = outside volume */ int CNT = 0; returnValue->totalFluence = (double *)malloc(inputValues->totalNumberOfVoxels * sizeof(double)); /* relative fluence rate [W/cm^2/W.delivered] */ if (detector_x != -1 && detector_z != -1) { if (detector_x<0 || detector_x>inputValues->Nx) { std::cout << "Requested detector x position not valid. Needs to be >= 0 and <= " << inputValues->Nx << std::endl; exit(EXIT_FAILURE); } if (detector_z<1 || detector_z>inputValues->Nz) { std::cout << "Requested detector z position not valid. Needs to be > 0 and <= " << inputValues->Nz << std::endl; exit(EXIT_FAILURE); } double photonNormalizationValue = 1 / inputValues->GetNormalizationValue(detector_x, inputValues->Ny / 2, detector_z); returnValue->detectorVoxel = new DetectorVoxel(initLocation(detector_x, inputValues->Ny / 2, detector_z, 0), inputValues->totalNumberOfVoxels, photonNormalizationValue); } /**** ======================== MAJOR CYCLE ============================ *****/ auto duration = std::chrono::system_clock::now().time_since_epoch(); - returnValue->RandomGen(0, (std::chrono::duration_cast(duration).count() + thread) % 32000, NULL); /* initiate with seed = 1, or any long integer. */ + returnValue->RandomGen(0, (std::chrono::duration_cast(duration).count() + thread) % 32000, nullptr); /* initiate with seed = 1, or any long integer. */ for (j = 0; j < inputValues->totalNumberOfVoxels; j++) returnValue->totalFluence[j] = 0; // ensure F[] starts empty. /**** RUN Launch N photons, initializing each one before progation. *****/ long photonsToSimulate = 0; do { photonsToSimulate = threadHandler->GetNextWorkPackage(); if (returnValue->detectorVoxel != nullptr) { photonsToSimulate = photonsToSimulate * returnValue->detectorVoxel->m_PhotonNormalizationValue; } if (verbose) MITK_INFO << "Photons to simulate: " << photonsToSimulate; photonIterator = 0L; do { /**** LAUNCH Initialize photon position and trajectory. *****/ photonIterator += 1; /* increment photon count */ W = 1.0; /* set photon weight to one */ photon_status = ALIVE; /* Launch an ALIVE photon */ CNT = 0; /**** SET SOURCE* Launch collimated beam at x,y center.****/ /****************************/ /* Initial position. */ if (m_PhotoacousticProbe.IsNotNull()) { double rnd1 = -1; double rnd2 = -1; double rnd3 = -1; double rnd4 = -1; double rnd5 = -1; double rnd6 = -1; double rnd7 = -1; double rnd8 = -1; - while ((rnd1 = returnValue->RandomGen(1, 0, NULL)) <= 0.0); - while ((rnd2 = returnValue->RandomGen(1, 0, NULL)) <= 0.0); - while ((rnd3 = returnValue->RandomGen(1, 0, NULL)) <= 0.0); - while ((rnd4 = returnValue->RandomGen(1, 0, NULL)) <= 0.0); - while ((rnd5 = returnValue->RandomGen(1, 0, NULL)) <= 0.0); - while ((rnd6 = returnValue->RandomGen(1, 0, NULL)) <= 0.0); - while ((rnd7 = returnValue->RandomGen(1, 0, NULL)) <= 0.0); - while ((rnd8 = returnValue->RandomGen(1, 0, NULL)) <= 0.0); + while ((rnd1 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); + while ((rnd2 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); + while ((rnd3 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); + while ((rnd4 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); + while ((rnd5 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); + while ((rnd6 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); + while ((rnd7 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); + while ((rnd8 = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); mitk::pa::LightSource::PhotonInformation info = m_PhotoacousticProbe->GetNextPhoton(rnd1, rnd2, rnd3, rnd4, rnd5, rnd6, rnd7, rnd8); x = info.xPosition; y = yOffset + info.yPosition; z = info.zPosition; ux = info.xAngle; uy = info.yAngle; uz = info.zAngle; if (verbose) std::cout << "Created photon at position (" << x << "|" << y << "|" << z << ") with angles (" << ux << "|" << uy << "|" << uz << ")." << std::endl; } else { /* trajectory */ if (inputValues->launchflag == 1) // manually set launch { x = inputValues->xs; y = inputValues->ys; z = inputValues->zs; ux = inputValues->ux0; uy = inputValues->uy0; uz = inputValues->uz0; } else // use mcflag { if (inputValues->mcflag == 0) // uniform beam { // set launch point and width of beam - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); // avoids rnd = 0 + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); // avoids rnd = 0 r = inputValues->radius*sqrt(rnd); // radius of beam at launch point - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); // avoids rnd = 0 + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); // avoids rnd = 0 phi = rnd*2.0*PI; x = inputValues->xs + r*cos(phi); y = inputValues->ys + r*sin(phi); z = inputValues->zs; // set trajectory toward focus - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); // avoids rnd = 0 + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); // avoids rnd = 0 r = inputValues->waist*sqrt(rnd); // radius of beam at focus - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); // avoids rnd = 0 + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); // avoids rnd = 0 phi = rnd*2.0*PI; // !!!!!!!!!!!!!!!!!!!!!!! setting input values will braek inputValues->xfocus = r*cos(phi); inputValues->yfocus = r*sin(phi); temp = sqrt((x - inputValues->xfocus)*(x - inputValues->xfocus) + (y - inputValues->yfocus)*(y - inputValues->yfocus) + inputValues->zfocus*inputValues->zfocus); ux = -(x - inputValues->xfocus) / temp; uy = -(y - inputValues->yfocus) / temp; uz = sqrt(1 - ux*ux + uy*uy); } else if (inputValues->mcflag == 5) // Multispectral DKFZ prototype { // set launch point and width of beam - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); //offset in x direction in cm (random) x = (rnd*2.5) - 1.25; - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); double b = ((rnd)-0.5); y = (b > 0 ? yOffset + 1.5 : yOffset - 1.5); z = 0.1; ux = 0; - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); //Angle of beam in y direction uy = sin((rnd*0.42) - 0.21 + (b < 0 ? 1.0 : -1.0) * 0.436); - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); // angle of beam in x direction ux = sin((rnd*0.42) - 0.21); uz = sqrt(1 - ux*ux - uy*uy); } else if (inputValues->mcflag == 4) // Monospectral prototype DKFZ { // set launch point and width of beam - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); //offset in x direction in cm (random) x = (rnd*2.5) - 1.25; - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); double b = ((rnd)-0.5); y = (b > 0 ? yOffset + 0.83 : yOffset - 0.83); z = 0.1; ux = 0; - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); //Angle of beam in y direction uy = sin((rnd*0.42) - 0.21 + (b < 0 ? 1.0 : -1.0) * 0.375); - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); // angle of beam in x direction ux = sin((rnd*0.42) - 0.21); uz = sqrt(1 - ux*ux - uy*uy); } else { // isotropic pt source - costheta = 1.0 - 2.0 * returnValue->RandomGen(1, 0, NULL); + costheta = 1.0 - 2.0 * returnValue->RandomGen(1, 0, nullptr); sintheta = sqrt(1.0 - costheta*costheta); - psi = 2.0 * PI * returnValue->RandomGen(1, 0, NULL); + psi = 2.0 * PI * returnValue->RandomGen(1, 0, nullptr); cospsi = cos(psi); if (psi < PI) sinpsi = sqrt(1.0 - cospsi*cospsi); else sinpsi = -sqrt(1.0 - cospsi*cospsi); x = inputValues->xs; y = inputValues->ys; z = inputValues->zs; ux = sintheta*cospsi; uy = sintheta*sinpsi; uz = costheta; } } // end use mcflag } /****************************/ /* Get tissue voxel properties of launchpoint. * If photon beyond outer edge of defined voxels, * the tissue equals properties of outermost voxels. * Therefore, set outermost voxels to infinite background value. */ ix = (int)(inputValues->Nx / 2 + x / inputValues->xSpacing); iy = (int)(inputValues->Ny / 2 + y / inputValues->ySpacing); iz = (int)(z / inputValues->zSpacing); if (ix >= inputValues->Nx) ix = inputValues->Nx - 1; if (iy >= inputValues->Ny) iy = inputValues->Ny - 1; if (iz >= inputValues->Nz) iz = inputValues->Nz - 1; if (ix < 0) ix = 0; if (iy < 0) iy = 0; if (iz < 0) iz = 0; /* Get the tissue type of located voxel */ i = (long)(iz*inputValues->Ny*inputValues->Nx + ix*inputValues->Ny + iy); bflag = 1; // initialize as 1 = inside volume, but later check as photon propagates. if (returnValue->detectorVoxel != nullptr) returnValue->detectorVoxel->recordedPhotonRoute->clear(); /* HOP_DROP_SPIN_CHECK Propagate one photon until it dies as determined by ROULETTE. *******/ do { /**** HOP Take step to new position s = dimensionless stepsize x, uy, uz are cosines of current photon trajectory *****/ - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); /* yields 0 < rnd <= 1 */ + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); /* yields 0 < rnd <= 1 */ sleft = -log(rnd); /* dimensionless step */ CNT += 1; do { // while sleft>0 s = sleft / inputValues->musVector[i]; /* Step size [cm].*/ tempx = x + s*ux; /* Update positions. [cm] */ tempy = y + s*uy; tempz = z + s*uz; sv = returnValue->SameVoxel(x, y, z, tempx, tempy, tempz, inputValues->xSpacing, inputValues->ySpacing, inputValues->zSpacing); if (sv) /* photon in same voxel */ { x = tempx; /* Update positions. */ y = tempy; z = tempz; /**** DROP Drop photon weight (W) into local bin. *****/ absorb = W*(1 - exp(-inputValues->muaVector[i] * s)); /* photon weight absorbed at this step */ W -= absorb; /* decrement WEIGHT by amount absorbed */ // If photon within volume of heterogeneity, deposit energy in F[]. // Normalize F[] later, when save output. if (bflag) { i = (long)(iz*inputValues->Ny*inputValues->Nx + ix*inputValues->Ny + iy); returnValue->totalFluence[i] += absorb; // only save data if blag==1, i.e., photon inside simulation cube //For each detectorvoxel if (returnValue->detectorVoxel != nullptr) { //Add photon position to the recorded photon route returnValue->detectorVoxel->recordedPhotonRoute->push_back(initLocation(ix, iy, iz, absorb)); //If the photon is currently at the detector position if ((returnValue->detectorVoxel->location.x == ix) && ((returnValue->detectorVoxel->location.y == iy) || (returnValue->detectorVoxel->location.y - 1 == iy)) && (returnValue->detectorVoxel->location.z == iz)) { //For each voxel in the recorded photon route for (unsigned int routeIndex = 0; routeIndex < returnValue->detectorVoxel->recordedPhotonRoute->size(); routeIndex++) { //increment the fluence contribution at that particular position i = (long)(returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).z*inputValues->Ny*inputValues->Nx + returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).x*inputValues->Ny + returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).y); returnValue->detectorVoxel->fluenceContribution[i] += returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).absorb; } //Clear the recorded photon route returnValue->detectorVoxel->m_NumberPhotonsCurrent++; returnValue->detectorVoxel->recordedPhotonRoute->clear(); } } } /* Update sleft */ sleft = 0; /* dimensionless step remaining */ } else /* photon has crossed voxel boundary */ { /* step to voxel face + "littlest step" so just inside new voxel. */ s = ls + returnValue->FindVoxelFace2(x, y, z, tempx, tempy, tempz, inputValues->xSpacing, inputValues->ySpacing, inputValues->zSpacing, ux, uy, uz); /**** DROP Drop photon weight (W) into local bin. *****/ absorb = W*(1 - exp(-inputValues->muaVector[i] * s)); /* photon weight absorbed at this step */ W -= absorb; /* decrement WEIGHT by amount absorbed */ // If photon within volume of heterogeneity, deposit energy in F[]. // Normalize F[] later, when save output. if (bflag) { // only save data if bflag==1, i.e., photon inside simulation cube //For each detectorvoxel if (returnValue->detectorVoxel != nullptr) { //Add photon position to the recorded photon route returnValue->detectorVoxel->recordedPhotonRoute->push_back(initLocation(ix, iy, iz, absorb)); //If the photon is currently at the detector position if ((returnValue->detectorVoxel->location.x == ix) && ((returnValue->detectorVoxel->location.y == iy) || (returnValue->detectorVoxel->location.y - 1 == iy)) && (returnValue->detectorVoxel->location.z == iz)) { //For each voxel in the recorded photon route for (unsigned int routeIndex = 0; routeIndex < returnValue->detectorVoxel->recordedPhotonRoute->size(); routeIndex++) { //increment the fluence contribution at that particular position i = (long)(returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).z*inputValues->Ny*inputValues->Nx + returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).x*inputValues->Ny + returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).y); returnValue->detectorVoxel->fluenceContribution[i] += returnValue->detectorVoxel->recordedPhotonRoute->at(routeIndex).absorb; } //Clear the recorded photon route returnValue->detectorVoxel->m_NumberPhotonsCurrent++; returnValue->detectorVoxel->recordedPhotonRoute->clear(); } } i = (long)(iz*inputValues->Ny*inputValues->Nx + ix*inputValues->Ny + iy); returnValue->totalFluence[i] += absorb; } /* Update sleft */ sleft -= s*inputValues->musVector[i]; /* dimensionless step remaining */ if (sleft <= ls) sleft = 0; /* Update positions. */ x += s*ux; y += s*uy; z += s*uz; // pointers to voxel containing optical properties ix = (int)(inputValues->Nx / 2 + x / inputValues->xSpacing); iy = (int)(inputValues->Ny / 2 + y / inputValues->ySpacing); iz = (int)(z / inputValues->zSpacing); bflag = 1; // Boundary flag. Initialize as 1 = inside volume, then check. if (inputValues->boundaryflag == 0) { // Infinite medium. // Check if photon has wandered outside volume. // If so, set tissue type to boundary value, but let photon wander. // Set blag to zero, so DROP does not deposit energy. if (iz >= inputValues->Nz) { iz = inputValues->Nz - 1; bflag = 0; } if (ix >= inputValues->Nx) { ix = inputValues->Nx - 1; bflag = 0; } if (iy >= inputValues->Ny) { iy = inputValues->Ny - 1; bflag = 0; } if (iz < 0) { iz = 0; bflag = 0; } if (ix < 0) { ix = 0; bflag = 0; } if (iy < 0) { iy = 0; bflag = 0; } } else if (inputValues->boundaryflag == 1) { // Escape at boundaries if (iz >= inputValues->Nz) { iz = inputValues->Nz - 1; photon_status = DEAD; sleft = 0; } if (ix >= inputValues->Nx) { ix = inputValues->Nx - 1; photon_status = DEAD; sleft = 0; } if (iy >= inputValues->Ny) { iy = inputValues->Ny - 1; photon_status = DEAD; sleft = 0; } if (iz < 0) { iz = 0; photon_status = DEAD; sleft = 0; } if (ix < 0) { ix = 0; photon_status = DEAD; sleft = 0; } if (iy < 0) { iy = 0; photon_status = DEAD; sleft = 0; } } else if (inputValues->boundaryflag == 2) { // Escape at top surface, no x,y bottom z boundaries if (iz >= inputValues->Nz) { iz = inputValues->Nz - 1; bflag = 0; } if (ix >= inputValues->Nx) { ix = inputValues->Nx - 1; bflag = 0; } if (iy >= inputValues->Ny) { iy = inputValues->Ny - 1; bflag = 0; } if (iz < 0) { iz = 0; photon_status = DEAD; sleft = 0; } if (ix < 0) { ix = 0; bflag = 0; } if (iy < 0) { iy = 0; bflag = 0; } } // update pointer to tissue type i = (long)(iz*inputValues->Ny*inputValues->Nx + ix*inputValues->Ny + iy); } //(sv) /* same voxel */ } while (sleft > 0); //do...while /**** SPIN Scatter photon into new trajectory defined by theta and psi. Theta is specified by cos(theta), which is determined based on the Henyey-Greenstein scattering function. Convert theta and psi into cosines ux, uy, uz. *****/ /* Sample for costheta */ - while ((rnd = returnValue->RandomGen(1, 0, NULL)) <= 0.0); + while ((rnd = returnValue->RandomGen(1, 0, nullptr)) <= 0.0); if (inputValues->gVector[i] == 0.0) { costheta = 2.0 * rnd - 1.0; } else { double temp = (1.0 - inputValues->gVector[i] * inputValues->gVector[i]) / (1.0 - inputValues->gVector[i] + 2 * inputValues->gVector[i] * rnd); costheta = (1.0 + inputValues->gVector[i] * inputValues->gVector[i] - temp*temp) / (2.0*inputValues->gVector[i]); } sintheta = sqrt(1.0 - costheta*costheta); /* sqrt() is faster than sin(). */ /* Sample psi. */ - psi = 2.0*PI*returnValue->RandomGen(1, 0, NULL); + psi = 2.0*PI*returnValue->RandomGen(1, 0, nullptr); cospsi = cos(psi); if (psi < PI) sinpsi = sqrt(1.0 - cospsi*cospsi); /* sqrt() is faster than sin(). */ else sinpsi = -sqrt(1.0 - cospsi*cospsi); /* New trajectory. */ if (1 - fabs(uz) <= ONE_MINUS_COSZERO) { /* close to perpendicular. */ uxx = sintheta * cospsi; uyy = sintheta * sinpsi; uzz = costheta * SIGN(uz); /* SIGN() is faster than division. */ } else { /* usually use this option */ temp = sqrt(1.0 - uz * uz); uxx = sintheta * (ux * uz * cospsi - uy * sinpsi) / temp + ux * costheta; uyy = sintheta * (uy * uz * cospsi + ux * sinpsi) / temp + uy * costheta; uzz = -sintheta * cospsi * temp + uz * costheta; } /* Update trajectory */ ux = uxx; uy = uyy; uz = uzz; /**** CHECK ROULETTE If photon weight below THRESHOLD, then terminate photon using Roulette technique. Photon has CHANCE probability of having its weight increased by factor of 1/CHANCE, and 1-CHANCE probability of terminating. *****/ if (W < THRESHOLD) { - if (returnValue->RandomGen(1, 0, NULL) <= CHANCE) + if (returnValue->RandomGen(1, 0, nullptr) <= CHANCE) W /= CHANCE; else photon_status = DEAD; } } while (photon_status == ALIVE); /* end STEP_CHECK_HOP_SPIN */ /* if ALIVE, continue propagating */ /* If photon DEAD, then launch new photon. */ } while (photonIterator < photonsToSimulate); /* end RUN */ returnValue->Nphotons += photonsToSimulate; } while (photonsToSimulate > 0); if (verbose) std::cout << "------------------------------------------------------" << std::endl; if (verbose) std::cout << "Thread " << thread << " is finished." << std::endl; } diff --git a/Modules/PhotoacousticsLib/src/Utils/ProbeDesign/mitkPAProbe.cpp b/Modules/PhotoacousticsLib/src/Utils/ProbeDesign/mitkPAProbe.cpp index d38db4a9a6..4aebe6f9b6 100644 --- a/Modules/PhotoacousticsLib/src/Utils/ProbeDesign/mitkPAProbe.cpp +++ b/Modules/PhotoacousticsLib/src/Utils/ProbeDesign/mitkPAProbe.cpp @@ -1,133 +1,133 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPAProbe.h" mitk::pa::Probe::Probe(std::string file, bool verbose) : m_TotalEnergy(0), m_Verbose(verbose) { TiXmlDocument xmlDoc(file); bool success = xmlDoc.LoadFile(TIXML_ENCODING_UTF8); if (m_Verbose) { std::cout << "reading of document was " << (success ? "" : "not ") << "successful" << std::endl; std::cout << "Content of the Xml file:" << std::endl; xmlDoc.Print(); } if (success) { InitProbe(xmlDoc); } else { if (m_Verbose) { std::cerr << "Could not load xml file" << std::endl; } } } mitk::pa::Probe::Probe(const char* fileStream, bool verbose) : m_TotalEnergy(0), m_Verbose(verbose) { TiXmlDocument xmlDoc; - const char* success = xmlDoc.Parse(fileStream, 0, TIXML_ENCODING_UTF8); + const char* success = xmlDoc.Parse(fileStream, nullptr, TIXML_ENCODING_UTF8); if (m_Verbose) { std::cout << "reading document was " << (success == nullptr ? "" : "not ") << "successful (" << (success == nullptr ? "NULL" : success) << ")" << std::endl; std::cout << "Content of the Xml file:" << std::endl; xmlDoc.Print(); } if (success == nullptr || atoi(success) == 0) { InitProbe(xmlDoc); } else { if (m_Verbose) { std::cerr << "Could not load xml file" << std::endl; } } } mitk::pa::Probe::~Probe() { } mitk::pa::LightSource::PhotonInformation mitk::pa::Probe::GetNextPhoton(double rng1, double rnd2, double rnd3, double rnd4, double rnd5, double rnd6, double rnd7, double rnd8) { rng1 = rng1*m_TotalEnergy; double currentEnergy = 0; for (LightSource::Pointer lightSource : m_LightSources) { currentEnergy += lightSource->GetEnergy(); if (currentEnergy >= rng1) return lightSource->GetNextPhoton(rnd2, rnd3, rnd4, rnd5, rnd6, rnd7, rnd8); } //Last resort: If something goes wrong, return a position from the first source. return m_LightSources[0]->GetNextPhoton(rnd2, rnd3, rnd4, rnd5, rnd6, rnd7, rnd8); } bool mitk::pa::Probe::IsValid() { return m_IsValid; } void mitk::pa::Probe::InitProbe(TiXmlDocument xmlDoc) { m_IsValid = true; TiXmlElement* root = xmlDoc.FirstChildElement(XML_TAG_PROBE); if (root) { for (TiXmlElement* element = root->FirstChildElement(XML_TAG_LIGHT_SOURCE); element != nullptr; element = element->NextSiblingElement(XML_TAG_LIGHT_SOURCE)) { LightSource::Pointer lightSource = LightSource::New(element, m_Verbose); if (lightSource.IsNotNull() && lightSource->IsValid()) { m_LightSources.push_back(lightSource); m_TotalEnergy += lightSource->GetEnergy(); } else { m_IsValid = false; } } } else { m_IsValid = false; } if (!m_IsValid) { std::cerr << "Creation of a valid Photoacoustic Probe failed." << std::endl; } else { if (m_Verbose) { std::cout << "Successfully created Photoacoustic Probe." << std::endl; } } } diff --git a/Modules/PlanarFigure/src/Rendering/mitkPlanarFigureMapper2D.cpp b/Modules/PlanarFigure/src/Rendering/mitkPlanarFigureMapper2D.cpp index f4fe4d4ea9..5d7b1144fd 100644 --- a/Modules/PlanarFigure/src/Rendering/mitkPlanarFigureMapper2D.cpp +++ b/Modules/PlanarFigure/src/Rendering/mitkPlanarFigureMapper2D.cpp @@ -1,971 +1,971 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlanarFigureMapper2D.h" #include "mitkBaseRenderer.h" #include "mitkColorProperty.h" #include "vtkContext2D.h" #include "vtkContextDevice2D.h" #include "vtkOpenGLContextDevice2D.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include "vtkTextProperty.h" #define _USE_MATH_DEFINES #include mitk::PlanarFigureMapper2D::PlanarFigureMapper2D() : m_NodeModified(true), m_NodeModifiedObserverTag(0), m_NodeModifiedObserverAdded(false), m_Initialized(false) { this->InitializeDefaultPlanarFigureProperties(); } mitk::PlanarFigureMapper2D::~PlanarFigureMapper2D() { if (m_NodeModifiedObserverAdded && GetDataNode() != nullptr) { GetDataNode()->RemoveObserver(m_NodeModifiedObserverTag); } } void mitk::PlanarFigureMapper2D::ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * /*actor*/) { float rgba[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; // check for color prop and use it for rendering if it exists GetDataNode()->GetColor(rgba, renderer, "color"); // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(rgba[3], renderer, "opacity"); this->m_Pen->SetColorF((double)rgba[0], (double)rgba[1], (double)rgba[2], (double)rgba[3]); } void mitk::PlanarFigureMapper2D::Initialize(mitk::BaseRenderer *) { this->m_Pen = vtkSmartPointer::New(); - vtkOpenGLContextDevice2D *device = NULL; + vtkOpenGLContextDevice2D *device = nullptr; device = vtkOpenGLContextDevice2D::New(); if (device) { this->m_Context->Begin(device); device->Delete(); this->m_Initialized = true; this->m_Context->ApplyPen(this->m_Pen); } else { } } void mitk::PlanarFigureMapper2D::MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) { if (type != mitk::VtkPropRenderer::Overlay) return; if (!this->m_Initialized) { this->Initialize(renderer); } vtkOpenGLContextDevice2D::SafeDownCast( this->m_Context->GetDevice())->Begin(renderer->GetVtkRenderer()); bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; // Get PlanarFigure from input mitk::PlanarFigure *planarFigure = const_cast(static_cast(GetDataNode()->GetData())); // Check if PlanarFigure has already been placed; otherwise, do nothing if (!planarFigure->IsPlaced()) { return; } // Get 2D geometry frame of PlanarFigure const mitk::PlaneGeometry *planarFigurePlaneGeometry = planarFigure->GetPlaneGeometry(); if (planarFigurePlaneGeometry == nullptr) { MITK_ERROR << "PlanarFigure does not have valid PlaneGeometry!"; return; } // Get current world 2D geometry from renderer const mitk::PlaneGeometry *rendererPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry(); // If the PlanarFigure geometry is a plane geometry, check if current // world plane is parallel to and within the planar figure geometry bounds // (otherwise, display nothing) if ((planarFigurePlaneGeometry != nullptr) && (rendererPlaneGeometry != nullptr)) { double planeThickness = planarFigurePlaneGeometry->GetExtentInMM(2); if (!planarFigurePlaneGeometry->IsParallel(rendererPlaneGeometry) || !(planarFigurePlaneGeometry->DistanceFromPlane(rendererPlaneGeometry) < planeThickness / 3.0)) { // Planes are not parallel or renderer plane is not within PlanarFigure // geometry bounds --> exit return; } } else { // Plane is not valid (curved reformations are not possible yet) return; } // Apply visual appearance properties from the PropertyList ApplyColorAndOpacityProperties(renderer); // Get properties from node (if present) const mitk::DataNode *node = this->GetDataNode(); this->InitializePlanarFigurePropertiesFromDataNode(node); PlanarFigureDisplayMode lineDisplayMode = PF_DEFAULT; if (m_IsSelected) { lineDisplayMode = PF_SELECTED; } else if (m_IsHovering) { lineDisplayMode = PF_HOVER; } mitk::Point2D anchorPoint; anchorPoint[0] = 0; anchorPoint[1] = 1; // render the actual lines of the PlanarFigure RenderLines(lineDisplayMode, planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); // position-offset of the annotations, is set in RenderAnnotations() and // used in RenderQuantities() double annotationOffset = 0.0; // Get Global Opacity float globalOpacity = 1.0; node->GetFloatProperty("opacity", globalOpacity); if (m_DrawControlPoints) { // draw the control-points RenderControlPoints(planarFigure, lineDisplayMode, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } // draw name near the anchor point (point located on the right) const std::string name = node->GetName(); if (m_DrawName && !name.empty()) { RenderAnnotations(renderer, name, anchorPoint, globalOpacity, lineDisplayMode, annotationOffset); } // draw feature quantities (if requested) next to the anchor point, // but under the name (that is where 'annotationOffset' is used) if (m_DrawQuantities) { RenderQuantities(planarFigure, renderer, anchorPoint, annotationOffset, globalOpacity, lineDisplayMode); } this->m_Context->GetDevice()->End(); } void mitk::PlanarFigureMapper2D::PaintPolyLine(const mitk::PlanarFigure::PolyLineType vertices, bool closed, Point2D &anchorPoint, const PlaneGeometry *planarFigurePlaneGeometry, const PlaneGeometry *rendererPlaneGeometry, const mitk::BaseRenderer *renderer) { mitk::Point2D rightMostPoint; rightMostPoint.Fill(itk::NumericTraits::min()); // transform all vertices into Point2Ds in display-Coordinates and store them in vector std::vector pointlist; for (auto iter = vertices.cbegin(); iter != vertices.cend(); ++iter) { // Draw this 2D point as OpenGL vertex mitk::Point2D displayPoint; this->TransformObjectToDisplay(*iter, displayPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); pointlist.push_back(displayPoint); if (displayPoint[0] > rightMostPoint[0]) rightMostPoint = displayPoint; } // If the planarfigure is closed, we add the first control point again. // Thus we can always use 'GL_LINE_STRIP' and get rid of strange flickering // effect when using the MESA OpenGL library. if (closed) { mitk::Point2D displayPoint; this->TransformObjectToDisplay( vertices.cbegin()[0], displayPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); pointlist.push_back(displayPoint); } // now paint all the points in one run float* points = new float[pointlist.size()*2]; for (unsigned int i = 0 ; i < pointlist.size() ; ++i) { points[i * 2] = pointlist[i][0]; points[i * 2 + 1] = pointlist[i][1]; } this->m_Context->DrawPoly(points,pointlist.size()); anchorPoint = rightMostPoint; } void mitk::PlanarFigureMapper2D::DrawMainLines(mitk::PlanarFigure *figure, Point2D &anchorPoint, const PlaneGeometry *planarFigurePlaneGeometry, const PlaneGeometry *rendererPlaneGeometry, const mitk::BaseRenderer *renderer) { const auto numberOfPolyLines = figure->GetPolyLinesSize(); for (auto loop = 0; loop < numberOfPolyLines; ++loop) { const auto polyline = figure->GetPolyLine(loop); this->PaintPolyLine( polyline, figure->IsClosed(), anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } } void mitk::PlanarFigureMapper2D::DrawHelperLines(mitk::PlanarFigure *figure, Point2D &anchorPoint, const PlaneGeometry *planarFigurePlaneGeometry, const PlaneGeometry *rendererPlaneGeometry, const mitk::BaseRenderer *renderer) { const auto numberOfHelperPolyLines = figure->GetHelperPolyLinesSize(); // Draw helper objects for (unsigned int loop = 0; loop < numberOfHelperPolyLines; ++loop) { const auto helperPolyLine = figure->GetHelperPolyLine(loop, renderer->GetScaleFactorMMPerDisplayUnit(), renderer->GetViewportSize()[1]); // Check if the current helper objects is to be painted if (!figure->IsHelperToBePainted(loop)) { continue; } // ... and once normally above the shadow. this->PaintPolyLine(helperPolyLine, false, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } } void mitk::PlanarFigureMapper2D::TransformObjectToDisplay(const mitk::Point2D &point2D, mitk::Point2D &displayPoint, const mitk::PlaneGeometry *objectGeometry, const mitk::PlaneGeometry * /*rendererGeometry*/, const mitk::BaseRenderer *renderer) { mitk::Point3D point3D; // Map circle point from local 2D geometry into 3D world space objectGeometry->Map(point2D, point3D); // Project 3D world point onto display geometry renderer->WorldToView(point3D, displayPoint); } void mitk::PlanarFigureMapper2D::DrawMarker(const mitk::Point2D &point, float *lineColor, float lineOpacity, float *markerColor, float markerOpacity, float lineWidth, PlanarFigureControlPointStyleProperty::Shape shape, const mitk::PlaneGeometry *objectGeometry, const mitk::PlaneGeometry *rendererGeometry, const mitk::BaseRenderer *renderer) { if (this->GetDataNode() != nullptr && this->GetDataNode()->GetDataInteractor().IsNull()) return; if (markerOpacity == 0 && lineOpacity == 0) return; mitk::Point2D displayPoint; this->TransformObjectToDisplay(point, displayPoint, objectGeometry, rendererGeometry, renderer); this->m_Context->GetPen()->SetColorF((double)markerColor[0], (double)markerColor[1], (double)markerColor[2], markerOpacity); this->m_Context->GetPen()->SetWidth(lineWidth); switch (shape) { case PlanarFigureControlPointStyleProperty::Square: default: { // Paint filled square if (markerOpacity > 0) { m_Context->DrawRect(displayPoint[0] - 4, displayPoint[1] - 4, 8, 8); } // Paint outline this->m_Context->GetPen()->SetColorF((double)lineColor[0], (double)lineColor[1], (double)lineColor[2], (double)lineOpacity); float* outline = new float[8]; outline[0] = displayPoint[0] - 4; outline[1] = displayPoint[1] - 4; outline[2] = outline[0]; outline[3] = displayPoint[1] + 4; outline[4] = displayPoint[0] + 4; outline[5] = outline[3]; outline[6] = outline[4]; outline[7] = outline[1]; m_Context->DrawLines(outline, 4); break; } case PlanarFigureControlPointStyleProperty::Circle: { // TODO: This code can not be reached using the properties provided in the GUI /*float radius = 4.0; if (markerOpacity > 0) { // Paint filled circle glBegin(GL_POLYGON); for (int angle = 0; angle < 8; ++angle) { float angleRad = angle * (float)3.14159 / 4.0; float x = displayPoint[0] + radius * (float)cos(angleRad); float y = displayPoint[1] + radius * (float)sin(angleRad); glVertex3f(x, y, PLANAR_OFFSET); } glEnd(); } // Paint outline glColor4f(lineColor[0], lineColor[1], lineColor[2], lineOpacity); glBegin(GL_LINE_LOOP); for (int angle = 0; angle < 8; ++angle) { float angleRad = angle * (float)3.14159 / 4.0; float x = displayPoint[0] + radius * (float)cos(angleRad); float y = displayPoint[1] + radius * (float)sin(angleRad); glVertex3f(x, y, PLANAR_OFFSET); } glEnd();*/ break; } } // end switch } void mitk::PlanarFigureMapper2D::InitializeDefaultPlanarFigureProperties() { m_IsSelected = false; m_IsHovering = false; m_DrawOutline = false; m_DrawQuantities = false; m_DrawShadow = false; m_DrawControlPoints = false; m_DrawName = true; m_DrawDashed = false; m_DrawHelperDashed = false; m_AnnotationsShadow = false; m_ShadowWidthFactor = 1.2; m_LineWidth = 1.0; m_OutlineWidth = 4.0; m_HelperlineWidth = 2.0; m_DevicePixelRatio = 1.0; m_ControlPointShape = PlanarFigureControlPointStyleProperty::Square; this->SetColorProperty(m_LineColor, PF_DEFAULT, 1.0, 1.0, 1.0); this->SetFloatProperty(m_LineOpacity, PF_DEFAULT, 1.0); this->SetColorProperty(m_OutlineColor, PF_DEFAULT, 0.0, 0.0, 1.0); this->SetFloatProperty(m_OutlineOpacity, PF_DEFAULT, 1.0); this->SetColorProperty(m_HelperlineColor, PF_DEFAULT, 0.4, 0.8, 0.2); this->SetFloatProperty(m_HelperlineOpacity, PF_DEFAULT, 0.4); this->SetColorProperty(m_MarkerlineColor, PF_DEFAULT, 1.0, 1.0, 1.0); this->SetFloatProperty(m_MarkerlineOpacity, PF_DEFAULT, 1.0); this->SetColorProperty(m_MarkerColor, PF_DEFAULT, 1.0, 1.0, 1.0); this->SetFloatProperty(m_MarkerOpacity, PF_DEFAULT, 0.0); this->SetColorProperty(m_AnnotationColor, PF_DEFAULT, 1.0, 1.0, 1.0); this->SetColorProperty(m_LineColor, PF_HOVER, 1.0, 0.7, 0.0); this->SetFloatProperty(m_LineOpacity, PF_HOVER, 1.0); this->SetColorProperty(m_OutlineColor, PF_HOVER, 0.0, 0.0, 1.0); this->SetFloatProperty(m_OutlineOpacity, PF_HOVER, 1.0); this->SetColorProperty(m_HelperlineColor, PF_HOVER, 0.4, 0.8, 0.2); this->SetFloatProperty(m_HelperlineOpacity, PF_HOVER, 0.4); this->SetColorProperty(m_MarkerlineColor, PF_HOVER, 1.0, 1.0, 1.0); this->SetFloatProperty(m_MarkerlineOpacity, PF_HOVER, 1.0); this->SetColorProperty(m_MarkerColor, PF_HOVER, 1.0, 0.6, 0.0); this->SetFloatProperty(m_MarkerOpacity, PF_HOVER, 0.2); this->SetColorProperty(m_AnnotationColor, PF_HOVER, 1.0, 0.7, 0.0); this->SetColorProperty(m_LineColor, PF_SELECTED, 1.0, 0.0, 0.0); this->SetFloatProperty(m_LineOpacity, PF_SELECTED, 1.0); this->SetColorProperty(m_OutlineColor, PF_SELECTED, 0.0, 0.0, 1.0); this->SetFloatProperty(m_OutlineOpacity, PF_SELECTED, 1.0); this->SetColorProperty(m_HelperlineColor, PF_SELECTED, 0.4, 0.8, 0.2); this->SetFloatProperty(m_HelperlineOpacity, PF_SELECTED, 0.4); this->SetColorProperty(m_MarkerlineColor, PF_SELECTED, 1.0, 1.0, 1.0); this->SetFloatProperty(m_MarkerlineOpacity, PF_SELECTED, 1.0); this->SetColorProperty(m_MarkerColor, PF_SELECTED, 1.0, 0.6, 0.0); this->SetFloatProperty(m_MarkerOpacity, PF_SELECTED, 1.0); this->SetColorProperty(m_AnnotationColor, PF_SELECTED, 1.0, 0.0, 0.0); } void mitk::PlanarFigureMapper2D::InitializePlanarFigurePropertiesFromDataNode(const mitk::DataNode *node) { if (node == nullptr) { return; } // if we have not added an observer for ModifiedEvents on the DataNode, // we add one now. if (!m_NodeModifiedObserverAdded) { itk::SimpleMemberCommand::Pointer nodeModifiedCommand = itk::SimpleMemberCommand::New(); nodeModifiedCommand->SetCallbackFunction(this, &mitk::PlanarFigureMapper2D::OnNodeModified); m_NodeModifiedObserverTag = node->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); m_NodeModifiedObserverAdded = true; } // If the DataNode has not been modified since the last execution of // this method, we do not run it now. if (!m_NodeModified) return; // Mark the current properties as unmodified m_NodeModified = false; // Get Global Opacity float globalOpacity = 1.0; node->GetFloatProperty("opacity", globalOpacity); node->GetBoolProperty("selected", m_IsSelected); node->GetBoolProperty("planarfigure.ishovering", m_IsHovering); node->GetBoolProperty("planarfigure.drawoutline", m_DrawOutline); node->GetBoolProperty("planarfigure.drawshadow", m_DrawShadow); node->GetBoolProperty("planarfigure.drawquantities", m_DrawQuantities); node->GetBoolProperty("planarfigure.drawcontrolpoints", m_DrawControlPoints); node->GetBoolProperty("planarfigure.drawname", m_DrawName); node->GetBoolProperty("planarfigure.drawdashed", m_DrawDashed); node->GetBoolProperty("planarfigure.helperline.drawdashed", m_DrawHelperDashed); node->GetFloatProperty("planarfigure.line.width", m_LineWidth); node->GetFloatProperty("planarfigure.shadow.widthmodifier", m_ShadowWidthFactor); node->GetFloatProperty("planarfigure.outline.width", m_OutlineWidth); node->GetFloatProperty("planarfigure.helperline.width", m_HelperlineWidth); node->GetFloatProperty("planarfigure.devicepixelratio", m_DevicePixelRatio); node->GetStringProperty("planarfigure.annotations.font.family", m_AnnotationFontFamily); node->GetBoolProperty("planarfigure.annotations.font.bold", m_DrawAnnotationBold); node->GetBoolProperty("planarfigure.annotations.font.italic", m_DrawAnnotationItalic); node->GetIntProperty("planarfigure.annotations.font.size", m_AnnotationSize); if (!node->GetBoolProperty("planarfigure.annotations.shadow", m_AnnotationsShadow)) { node->GetBoolProperty("planarfigure.drawshadow", m_AnnotationsShadow); } PlanarFigureControlPointStyleProperty::Pointer styleProperty = dynamic_cast(node->GetProperty("planarfigure.controlpointshape")); if (styleProperty.IsNotNull()) { m_ControlPointShape = styleProperty->GetShape(); } // Set default color and opacity // If property "planarfigure.default.*.color" exists, then use that color. Otherwise global "color" property is used. if (!node->GetColor(m_LineColor[PF_DEFAULT], nullptr, "planarfigure.default.line.color")) { node->GetColor(m_LineColor[PF_DEFAULT], nullptr, "color"); } node->GetFloatProperty("planarfigure.default.line.opacity", m_LineOpacity[PF_DEFAULT]); if (!node->GetColor(m_OutlineColor[PF_DEFAULT], nullptr, "planarfigure.default.outline.color")) { node->GetColor(m_OutlineColor[PF_DEFAULT], nullptr, "color"); } node->GetFloatProperty("planarfigure.default.outline.opacity", m_OutlineOpacity[PF_DEFAULT]); if (!node->GetColor(m_HelperlineColor[PF_DEFAULT], nullptr, "planarfigure.default.helperline.color")) { node->GetColor(m_HelperlineColor[PF_DEFAULT], nullptr, "color"); } node->GetFloatProperty("planarfigure.default.helperline.opacity", m_HelperlineOpacity[PF_DEFAULT]); node->GetColor(m_MarkerlineColor[PF_DEFAULT], nullptr, "planarfigure.default.markerline.color"); node->GetFloatProperty("planarfigure.default.markerline.opacity", m_MarkerlineOpacity[PF_DEFAULT]); node->GetColor(m_MarkerColor[PF_DEFAULT], nullptr, "planarfigure.default.marker.color"); node->GetFloatProperty("planarfigure.default.marker.opacity", m_MarkerOpacity[PF_DEFAULT]); if (!node->GetColor(m_AnnotationColor[PF_DEFAULT], nullptr, "planarfigure.default.annotation.color")) { if (!node->GetColor(m_AnnotationColor[PF_DEFAULT], nullptr, "planarfigure.default.line.color")) { node->GetColor(m_AnnotationColor[PF_DEFAULT], nullptr, "color"); } } // Set hover color and opacity node->GetColor(m_LineColor[PF_HOVER], nullptr, "planarfigure.hover.line.color"); node->GetFloatProperty("planarfigure.hover.line.opacity", m_LineOpacity[PF_HOVER]); node->GetColor(m_OutlineColor[PF_HOVER], nullptr, "planarfigure.hover.outline.color"); node->GetFloatProperty("planarfigure.hover.outline.opacity", m_OutlineOpacity[PF_HOVER]); node->GetColor(m_HelperlineColor[PF_HOVER], nullptr, "planarfigure.hover.helperline.color"); node->GetFloatProperty("planarfigure.hover.helperline.opacity", m_HelperlineOpacity[PF_HOVER]); node->GetColor(m_MarkerlineColor[PF_HOVER], nullptr, "planarfigure.hover.markerline.color"); node->GetFloatProperty("planarfigure.hover.markerline.opacity", m_MarkerlineOpacity[PF_HOVER]); node->GetColor(m_MarkerColor[PF_HOVER], nullptr, "planarfigure.hover.marker.color"); node->GetFloatProperty("planarfigure.hover.marker.opacity", m_MarkerOpacity[PF_HOVER]); if (!node->GetColor(m_AnnotationColor[PF_HOVER], nullptr, "planarfigure.hover.annotation.color")) { if (!node->GetColor(m_AnnotationColor[PF_HOVER], nullptr, "planarfigure.hover.line.color")) { node->GetColor(m_AnnotationColor[PF_HOVER], nullptr, "color"); } } // Set selected color and opacity node->GetColor(m_LineColor[PF_SELECTED], nullptr, "planarfigure.selected.line.color"); node->GetFloatProperty("planarfigure.selected.line.opacity", m_LineOpacity[PF_SELECTED]); node->GetColor(m_OutlineColor[PF_SELECTED], nullptr, "planarfigure.selected.outline.color"); node->GetFloatProperty("planarfigure.selected.outline.opacity", m_OutlineOpacity[PF_SELECTED]); node->GetColor(m_HelperlineColor[PF_SELECTED], nullptr, "planarfigure.selected.helperline.color"); node->GetFloatProperty("planarfigure.selected.helperline.opacity", m_HelperlineOpacity[PF_SELECTED]); node->GetColor(m_MarkerlineColor[PF_SELECTED], nullptr, "planarfigure.selected.markerline.color"); node->GetFloatProperty("planarfigure.selected.markerline.opacity", m_MarkerlineOpacity[PF_SELECTED]); node->GetColor(m_MarkerColor[PF_SELECTED], nullptr, "planarfigure.selected.marker.color"); node->GetFloatProperty("planarfigure.selected.marker.opacity", m_MarkerOpacity[PF_SELECTED]); if (!node->GetColor(m_AnnotationColor[PF_SELECTED], nullptr, "planarfigure.selected.annotation.color")) { if (!node->GetColor(m_AnnotationColor[PF_SELECTED], nullptr, "planarfigure.selected.line.color")) { node->GetColor(m_AnnotationColor[PF_SELECTED], nullptr, "color"); } } // adapt opacity values to global "opacity" property for (unsigned int i = 0; i < PF_COUNT; ++i) { m_LineOpacity[i] *= globalOpacity; m_OutlineOpacity[i] *= globalOpacity; m_HelperlineOpacity[i] *= globalOpacity; m_MarkerlineOpacity[i] *= globalOpacity; m_MarkerOpacity[i] *= globalOpacity; } } void mitk::PlanarFigureMapper2D::OnNodeModified() { m_NodeModified = true; } void mitk::PlanarFigureMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("visible", mitk::BoolProperty::New(true), renderer, overwrite); // node->SetProperty("planarfigure.iseditable",mitk::BoolProperty::New(true)); node->AddProperty("planarfigure.isextendable", mitk::BoolProperty::New(false)); // node->AddProperty( "planarfigure.ishovering", mitk::BoolProperty::New(true) ); node->AddProperty("planarfigure.drawoutline", mitk::BoolProperty::New(false)); // node->AddProperty( "planarfigure.drawquantities", mitk::BoolProperty::New(true) ); node->AddProperty("planarfigure.drawshadow", mitk::BoolProperty::New(true)); node->AddProperty("planarfigure.drawcontrolpoints", mitk::BoolProperty::New(true)); node->AddProperty("planarfigure.drawname", mitk::BoolProperty::New(true)); node->AddProperty("planarfigure.drawdashed", mitk::BoolProperty::New(false)); node->AddProperty("planarfigure.helperline.drawdashed", mitk::BoolProperty::New(false)); node->AddProperty("planarfigure.annotations.font.family", mitk::StringProperty::New("Arial")); node->AddProperty("planarfigure.annotations.font.bold", mitk::BoolProperty::New(false)); node->AddProperty("planarfigure.annotations.font.italic", mitk::BoolProperty::New(false)); node->AddProperty("planarfigure.annotations.font.size", mitk::IntProperty::New(12)); node->AddProperty("planarfigure.line.width", mitk::FloatProperty::New(2.0)); node->AddProperty("planarfigure.shadow.widthmodifier", mitk::FloatProperty::New(2.0)); node->AddProperty("planarfigure.outline.width", mitk::FloatProperty::New(2.0)); node->AddProperty("planarfigure.helperline.width", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.default.line.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.default.outline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.default.helperline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.default.markerline.color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); node->AddProperty("planarfigure.default.markerline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.default.marker.color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); node->AddProperty("planarfigure.default.marker.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.hover.line.color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); node->AddProperty("planarfigure.hover.line.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.hover.outline.color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); node->AddProperty("planarfigure.hover.outline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.hover.helperline.color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); node->AddProperty("planarfigure.hover.helperline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.hover.markerline.color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); node->AddProperty("planarfigure.hover.markerline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.hover.marker.color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); node->AddProperty("planarfigure.hover.marker.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.selected.line.color", mitk::ColorProperty::New(1.0, 0.0, 0.0)); node->AddProperty("planarfigure.selected.line.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.selected.outline.color", mitk::ColorProperty::New(1.0, 0.0, 0.0)); node->AddProperty("planarfigure.selected.outline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.selected.helperline.color", mitk::ColorProperty::New(1.0, 0.0, 0.0)); node->AddProperty("planarfigure.selected.helperline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.selected.markerline.color", mitk::ColorProperty::New(1.0, 0.0, 0.0)); node->AddProperty("planarfigure.selected.markerline.opacity", mitk::FloatProperty::New(1.0)); node->AddProperty("planarfigure.selected.marker.color", mitk::ColorProperty::New(1.0, 0.0, 0.0)); node->AddProperty("planarfigure.selected.marker.opacity", mitk::FloatProperty::New(1.0)); } void mitk::PlanarFigureMapper2D::RenderControlPoints(const mitk::PlanarFigure *planarFigure, const PlanarFigureDisplayMode lineDisplayMode, const mitk::PlaneGeometry *planarFigurePlaneGeometry, const mitk::PlaneGeometry *rendererPlaneGeometry, mitk::BaseRenderer *renderer) { bool isEditable = true; m_DataNode->GetBoolProperty("planarfigure.iseditable", isEditable); PlanarFigureDisplayMode pointDisplayMode = PF_DEFAULT; const unsigned int selectedControlPointsIdx = (unsigned int)planarFigure->GetSelectedControlPoint(); const unsigned int numberOfControlPoints = planarFigure->GetNumberOfControlPoints(); // Draw markers at control points (selected control point will be colored) for (unsigned int i = 0; i < numberOfControlPoints; ++i) { // Only if planar figure is marked as editable: display markers (control points) in a // different style if mouse is over them or they are selected if (isEditable) { if (i == selectedControlPointsIdx) { pointDisplayMode = PF_SELECTED; } else if (m_IsHovering && isEditable) { pointDisplayMode = PF_HOVER; } } if (m_MarkerOpacity[pointDisplayMode] == 0 && m_MarkerlineOpacity[pointDisplayMode] == 0) { continue; } if (m_DrawOutline) { // draw outlines for markers as well // linewidth for the contour is only half, as full width looks // much too thick! this->DrawMarker(planarFigure->GetControlPoint(i), m_OutlineColor[lineDisplayMode], m_MarkerlineOpacity[pointDisplayMode], m_OutlineColor[lineDisplayMode], m_MarkerOpacity[pointDisplayMode], m_OutlineWidth / 2, m_ControlPointShape, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } this->DrawMarker(planarFigure->GetControlPoint(i), m_MarkerlineColor[pointDisplayMode], m_MarkerlineOpacity[pointDisplayMode], m_MarkerColor[pointDisplayMode], m_MarkerOpacity[pointDisplayMode], m_LineWidth, m_ControlPointShape, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } if (planarFigure->IsPreviewControlPointVisible()) { this->DrawMarker(planarFigure->GetPreviewControlPoint(), m_MarkerlineColor[PF_HOVER], m_MarkerlineOpacity[PF_HOVER], m_MarkerColor[PF_HOVER], m_MarkerOpacity[PF_HOVER], m_LineWidth, m_ControlPointShape, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } } void mitk::PlanarFigureMapper2D::RenderAnnotations(mitk::BaseRenderer *, const std::string name, const mitk::Point2D anchorPoint, float globalOpacity, const PlanarFigureDisplayMode lineDisplayMode, double &annotationOffset) { if (anchorPoint[0] < mitk::eps || anchorPoint[1] < mitk::eps) { return; } vtkTextProperty* textProp = vtkTextProperty::New(); textProp->SetFontSize(m_AnnotationSize); textProp->SetFontFamilyAsString(m_AnnotationFontFamily.c_str()); textProp->SetJustificationToLeft(); textProp->SetOpacity(globalOpacity); textProp->SetShadow(0); textProp->SetBold(m_DrawAnnotationBold); textProp->SetItalic(m_DrawAnnotationItalic); mitk::Point2D offset; offset.Fill(5); mitk::Point2D scaledAnchorPoint; scaledAnchorPoint[0] = anchorPoint[0] * m_DevicePixelRatio; scaledAnchorPoint[1] = anchorPoint[1] * m_DevicePixelRatio; offset[0] = offset[0] * m_DevicePixelRatio; offset[1] = offset[1] * m_DevicePixelRatio; if(m_DrawShadow) { textProp->SetColor(0.0,0.0,0.0); this->m_Context->ApplyTextProp(textProp); this->m_Context->DrawString(scaledAnchorPoint[0]+offset[0]+1, scaledAnchorPoint[1]+offset[1]-1, name.c_str()); } textProp->SetColor(m_AnnotationColor[lineDisplayMode][0], m_AnnotationColor[lineDisplayMode][1], m_AnnotationColor[lineDisplayMode][2]); this->m_Context->ApplyTextProp(textProp); this->m_Context->DrawString(scaledAnchorPoint[0]+offset[0], scaledAnchorPoint[1]+offset[1], name.c_str()); annotationOffset -= 15.0; // annotationOffset -= m_AnnotationAnnotation->GetBoundsOnDisplay( renderer ).Size[1]; textProp->Delete(); } void mitk::PlanarFigureMapper2D::RenderQuantities(const mitk::PlanarFigure *planarFigure, mitk::BaseRenderer *, const mitk::Point2D anchorPoint, double &annotationOffset, float globalOpacity, const PlanarFigureDisplayMode lineDisplayMode) { if (anchorPoint[0] < mitk::eps || anchorPoint[1] < mitk::eps) { return; } std::stringstream quantityString; quantityString.setf(ios::fixed, ios::floatfield); quantityString.precision(1); bool firstActiveFeature = true; for (unsigned int i = 0; i < planarFigure->GetNumberOfFeatures(); ++i) { if (planarFigure->IsFeatureActive(i) && planarFigure->IsFeatureVisible(i)) { if (!firstActiveFeature) { quantityString << " x "; } quantityString << planarFigure->GetQuantity(i) << " "; quantityString << planarFigure->GetFeatureUnit(i); firstActiveFeature = false; } } vtkTextProperty* textProp = vtkTextProperty::New(); textProp->SetFontSize(m_AnnotationSize); textProp->SetFontFamilyAsString(m_AnnotationFontFamily.c_str()); textProp->SetJustificationToLeft(); textProp->SetOpacity(globalOpacity); textProp->SetShadow(0); textProp->SetBold(m_DrawAnnotationBold); textProp->SetItalic(m_DrawAnnotationItalic); mitk::Point2D offset; offset.Fill(5); mitk::Point2D scaledAnchorPoint; scaledAnchorPoint[0] = anchorPoint[0] * m_DevicePixelRatio; scaledAnchorPoint[1] = anchorPoint[1] * m_DevicePixelRatio; offset[0] = offset[0] * m_DevicePixelRatio; offset[1] = offset[1] * m_DevicePixelRatio; if(m_DrawShadow) { textProp->SetColor(0,0,0); this->m_Context->ApplyTextProp(textProp); this->m_Context->DrawString(scaledAnchorPoint[0]+offset[0]+1, scaledAnchorPoint[1]+offset[1]-1, quantityString.str().c_str()); } textProp->SetColor(m_AnnotationColor[lineDisplayMode][0], m_AnnotationColor[lineDisplayMode][1], m_AnnotationColor[lineDisplayMode][2]); this->m_Context->ApplyTextProp(textProp); this->m_Context->DrawString(scaledAnchorPoint[0]+offset[0], scaledAnchorPoint[1]+offset[1], quantityString.str().c_str()); annotationOffset -= 15.0; // annotationOffset -= m_AnnotationAnnotation->GetBoundsOnDisplay( renderer ).Size[1]; textProp->Delete(); } void mitk::PlanarFigureMapper2D::RenderLines(const PlanarFigureDisplayMode lineDisplayMode, mitk::PlanarFigure *planarFigure, mitk::Point2D &anchorPoint, const mitk::PlaneGeometry *planarFigurePlaneGeometry, const mitk::PlaneGeometry *rendererPlaneGeometry, const mitk::BaseRenderer *renderer) { // If we want to draw an outline, we do it here if (m_DrawOutline) { const float *color = m_OutlineColor[lineDisplayMode]; const float opacity = m_OutlineOpacity[lineDisplayMode]; // convert to a float array that also contains opacity, faster GL float *colorVector = new float[4]; colorVector[0] = color[0]; colorVector[1] = color[1]; colorVector[2] = color[2]; colorVector[3] = opacity; // set the color and opacity here as it is common for all outlines this->m_Context->GetPen()->SetColorF((double)color[0], (double)color[1], (double)color[2], opacity); this->m_Context->GetPen()->SetWidth(m_OutlineWidth); if (m_DrawDashed) this->m_Context->GetPen()->SetLineType(vtkPen::DASH_LINE); else this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Draw the outline for all polylines if requested this->DrawMainLines(planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); this->m_Context->GetPen()->SetWidth(m_HelperlineWidth); if (m_DrawHelperDashed) this->m_Context->GetPen()->SetLineType(vtkPen::DASH_LINE); else this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Draw the outline for all helper objects if requested this->DrawHelperLines(planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); // cleanup delete[] colorVector; } // If we want to draw a shadow, we do it here if (m_DrawShadow) { // determine the shadow opacity const float opacity = m_OutlineOpacity[lineDisplayMode]; float shadowOpacity = 0.0f; if (opacity > 0.2f) shadowOpacity = opacity - 0.2f; // convert to a float array that also contains opacity, faster GL float *shadow = new float[4]; shadow[0] = 0; shadow[1] = 0; shadow[2] = 0; shadow[3] = shadowOpacity; // set the color and opacity here as it is common for all shadows this->m_Context->GetPen()->SetColorF(0, 0, 0, shadowOpacity); this->m_Context->GetPen()->SetWidth(m_OutlineWidth * m_ShadowWidthFactor); if (m_DrawDashed) this->m_Context->GetPen()->SetLineType(vtkPen::DASH_LINE); else this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Draw the outline for all polylines if requested this->DrawMainLines(planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); this->m_Context->GetPen()->SetWidth(m_HelperlineWidth); if (m_DrawHelperDashed) this->m_Context->GetPen()->SetLineType(vtkPen::DASH_LINE); else this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Draw the outline for all helper objects if requested this->DrawHelperLines(planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); // cleanup delete[] shadow; } // set this in brackets to avoid duplicate variables in the same scope { const float *color = m_LineColor[lineDisplayMode]; const float opacity = m_LineOpacity[lineDisplayMode]; // set the color and opacity here as it is common for all mainlines this->m_Context->GetPen()->SetColorF((double)color[0], (double)color[1], (double)color[2], (double)opacity); this->m_Context->GetPen()->SetWidth(m_LineWidth); if (m_DrawDashed) this->m_Context->GetPen()->SetLineType(vtkPen::DASH_LINE); else this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Draw the main line for all polylines this->DrawMainLines(planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); const float *helperColor = m_HelperlineColor[lineDisplayMode]; const float helperOpacity = m_HelperlineOpacity[lineDisplayMode]; // we only set the color for the helperlines as the linewidth is unchanged this->m_Context->GetPen()->SetColorF((double)helperColor[0], (double)helperColor[1], (double)helperColor[2], (double)helperOpacity); this->m_Context->GetPen()->SetWidth(m_HelperlineWidth); if (m_DrawHelperDashed) this->m_Context->GetPen()->SetLineType(vtkPen::DASH_LINE); else this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); // Draw helper objects this->DrawHelperLines(planarFigure, anchorPoint, planarFigurePlaneGeometry, rendererPlaneGeometry, renderer); } if (m_DrawDashed || m_DrawHelperDashed) this->m_Context->GetPen()->SetLineType(vtkPen::SOLID_LINE); } diff --git a/Modules/Segmentation/Algorithms/mitkShowSegmentationAsSurface.cpp b/Modules/Segmentation/Algorithms/mitkShowSegmentationAsSurface.cpp index 36f595df2c..eb96d327c8 100644 --- a/Modules/Segmentation/Algorithms/mitkShowSegmentationAsSurface.cpp +++ b/Modules/Segmentation/Algorithms/mitkShowSegmentationAsSurface.cpp @@ -1,226 +1,226 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkShowSegmentationAsSurface.h" #include "mitkManualSegmentationToSurfaceFilter.h" #include "mitkVtkRepresentationProperty.h" #include #include namespace mitk { ShowSegmentationAsSurface::ShowSegmentationAsSurface() : m_UIDGeneratorSurfaces("Surface_") {} ShowSegmentationAsSurface::~ShowSegmentationAsSurface() {} void ShowSegmentationAsSurface::Initialize(const NonBlockingAlgorithm *other) { Superclass::Initialize(other); bool syncVisibility(false); if (other) { other->GetParameter("Sync visibility", syncVisibility); } SetParameter("Sync visibility", syncVisibility); SetParameter("Median kernel size", 3u); SetParameter("Apply median", true); SetParameter("Smooth", true); SetParameter("Gaussian SD", 1.5f); SetParameter("Decimate mesh", true); SetParameter("Decimation rate", 0.8f); SetParameter("Wireframe", false); } bool ShowSegmentationAsSurface::ReadyToRun() { try { Image::Pointer image; GetPointerParameter("Input", image); return image.IsNotNull() && GetGroupNode(); } catch (std::invalid_argument &) { return false; } } bool ShowSegmentationAsSurface::ThreadedUpdateFunction() { Image::Pointer image; GetPointerParameter("Input", image); bool smooth(true); GetParameter("Smooth", smooth); bool applyMedian(true); GetParameter("Apply median", applyMedian); bool decimateMesh(true); GetParameter("Decimate mesh", decimateMesh); unsigned int medianKernelSize(3); GetParameter("Median kernel size", medianKernelSize); float gaussianSD(1.5); GetParameter("Gaussian SD", gaussianSD); float reductionRate(0.8); GetParameter("Decimation rate", reductionRate); MITK_INFO << "Creating polygon model with smoothing " << smooth << " gaussianSD " << gaussianSD << " median " << applyMedian << " median kernel " << medianKernelSize << " mesh reduction " << decimateMesh << " reductionRate " << reductionRate; ManualSegmentationToSurfaceFilter::Pointer surfaceFilter = ManualSegmentationToSurfaceFilter::New(); surfaceFilter->SetInput(image); surfaceFilter->SetThreshold(0.5); // expects binary image with zeros and ones surfaceFilter->SetUseGaussianImageSmooth(smooth); // apply gaussian to thresholded image ? surfaceFilter->SetSmooth(smooth); if (smooth) { surfaceFilter->InterpolationOn(); surfaceFilter->SetGaussianStandardDeviation(gaussianSD); } surfaceFilter->SetMedianFilter3D(applyMedian); // apply median to segmentation before marching cubes ? if (applyMedian) { surfaceFilter->SetMedianKernelSize( medianKernelSize, medianKernelSize, medianKernelSize); // apply median to segmentation before marching cubes } // fix to avoid vtk warnings see bug #5390 if (image->GetDimension() > 3) decimateMesh = false; if (decimateMesh) { surfaceFilter->SetDecimate(ImageToSurfaceFilter::QuadricDecimation); surfaceFilter->SetTargetReduction(reductionRate); } else { surfaceFilter->SetDecimate(ImageToSurfaceFilter::NoDecimation); } surfaceFilter->UpdateLargestPossibleRegion(); // calculate normals for nicer display m_Surface = surfaceFilter->GetOutput(); vtkPolyData *polyData = m_Surface->GetVtkPolyData(); if (!polyData) throw std::logic_error("Could not create polygon model"); - polyData->SetVerts(0); - polyData->SetLines(0); + polyData->SetVerts(nullptr); + polyData->SetLines(nullptr); if (smooth || applyMedian || decimateMesh) { vtkPolyDataNormals *normalsGen = vtkPolyDataNormals::New(); normalsGen->AutoOrientNormalsOn(); normalsGen->FlipNormalsOff(); normalsGen->SetInputData(polyData); normalsGen->Update(); m_Surface->SetVtkPolyData(normalsGen->GetOutput()); normalsGen->Delete(); } else { m_Surface->SetVtkPolyData(polyData); } return true; } void ShowSegmentationAsSurface::ThreadedUpdateSuccessful() { m_Node = DataNode::New(); bool wireframe(false); GetParameter("Wireframe", wireframe); if (wireframe) { VtkRepresentationProperty *np = dynamic_cast(m_Node->GetProperty("material.representation")); if (np) np->SetRepresentationToWireframe(); } m_Node->SetProperty("opacity", FloatProperty::New(0.3)); m_Node->SetProperty("line width", IntProperty::New(1)); m_Node->SetProperty("scalar visibility", BoolProperty::New(false)); std::string groupNodesName("surface"); DataNode *groupNode = GetGroupNode(); if (groupNode) { groupNode->GetName(groupNodesName); // if parameter smooth is set add extension to node name bool smooth(true); GetParameter("Smooth", smooth); if (smooth) groupNodesName.append("_smoothed"); } m_Node->SetProperty("name", StringProperty::New(groupNodesName)); // synchronize this object's color with the parent's color // surfaceNode->SetProperty( "color", parentNode->GetProperty( "color" ) ); // surfaceNode->SetProperty( "visible", parentNode->GetProperty( "visible" ) ); m_Node->SetData(m_Surface); BaseProperty *colorProp = groupNode->GetProperty("color"); if (colorProp) m_Node->ReplaceProperty("color", colorProp->Clone()); else m_Node->SetProperty("color", ColorProperty::New(1.0, 1.0, 0.0)); bool showResult(true); GetParameter("Show result", showResult); bool syncVisibility(false); GetParameter("Sync visibility", syncVisibility); Image::Pointer image; GetPointerParameter("Input", image); BaseProperty *organTypeProp = image->GetProperty("organ type"); if (organTypeProp) m_Surface->SetProperty("organ type", organTypeProp); BaseProperty *visibleProp = groupNode->GetProperty("visible"); if (visibleProp && syncVisibility) m_Node->ReplaceProperty("visible", visibleProp->Clone()); else m_Node->SetProperty("visible", BoolProperty::New(showResult)); InsertBelowGroupNode(m_Node); Superclass::ThreadedUpdateSuccessful(); } } // namespace diff --git a/Modules/Segmentation/Controllers/mitkToolManagerProvider.cpp b/Modules/Segmentation/Controllers/mitkToolManagerProvider.cpp index 7a884cfc74..b218baf894 100644 --- a/Modules/Segmentation/Controllers/mitkToolManagerProvider.cpp +++ b/Modules/Segmentation/Controllers/mitkToolManagerProvider.cpp @@ -1,61 +1,61 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkToolManagerProvider.h" // micro service includes #include #include mitk::ToolManagerProvider::ToolManagerProvider() : m_ToolManager(mitk::ToolManager::New(nullptr)) { } mitk::ToolManagerProvider::~ToolManagerProvider() { this->m_ToolManager = nullptr; } mitk::ToolManager *mitk::ToolManagerProvider::GetToolManager() { return this->m_ToolManager.GetPointer(); } mitk::ToolManagerProvider *mitk::ToolManagerProvider::GetInstance() { static us::ServiceReference serviceRef; static us::ModuleContext *context = us::GetModuleContext(); if (!serviceRef) { // This is either the first time GetInstance() was called, // or a SingletonOneService instance has not yet been registered. serviceRef = context->GetServiceReference(); } if (serviceRef) { // We have a valid service reference. It always points to the service // with the lowest id (usually the one which was registered first). // This still might return a null pointer, if all SingletonOneService // instances have been unregistered (during unloading of the library, // for example). return context->GetService(serviceRef); } else { // No SingletonOneService instance was registered yet. - return 0; + return nullptr; } } diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp b/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp index 7a7abb080c..94d7c87fe7 100644 --- a/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp @@ -1,484 +1,484 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkFastMarchingTool.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkInteractionConst.h" #include "mitkRenderingManager.h" #include "itkOrImageFilter.h" #include "mitkImageTimeSelector.h" // us #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, FastMarchingTool, "FastMarching2D tool"); } mitk::FastMarchingTool::FastMarchingTool() : FeedbackContourTool("PressMoveReleaseAndPointSetting"), m_NeedUpdate(true), m_CurrentTimeStep(0), - m_PositionEvent(0), + m_PositionEvent(nullptr), m_LowerThreshold(0), m_UpperThreshold(200), m_StoppingValue(100), m_Sigma(1.0), m_Alpha(-0.5), m_Beta(3.0) { } mitk::FastMarchingTool::~FastMarchingTool() { if (this->m_SmoothFilter.IsNotNull()) this->m_SmoothFilter->RemoveAllObservers(); if (this->m_SigmoidFilter.IsNotNull()) this->m_SigmoidFilter->RemoveAllObservers(); if (this->m_GradientMagnitudeFilter.IsNotNull()) this->m_GradientMagnitudeFilter->RemoveAllObservers(); if (this->m_FastMarchingFilter.IsNotNull()) this->m_FastMarchingFilter->RemoveAllObservers(); } void mitk::FastMarchingTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddPoint); CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPoint); CONNECT_FUNCTION("DeletePoint", OnDelete); } const char **mitk::FastMarchingTool::GetXPM() const { return nullptr; // mitkFastMarchingTool_xpm; } us::ModuleResource mitk::FastMarchingTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("FastMarching_48x48.png"); return resource; } us::ModuleResource mitk::FastMarchingTool::GetCursorIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("FastMarching_Cursor_32x32.png"); return resource; } const char *mitk::FastMarchingTool::GetName() const { return "2D Fast Marching"; } void mitk::FastMarchingTool::BuildITKPipeline() { m_ReferenceImageSliceAsITK = InternalImageType::New(); m_ReferenceImageSlice = GetAffectedReferenceSlice(m_PositionEvent); CastToItkImage(m_ReferenceImageSlice, m_ReferenceImageSliceAsITK); m_ProgressCommand = mitk::ToolCommand::New(); m_SmoothFilter = SmoothingFilterType::New(); m_SmoothFilter->SetInput(m_ReferenceImageSliceAsITK); m_SmoothFilter->SetTimeStep(0.05); m_SmoothFilter->SetNumberOfIterations(2); m_SmoothFilter->SetConductanceParameter(9.0); m_GradientMagnitudeFilter = GradientFilterType::New(); m_GradientMagnitudeFilter->SetSigma(m_Sigma); m_SigmoidFilter = SigmoidFilterType::New(); m_SigmoidFilter->SetAlpha(m_Alpha); m_SigmoidFilter->SetBeta(m_Beta); m_SigmoidFilter->SetOutputMinimum(0.0); m_SigmoidFilter->SetOutputMaximum(1.0); m_FastMarchingFilter = FastMarchingFilterType::New(); m_FastMarchingFilter->SetStoppingValue(m_StoppingValue); m_ThresholdFilter = ThresholdingFilterType::New(); m_ThresholdFilter->SetLowerThreshold(m_LowerThreshold); m_ThresholdFilter->SetUpperThreshold(m_UpperThreshold); m_ThresholdFilter->SetOutsideValue(0); m_ThresholdFilter->SetInsideValue(1.0); m_SeedContainer = NodeContainer::New(); m_SeedContainer->Initialize(); m_FastMarchingFilter->SetTrialPoints(m_SeedContainer); if (this->m_SmoothFilter.IsNotNull()) this->m_SmoothFilter->RemoveAllObservers(); if (this->m_SigmoidFilter.IsNotNull()) this->m_SigmoidFilter->RemoveAllObservers(); if (this->m_GradientMagnitudeFilter.IsNotNull()) this->m_GradientMagnitudeFilter->RemoveAllObservers(); if (this->m_FastMarchingFilter.IsNotNull()) this->m_FastMarchingFilter->RemoveAllObservers(); m_SmoothFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_GradientMagnitudeFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_SigmoidFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_FastMarchingFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_SmoothFilter->SetInput(m_ReferenceImageSliceAsITK); m_GradientMagnitudeFilter->SetInput(m_SmoothFilter->GetOutput()); m_SigmoidFilter->SetInput(m_GradientMagnitudeFilter->GetOutput()); m_FastMarchingFilter->SetInput(m_SigmoidFilter->GetOutput()); m_ThresholdFilter->SetInput(m_FastMarchingFilter->GetOutput()); m_ReferenceImageSliceAsITK = InternalImageType::New(); } void mitk::FastMarchingTool::SetUpperThreshold(double value) { if (m_UpperThreshold != value) { m_UpperThreshold = value / 10.0; m_ThresholdFilter->SetUpperThreshold(m_UpperThreshold); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetLowerThreshold(double value) { if (m_LowerThreshold != value) { m_LowerThreshold = value / 10.0; m_ThresholdFilter->SetLowerThreshold(m_LowerThreshold); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetBeta(double value) { if (m_Beta != value) { m_Beta = value; m_SigmoidFilter->SetBeta(m_Beta); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetSigma(double value) { if (m_Sigma != value) { m_Sigma = value; m_GradientMagnitudeFilter->SetSigma(m_Sigma); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetAlpha(double value) { if (m_Alpha != value) { m_Alpha = value; m_SigmoidFilter->SetAlpha(m_Alpha); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetStoppingValue(double value) { if (m_StoppingValue != value) { m_StoppingValue = value; m_FastMarchingFilter->SetStoppingValue(m_StoppingValue); m_NeedUpdate = true; } } void mitk::FastMarchingTool::Activated() { Superclass::Activated(); m_ResultImageNode = mitk::DataNode::New(); m_ResultImageNode->SetName("FastMarching_Preview"); m_ResultImageNode->SetBoolProperty("helper object", true); m_ResultImageNode->SetColor(0.0, 1.0, 0.0); m_ResultImageNode->SetVisibility(true); m_ToolManager->GetDataStorage()->Add(this->m_ResultImageNode, m_ToolManager->GetReferenceData(0)); m_SeedsAsPointSet = mitk::PointSet::New(); m_SeedsAsPointSetNode = mitk::DataNode::New(); m_SeedsAsPointSetNode->SetData(m_SeedsAsPointSet); m_SeedsAsPointSetNode->SetName("Seeds_Preview"); m_SeedsAsPointSetNode->SetBoolProperty("helper object", true); m_SeedsAsPointSetNode->SetColor(0.0, 1.0, 0.0); m_SeedsAsPointSetNode->SetVisibility(true); m_ToolManager->GetDataStorage()->Add(this->m_SeedsAsPointSetNode, m_ToolManager->GetReferenceData(0)); this->Initialize(); } void mitk::FastMarchingTool::Deactivated() { m_ToolManager->GetDataStorage()->Remove(this->m_ResultImageNode); m_ToolManager->GetDataStorage()->Remove(this->m_SeedsAsPointSetNode); this->ClearSeeds(); m_ResultImageNode = nullptr; m_SeedsAsPointSetNode = nullptr; mitk::RenderingManager::GetInstance()->RequestUpdateAll(); Superclass::Deactivated(); } void mitk::FastMarchingTool::Initialize() { m_ReferenceImage = dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData()); if (m_ReferenceImage->GetTimeGeometry()->CountTimeSteps() > 1) { mitk::ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(m_ReferenceImage); timeSelector->SetTimeNr(m_CurrentTimeStep); timeSelector->UpdateLargestPossibleRegion(); m_ReferenceImage = timeSelector->GetOutput(); } m_NeedUpdate = true; } void mitk::FastMarchingTool::ConfirmSegmentation() { // combine preview image with current working segmentation if (dynamic_cast(m_ResultImageNode->GetData())) { // logical or combination of preview and segmentation slice mitk::Image::Pointer workingImageSlice; mitk::Image::Pointer workingImage = dynamic_cast(this->m_ToolManager->GetWorkingData(0)->GetData()); workingImageSlice = GetAffectedImageSliceAs2DImage(m_WorkingPlane, workingImage, m_CurrentTimeStep); mitk::Image::Pointer segmentationResult = mitk::Image::New(); bool isDeprecatedUnsignedCharSegmentation = (workingImage->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR); if (isDeprecatedUnsignedCharSegmentation) { typedef itk::Image OutputUCharImageType; OutputUCharImageType::Pointer workingImageSliceInITK = OutputUCharImageType::New(); CastToItkImage(workingImageSlice, workingImageSliceInITK); typedef itk::OrImageFilter OrImageFilterType; OrImageFilterType::Pointer orFilter = OrImageFilterType::New(); orFilter->SetInput1(m_ThresholdFilter->GetOutput()); orFilter->SetInput2(workingImageSliceInITK); orFilter->Update(); mitk::CastToMitkImage(orFilter->GetOutput(), segmentationResult); } else { OutputImageType::Pointer workingImageSliceInITK = OutputImageType::New(); CastToItkImage(workingImageSlice, workingImageSliceInITK); typedef itk::OrImageFilter OrImageFilterType; OrImageFilterType::Pointer orFilter = OrImageFilterType::New(); orFilter->SetInput(0, m_ThresholdFilter->GetOutput()); orFilter->SetInput(1, workingImageSliceInITK); orFilter->Update(); mitk::CastToMitkImage(orFilter->GetOutput(), segmentationResult); } segmentationResult->GetGeometry()->SetOrigin(workingImageSlice->GetGeometry()->GetOrigin()); segmentationResult->GetGeometry()->SetIndexToWorldTransform( workingImageSlice->GetGeometry()->GetIndexToWorldTransform()); // write to segmentation volume and hide preview image // again, current time step is not considered this->WriteBackSegmentationResult(m_WorkingPlane, segmentationResult, m_CurrentTimeStep); this->m_ResultImageNode->SetVisibility(false); this->ClearSeeds(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_ToolManager->ActivateTool(-1); } void mitk::FastMarchingTool::OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent) { // Add a new seed point for FastMarching algorithm mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); // const PositionEvent* p = dynamic_cast(stateEvent->GetEvent()); if (positionEvent == nullptr) return; if (m_PositionEvent.IsNotNull()) m_PositionEvent = nullptr; m_PositionEvent = InteractionPositionEvent::New(positionEvent->GetSender(), positionEvent->GetPointerPositionOnScreen()); // if click was on another renderwindow or slice then reset pipeline and preview if ((m_LastEventSender != m_PositionEvent->GetSender()) || (m_LastEventSlice != m_PositionEvent->GetSender()->GetSlice())) { this->BuildITKPipeline(); this->ClearSeeds(); } m_LastEventSender = m_PositionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); m_WorkingPlane = positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone(); mitk::Point3D clickInIndex; m_ReferenceImageSlice->GetGeometry()->WorldToIndex(m_PositionEvent->GetPositionInWorld(), clickInIndex); itk::Index<2> seedPosition; seedPosition[0] = clickInIndex[0]; seedPosition[1] = clickInIndex[1]; NodeType node; const double seedValue = 0.0; node.SetValue(seedValue); node.SetIndex(seedPosition); this->m_SeedContainer->InsertElement(this->m_SeedContainer->Size(), node); m_FastMarchingFilter->Modified(); m_SeedsAsPointSet->InsertPoint(m_SeedsAsPointSet->GetSize(), m_PositionEvent->GetPositionInWorld()); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_NeedUpdate = true; this->Update(); m_ReadyMessage.Send(); } void mitk::FastMarchingTool::OnDelete(StateMachineAction *, InteractionEvent *) { // delete last seed point if (!(this->m_SeedContainer->empty())) { // delete last element of seeds container this->m_SeedContainer->pop_back(); m_FastMarchingFilter->Modified(); // delete last point in pointset - somehow ugly m_SeedsAsPointSet->GetPointSet()->GetPoints()->DeleteIndex(m_SeedsAsPointSet->GetSize() - 1); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_NeedUpdate = true; this->Update(); } } void mitk::FastMarchingTool::Update() { const unsigned int progress_steps = 20; // update FastMarching pipeline and show result if (m_NeedUpdate) { m_ProgressCommand->AddStepsToDo(progress_steps); CurrentlyBusy.Send(true); try { m_ThresholdFilter->Update(); } catch (itk::ExceptionObject &excep) { MITK_ERROR << "Exception caught: " << excep.GetDescription(); // progress by max step count, will force m_ProgressCommand->SetProgress(progress_steps); CurrentlyBusy.Send(false); std::string msg = excep.GetDescription(); ErrorMessage.Send(msg); return; } m_ProgressCommand->SetProgress(progress_steps); CurrentlyBusy.Send(false); // make output visible mitk::Image::Pointer result = mitk::Image::New(); CastToMitkImage(m_ThresholdFilter->GetOutput(), result); result->GetGeometry()->SetOrigin(m_ReferenceImageSlice->GetGeometry()->GetOrigin()); result->GetGeometry()->SetIndexToWorldTransform(m_ReferenceImageSlice->GetGeometry()->GetIndexToWorldTransform()); m_ResultImageNode->SetData(result); m_ResultImageNode->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void mitk::FastMarchingTool::ClearSeeds() { // clear seeds for FastMarching as well as the PointSet for visualization if (this->m_SeedContainer.IsNotNull()) this->m_SeedContainer->Initialize(); if (this->m_SeedsAsPointSet.IsNotNull()) { this->m_SeedsAsPointSet = mitk::PointSet::New(); this->m_SeedsAsPointSetNode->SetData(this->m_SeedsAsPointSet); m_SeedsAsPointSetNode->SetName("Seeds_Preview"); m_SeedsAsPointSetNode->SetBoolProperty("helper object", true); m_SeedsAsPointSetNode->SetColor(0.0, 1.0, 0.0); m_SeedsAsPointSetNode->SetVisibility(true); } if (this->m_FastMarchingFilter.IsNotNull()) m_FastMarchingFilter->Modified(); this->m_NeedUpdate = true; } void mitk::FastMarchingTool::Reset() { // clear all seeds and preview empty result this->ClearSeeds(); m_ResultImageNode->SetVisibility(false); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::FastMarchingTool::SetCurrentTimeStep(int t) { if (m_CurrentTimeStep != t) { m_CurrentTimeStep = t; this->Initialize(); } } diff --git a/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp b/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp index 7fc7b390a9..8b13edc323 100644 --- a/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp +++ b/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp @@ -1,180 +1,180 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSetRegionTool.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkImageDataItem.h" #include "mitkLabelSetImage.h" #include #include #include #include #include mitk::SetRegionTool::SetRegionTool(int paintingPixelValue) : FeedbackContourTool("PressMoveRelease"), m_PaintingPixelValue(paintingPixelValue) { } mitk::SetRegionTool::~SetRegionTool() { } void mitk::SetRegionTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION("Release", OnMouseReleased); CONNECT_FUNCTION("Move", OnMouseMoved); } void mitk::SetRegionTool::Activated() { Superclass::Activated(); } void mitk::SetRegionTool::Deactivated() { Superclass::Deactivated(); } void mitk::SetRegionTool::OnMousePressed(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); // 1. Get the working image Image::Pointer workingSlice = FeedbackContourTool::GetAffectedWorkingSlice(positionEvent); if (workingSlice.IsNull()) return; // can't do anything without the segmentation // if click was outside the image, don't continue const BaseGeometry *sliceGeometry = workingSlice->GetGeometry(); itk::Index<3> projectedPointIn2D; sliceGeometry->WorldToIndex(positionEvent->GetPositionInWorld(), projectedPointIn2D); if (!sliceGeometry->IsIndexInside(projectedPointIn2D)) { MITK_ERROR << "point apparently not inside segmentation slice" << std::endl; return; // can't use that as a seed point } typedef itk::Image InputImageType; typedef InputImageType::IndexType IndexType; typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); // convert world coordinates to image indices IndexType seedIndex; sliceGeometry->WorldToIndex(positionEvent->GetPositionInWorld(), seedIndex); // perform region growing in desired segmented region InputImageType::Pointer itkImage = InputImageType::New(); CastToItkImage(workingSlice, itkImage); regionGrower->SetInput(itkImage); regionGrower->AddSeed(seedIndex); InputImageType::PixelType bound = itkImage->GetPixel(seedIndex); regionGrower->SetLower(bound); regionGrower->SetUpper(bound); regionGrower->SetReplaceValue(1); itk::BinaryFillholeImageFilter::Pointer fillHolesFilter = itk::BinaryFillholeImageFilter::New(); fillHolesFilter->SetInput(regionGrower->GetOutput()); fillHolesFilter->SetForegroundValue(1); // Store result and preview mitk::Image::Pointer resultImage = mitk::GrabItkImageMemory(fillHolesFilter->GetOutput()); resultImage->SetGeometry(workingSlice->GetGeometry()); // Get the current working color DataNode *workingNode(m_ToolManager->GetWorkingData(0)); if (!workingNode) return; mitk::ImageToContourModelFilter::Pointer contourextractor = mitk::ImageToContourModelFilter::New(); contourextractor->SetInput(resultImage); contourextractor->Update(); mitk::ContourModel::Pointer awesomeContour = contourextractor->GetOutput(); FeedbackContourTool::SetFeedbackContour(awesomeContour); FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::SetRegionTool::OnMouseReleased(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; assert(positionEvent->GetSender()->GetRenderWindow()); // 1. Hide the feedback contour, find out which slice the user clicked, find out which slice of the toolmanager's // working image corresponds to that FeedbackContourTool::SetFeedbackContourVisible(false); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); int timeStep = positionEvent->GetSender()->GetTimeStep(); DataNode *workingNode(m_ToolManager->GetWorkingData(0)); if (!workingNode) return; Image *image = dynamic_cast(workingNode->GetData()); const PlaneGeometry *planeGeometry((positionEvent->GetSender()->GetCurrentWorldPlaneGeometry())); if (!image || !planeGeometry) return; Image::Pointer slice = FeedbackContourTool::GetAffectedImageSliceAs2DImage(positionEvent, image); if (slice.IsNull()) { MITK_ERROR << "Unable to extract slice." << std::endl; return; } ContourModel *feedbackContour(FeedbackContourTool::GetFeedbackContour()); ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( slice, feedbackContour, false, false); // false: don't add 0.5 (done by FillContourInSlice) // false: don't constrain the contour to the image's inside if (projectedContour.IsNull()) return; LabelSetImage *labelImage = dynamic_cast(image); int activeColor = 1; - if (labelImage != 0) + if (labelImage != nullptr) { activeColor = labelImage->GetActiveLabel()->GetValue(); } mitk::ContourModelUtils::FillContourInSlice( projectedContour, timeStep, slice, image, m_PaintingPixelValue * activeColor); this->WriteBackSegmentationResult(positionEvent, slice); } void mitk::SetRegionTool::OnMouseMoved(mitk::StateMachineAction *, mitk::InteractionEvent *) { } diff --git a/Modules/SurfaceInterpolation/mitkSurfaceBasedInterpolationController.cpp b/Modules/SurfaceInterpolation/mitkSurfaceBasedInterpolationController.cpp index 0e0ab06ffb..de9f0ed9d7 100644 --- a/Modules/SurfaceInterpolation/mitkSurfaceBasedInterpolationController.cpp +++ b/Modules/SurfaceInterpolation/mitkSurfaceBasedInterpolationController.cpp @@ -1,303 +1,303 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurfaceBasedInterpolationController.h" #include "mitkColorProperty.h" #include "mitkComputeContourSetNormalsFilter.h" #include "mitkContourModelToSurfaceFilter.h" #include "mitkIOUtil.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageToSurfaceFilter.h" #include "mitkInteractionConst.h" #include "mitkMemoryUtilities.h" #include "mitkProperties.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkVtkRepresentationProperty.h" #include "vtkAppendPolyData.h" #include "vtkPolyData.h" #include "vtkProperty.h" #include "vtkSmartPointer.h" #include mitk::SurfaceBasedInterpolationController::SurfaceBasedInterpolationController() : m_MinSpacing(1.0), m_MaxSpacing(1.0), m_WorkingImage(nullptr), m_ActiveLabel(0) { this->Initialize(); } mitk::SurfaceBasedInterpolationController::~SurfaceBasedInterpolationController() { ContourListMap::iterator it = m_MapOfContourLists.begin(); for (; it != m_MapOfContourLists.end(); it++) { for (unsigned int j = 0; j < m_MapOfContourLists[(*it).first].size(); ++j) { delete (m_MapOfContourLists[(*it).first].at(j).second); } m_MapOfContourLists.erase(it); } } void mitk::SurfaceBasedInterpolationController::Initialize() { m_InterpolateSurfaceFilter = CreateDistanceImageFromSurfaceFilter::New(); m_InterpolateSurfaceFilter->SetUseProgressBar(false); m_Contours = Surface::New(); m_InterpolationResult = nullptr; } mitk::SurfaceBasedInterpolationController *mitk::SurfaceBasedInterpolationController::GetInstance() { static mitk::SurfaceBasedInterpolationController *m_Instance; - if (m_Instance == 0) + if (m_Instance == nullptr) { m_Instance = new SurfaceBasedInterpolationController(); } return m_Instance; } void mitk::SurfaceBasedInterpolationController::AddNewContour(mitk::ContourModel::Pointer newContour, RestorePlanePositionOperation *op) { if (m_ActiveLabel == 0) return; AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform = op->GetTransform(); mitk::Vector3D direction = op->GetDirectionVector(); int pos(-1); for (unsigned int i = 0; i < m_MapOfContourLists[m_ActiveLabel].size(); i++) { itk::Matrix diffM = transform->GetMatrix() - m_MapOfContourLists[m_ActiveLabel].at(i).second->GetTransform()->GetMatrix(); bool isSameMatrix(true); for (unsigned int j = 0; j < 3; j++) { if (fabs(diffM[j][0]) > 0.0001 && fabs(diffM[j][1]) > 0.0001 && fabs(diffM[j][2]) > 0.0001) { isSameMatrix = false; break; } } itk::Vector diffV = m_MapOfContourLists[m_ActiveLabel].at(i).second->GetTransform()->GetOffset() - transform->GetOffset(); if (isSameMatrix && m_MapOfContourLists[m_ActiveLabel].at(i).second->GetPos() == op->GetPos() && (fabs(diffV[0]) < 0.0001 && fabs(diffV[1]) < 0.0001 && fabs(diffV[2]) < 0.0001)) { pos = i; break; } } if (pos == -1 && newContour->GetNumberOfVertices() > 0) // add a new contour { mitk::RestorePlanePositionOperation *newOp = new mitk::RestorePlanePositionOperation( OpRESTOREPLANEPOSITION, op->GetWidth(), op->GetHeight(), op->GetSpacing(), op->GetPos(), direction, transform); ContourPositionPair newData = std::make_pair(newContour, newOp); m_MapOfContourLists[m_ActiveLabel].push_back(newData); } else if (pos != -1) // replace existing contour { m_MapOfContourLists[m_ActiveLabel].at(pos).first = newContour; } this->Modified(); } void mitk::SurfaceBasedInterpolationController::Interpolate() { if (m_MapOfContourLists[m_ActiveLabel].size() < 2) { // If no interpolation is possible reset the interpolation result m_InterpolationResult = nullptr; return; } m_InterpolateSurfaceFilter->Reset(); // MLI TODO // m_InterpolateSurfaceFilter->SetReferenceImage( m_WorkingImage ); // CreateDistanceImageFromSurfaceFilter::Pointer interpolateSurfaceFilter = // CreateDistanceImageFromSurfaceFilter::New(); vtkSmartPointer polyDataAppender = vtkSmartPointer::New(); for (unsigned int i = 0; i < m_MapOfContourLists[m_ActiveLabel].size(); i++) { mitk::ContourModelToSurfaceFilter::Pointer converter = mitk::ContourModelToSurfaceFilter::New(); converter->SetInput(m_MapOfContourLists[m_ActiveLabel].at(i).first); converter->Update(); mitk::Surface::Pointer surface = converter->GetOutput(); surface->DisconnectPipeline(); ReduceContourSetFilter::Pointer reduceFilter = ReduceContourSetFilter::New(); reduceFilter->SetUseProgressBar(false); reduceFilter->SetInput(surface); reduceFilter->SetMinSpacing(m_MinSpacing); reduceFilter->SetMaxSpacing(m_MaxSpacing); reduceFilter->Update(); ComputeContourSetNormalsFilter::Pointer normalsFilter = ComputeContourSetNormalsFilter::New(); normalsFilter->SetUseProgressBar(false); normalsFilter->SetInput(reduceFilter->GetOutput()); normalsFilter->SetMaxSpacing(m_MaxSpacing); // MLI TODO // normalsFilter->SetSegmentationBinaryImage(m_WorkingImage, m_ActiveLabel); // normalsFilter->Update(); m_InterpolateSurfaceFilter->SetInput(i, normalsFilter->GetOutput()); polyDataAppender->AddInputData(reduceFilter->GetOutput()->GetVtkPolyData()); } polyDataAppender->Update(); m_Contours->SetVtkPolyData(polyDataAppender->GetOutput()); // update the filter and get the resulting distance-image m_InterpolateSurfaceFilter->Update(); Image::Pointer distanceImage = m_InterpolateSurfaceFilter->GetOutput(); if (distanceImage.IsNull()) { // If no interpolation is possible reset the interpolation result m_InterpolationResult = nullptr; return; } // create a surface from the distance-image mitk::ImageToSurfaceFilter::Pointer imageToSurfaceFilter = mitk::ImageToSurfaceFilter::New(); imageToSurfaceFilter->SetInput(distanceImage); imageToSurfaceFilter->SetThreshold(0); imageToSurfaceFilter->SetSmooth(true); imageToSurfaceFilter->SetSmoothIteration(20); imageToSurfaceFilter->Update(); m_InterpolationResult = imageToSurfaceFilter->GetOutput(); m_InterpolationResult->DisconnectPipeline(); } mitk::Surface::Pointer mitk::SurfaceBasedInterpolationController::GetInterpolationResult() { return m_InterpolationResult; } mitk::Surface *mitk::SurfaceBasedInterpolationController::GetContoursAsSurface() { return m_Contours; } void mitk::SurfaceBasedInterpolationController::SetMinSpacing(double minSpacing) { m_MinSpacing = minSpacing; } void mitk::SurfaceBasedInterpolationController::SetMaxSpacing(double maxSpacing) { m_MaxSpacing = maxSpacing; } void mitk::SurfaceBasedInterpolationController::SetDistanceImageVolume(unsigned int value) { m_DistanceImageVolume = value; } void mitk::SurfaceBasedInterpolationController::SetWorkingImage(Image *workingImage) { m_WorkingImage = workingImage; } mitk::Image *mitk::SurfaceBasedInterpolationController::GetImage() { return m_InterpolateSurfaceFilter->GetOutput(); } double mitk::SurfaceBasedInterpolationController::EstimatePortionOfNeededMemory() { double numberOfPoints = 0.0; for (unsigned int i = 0; i < m_MapOfContourLists[m_ActiveLabel].size(); i++) { numberOfPoints += m_MapOfContourLists[m_ActiveLabel].at(i).first->GetNumberOfVertices() * 3; } double sizeOfPoints = pow(numberOfPoints, 2) * sizeof(double); double totalMem = mitk::MemoryUtilities::GetTotalSizeOfPhysicalRam(); double percentage = sizeOfPoints / totalMem; return percentage; } void mitk::SurfaceBasedInterpolationController::SetActiveLabel(int activeLabel) { if (m_ActiveLabel == activeLabel) return; if (activeLabel == 0) return; m_ActiveLabel = activeLabel; ContourListMap::iterator it = m_MapOfContourLists.find(m_ActiveLabel); if (it == m_MapOfContourLists.end()) { ContourPositionPairList newList; m_MapOfContourLists.insert(std::pair(m_ActiveLabel, newList)); m_InterpolationResult = nullptr; } this->Modified(); } /* void mitk::SurfaceBasedInterpolationController::RemoveSegmentationFromContourList(mitk::Image *segmentation) { if (segmentation != 0) { m_MapOfContourLists.erase(segmentation); if (m_SelectedSegmentation == segmentation) { SetSegmentationImage(nullptr); m_SelectedSegmentation = 0; } } } */ /* void mitk::SurfaceBasedInterpolationController::OnSegmentationDeleted(const itk::Object *caller, const itk::EventObject &event) { mitk::Image* tempImage = dynamic_cast(const_cast(caller)); if (tempImage) { RemoveSegmentationFromContourList(tempImage); if (tempImage == m_SelectedSegmentation) { SetSegmentationImage(nullptr); m_SelectedSegmentation = 0; } } } */ diff --git a/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp b/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp index 61d35234fc..42e1f82f56 100644 --- a/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp +++ b/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp @@ -1,597 +1,597 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTubeGraphIO.h" #include "mitkCircularProfileTubeElement.h" #include "mitkTubeGraphDefinitions.h" #include "mitkTubeGraphProperty.h" #include #include #include #include namespace mitk { TubeGraphIO::TubeGraphIO(const TubeGraphIO &other) : AbstractFileIO(other) {} TubeGraphIO::TubeGraphIO() : AbstractFileIO( mitk::TubeGraph::GetStaticNameOfClass(), mitk::TubeGraphIO::TUBEGRAPH_MIMETYPE(), "Tube Graph Structure File") { this->RegisterService(); } std::vector TubeGraphIO::Read() { std::locale::global(std::locale("C")); std::vector> result; InputStream stream(this); TiXmlDocument doc; stream >> doc; if (!doc.Error()) { TubeGraph::Pointer newTubeGraph = TubeGraph::New(); TiXmlHandle hDoc(&doc); TiXmlElement *pElem; - TiXmlHandle hRoot(0); + TiXmlHandle hRoot(nullptr); pElem = hDoc.FirstChildElement().Element(); // save this for later hRoot = TiXmlHandle(pElem); pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_GEOMETRY).Element(); // read geometry mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->Initialize(); // read origin mitk::Point3D origin; double temp = 0; pElem->Attribute(mitk::TubeGraphDefinitions::XML_ORIGIN_X, &temp); origin[0] = temp; pElem->Attribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Y, &temp); origin[1] = temp; pElem->Attribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Z, &temp); origin[2] = temp; geometry->SetOrigin(origin); // read spacing Vector3D spacing; pElem->Attribute(mitk::TubeGraphDefinitions::XML_SPACING_X, &temp); spacing.SetElement(0, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_SPACING_Y, &temp); spacing.SetElement(1, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_SPACING_Z, &temp); spacing.SetElement(2, temp); geometry->SetSpacing(spacing); // read transform vtkMatrix4x4 *m = vtkMatrix4x4::New(); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_XX, &temp); m->SetElement(0, 0, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_XY, &temp); m->SetElement(1, 0, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_XZ, &temp); m->SetElement(2, 0, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_YX, &temp); m->SetElement(0, 1, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_YY, &temp); m->SetElement(1, 1, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_YZ, &temp); m->SetElement(2, 1, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZX, &temp); m->SetElement(0, 2, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZY, &temp); m->SetElement(1, 2, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZZ, &temp); m->SetElement(2, 2, temp); m->SetElement(0, 3, origin[0]); m->SetElement(1, 3, origin[1]); m->SetElement(2, 3, origin[2]); m->SetElement(3, 3, 1); geometry->SetIndexToWorldTransformByVtkMatrix(m); geometry->SetImageGeometry(false); // read tube graph // read vertices pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_VERTICES).Element(); - if (pElem != 0) + if (pElem != nullptr) { // walk through the vertices for (TiXmlElement *vertexElement = pElem->FirstChildElement(); vertexElement != nullptr; vertexElement = vertexElement->NextSiblingElement()) { int vertexID(0); mitk::Point3D coordinate; coordinate.Fill(0.0); double diameter(0); vertexElement->Attribute(mitk::TubeGraphDefinitions::XML_VERTEX_ID, &vertexID); TiXmlElement *tubeElement = vertexElement->FirstChildElement(); tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X, &temp); coordinate[0] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y, &temp); coordinate[1] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z, &temp); coordinate[2] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, &diameter); mitk::TubeGraphVertex vertexData; mitk::CircularProfileTubeElement *newElement = new mitk::CircularProfileTubeElement(coordinate, diameter); vertexData.SetTubeElement(newElement); mitk::TubeGraph::VertexDescriptorType newVertex = newTubeGraph->AddVertex(vertexData); if (static_cast(newVertex) != vertexID) { MITK_ERROR << "Aborting tube graph creation, different vertex ids."; return result; ; } } } // read edges pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_EDGES).Element(); if (pElem != nullptr) { // walk through the edges auto edgeElement = pElem->FirstChildElement(); for ( ; edgeElement != nullptr; edgeElement = edgeElement->NextSiblingElement()) { int edgeID(0), edgeSourceID(0), edgeTargetID(0); mitk::Point3D coordinate; double diameter(0); edgeElement->Attribute(mitk::TubeGraphDefinitions::XML_EDGE_ID, &edgeID); edgeElement->Attribute(mitk::TubeGraphDefinitions::XML_EDGE_SOURCE_ID, &edgeSourceID); edgeElement->Attribute(mitk::TubeGraphDefinitions::XML_EDGE_TARGET_ID, &edgeTargetID); mitk::TubeGraphEdge edgeData; for (TiXmlElement *tubeElement = edgeElement->FirstChildElement(mitk::TubeGraphDefinitions::XML_ELEMENT); tubeElement != nullptr; tubeElement = tubeElement->NextSiblingElement()) { tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X, &temp); coordinate[0] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y, &temp); coordinate[1] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z, &temp); coordinate[2] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, &diameter); mitk::CircularProfileTubeElement *newElement = new mitk::CircularProfileTubeElement(coordinate, diameter); edgeData.AddTubeElement(newElement); } try { newTubeGraph->AddEdge(edgeSourceID, edgeTargetID, edgeData); } catch (const std::runtime_error &error) { MITK_ERROR << error.what(); return result; } } } // Compute bounding box BoundingBox::Pointer bb = this->ComputeBoundingBox(newTubeGraph); geometry->SetBounds(bb->GetBounds()); MITK_INFO << "Tube Graph read"; MITK_INFO << "Edge numb:" << newTubeGraph->GetNumberOfEdges() << " Vertices: " << newTubeGraph->GetNumberOfVertices(); MITK_INFO << "Reading tube graph property"; mitk::TubeGraphProperty::Pointer newProperty = mitk::TubeGraphProperty::New(); // read label groups pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_LABELGROUPS).Element(); - if (pElem != 0) + if (pElem != nullptr) { // walk through the label groups for (TiXmlElement *labelGroupElement = pElem->FirstChildElement(); labelGroupElement != nullptr; labelGroupElement = labelGroupElement->NextSiblingElement()) { mitk::TubeGraphProperty::LabelGroup *newLabelGroup = new mitk::TubeGraphProperty::LabelGroup(); const char *labelGroupName; labelGroupName = labelGroupElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME.c_str()); if (labelGroupName) newLabelGroup->labelGroupName = labelGroupName; for (TiXmlElement *labelElement = labelGroupElement->FirstChildElement(mitk::TubeGraphDefinitions::XML_LABEL); labelElement != nullptr; labelElement = labelElement->NextSiblingElement()) { mitk::TubeGraphProperty::LabelGroup::Label *newLabel = new mitk::TubeGraphProperty::LabelGroup::Label(); const char *labelName; bool isVisible = true; Color color; labelName = labelElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABEL_NAME.c_str()); if (labelName) newLabel->labelName = labelName; labelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_VISIBILITY, &temp); if (temp == 0) isVisible = false; else isVisible = true; labelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_R, &temp); color[0] = temp; labelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_G, &temp); color[1] = temp; labelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_B, &temp); color[2] = temp; newLabel->isVisible = isVisible; newLabel->labelColor = color; newLabelGroup->labels.push_back(newLabel); } newProperty->AddLabelGroup(newLabelGroup, newProperty->GetLabelGroups().size()); } } // read attributations pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_ATTRIBUTIONS).Element(); - if (pElem != 0) + if (pElem != nullptr) { std::map tubeToLabelsMap; for (TiXmlElement *tubeToLabelElement = pElem->FirstChildElement(); tubeToLabelElement != nullptr; tubeToLabelElement = tubeToLabelElement->NextSiblingElement()) { TubeGraph::TubeDescriptorType tube; mitk::TubeGraphProperty::LabelGroup *labelGroup = new mitk::TubeGraphProperty::LabelGroup(); mitk::TubeGraphProperty::LabelGroup::Label *label = new mitk::TubeGraphProperty::LabelGroup::Label(); tubeToLabelElement->Attribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1, &temp); tube.first = temp; tubeToLabelElement->Attribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2, &temp); tube.second = temp; const char *labelGroupName = tubeToLabelElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME.c_str()); if (labelGroupName) labelGroup = newProperty->GetLabelGroupByName(labelGroupName); const char *labelName = tubeToLabelElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABEL_NAME.c_str()); if (labelName) label = newProperty->GetLabelByName(labelGroup, labelName); if (tube != TubeGraph::ErrorId && labelGroup != nullptr && label != nullptr) { TubeGraphProperty::TubeToLabelGroupType tubeToLabelGroup(tube, labelGroupName); tubeToLabelsMap.insert( std::pair(tubeToLabelGroup, labelName)); } } if (tubeToLabelsMap.size() > 0) newProperty->SetTubesToLabels(tubeToLabelsMap); } // read annotations pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_ANNOTATIONS).Element(); - if (pElem != 0) + if (pElem != nullptr) { for (TiXmlElement *annotationElement = pElem->FirstChildElement(); annotationElement != nullptr; annotationElement = annotationElement->NextSiblingElement()) { mitk::TubeGraphProperty::Annotation *annotation = new mitk::TubeGraphProperty::Annotation(); std::string annotationName; std::string annotationDescription; TubeGraph::TubeDescriptorType tube; annotationName = annotationElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_ANNOTATION_NAME.c_str()); annotation->name = annotationName; annotationDescription = annotationElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_ANNOTATION_DESCRIPTION.c_str()); annotation->description = annotationDescription; annotationElement->Attribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1, &temp); tube.first = temp; annotationElement->Attribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2, &temp); tube.second = temp; if (tube != TubeGraph::ErrorId) { annotation->tube = tube; newProperty->AddAnnotation(annotation); } } } MITK_INFO << "Tube Graph Property read"; newTubeGraph->SetGeometry(geometry); newTubeGraph->SetProperty("Tube Graph.Visualization Information", newProperty); result.push_back(newTubeGraph.GetPointer()); } else { mitkThrow() << "Parsing error at line " << doc.ErrorRow() << ", col " << doc.ErrorCol() << ": " << doc.ErrorDesc(); } return result; } AbstractFileIO::ConfidenceLevel TubeGraphIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; return Supported; } void TubeGraphIO::Write() { OutputStream out(this); if (!out.good()) { mitkThrow() << "Stream not good."; } std::locale previousLocale(out.getloc()); std::locale I("C"); out.imbue(I); const mitk::TubeGraph *tubeGraph = dynamic_cast(this->GetInput()); // Get geometry of the tube graph mitk::Geometry3D::Pointer geometry = dynamic_cast(tubeGraph->GetGeometry()); // Get property of the tube graph mitk::TubeGraphProperty::Pointer tubeGraphProperty = dynamic_cast( tubeGraph->GetProperty("Tube Graph.Visualization Information").GetPointer()); // Create XML document TiXmlDocument documentXML; { // Begin document TiXmlDeclaration *declXML = new TiXmlDeclaration("1.0", "", ""); documentXML.LinkEndChild(declXML); TiXmlElement *mainXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_TUBEGRAPH_FILE); mainXML->SetAttribute(mitk::TubeGraphDefinitions::XML_FILE_VERSION, mitk::TubeGraphDefinitions::VERSION_STRING); documentXML.LinkEndChild(mainXML); TiXmlElement *geometryXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_GEOMETRY); { // begin geometry geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XX, geometry->GetMatrixColumn(0)[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XY, geometry->GetMatrixColumn(0)[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XZ, geometry->GetMatrixColumn(0)[2]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YX, geometry->GetMatrixColumn(1)[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YY, geometry->GetMatrixColumn(1)[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YZ, geometry->GetMatrixColumn(1)[2]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZX, geometry->GetMatrixColumn(2)[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZY, geometry->GetMatrixColumn(2)[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZZ, geometry->GetMatrixColumn(2)[2]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_X, geometry->GetOrigin()[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Y, geometry->GetOrigin()[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Z, geometry->GetOrigin()[2]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_SPACING_X, geometry->GetSpacing()[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_SPACING_Y, geometry->GetSpacing()[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_SPACING_Z, geometry->GetSpacing()[2]); } // end geometry mainXML->LinkEndChild(geometryXML); TiXmlElement *verticesXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_VERTICES); { // begin vertices section std::vector vertexVector = tubeGraph->GetVectorOfAllVertices(); for (unsigned int index = 0; index < vertexVector.size(); index++) { TiXmlElement *vertexXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_VERTEX); vertexXML->SetAttribute(mitk::TubeGraphDefinitions::XML_VERTEX_ID, tubeGraph->GetVertexDescriptor(vertexVector[index])); // element of each vertex const mitk::TubeElement *element = vertexVector[index].GetTubeElement(); TiXmlElement *elementXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ELEMENT); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X, element->GetCoordinates().GetElement(0)); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y, element->GetCoordinates().GetElement(1)); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z, element->GetCoordinates().GetElement(2)); if (dynamic_cast(element)) elementXML->SetDoubleAttribute( mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, (dynamic_cast(element))->GetDiameter()); else elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, 2); vertexXML->LinkEndChild(elementXML); verticesXML->LinkEndChild(vertexXML); } } // end vertices section mainXML->LinkEndChild(verticesXML); TiXmlElement *edgesXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_EDGES); { // begin edges section std::vector edgeVector = tubeGraph->GetVectorOfAllEdges(); for (unsigned int index = 0; index < edgeVector.size(); index++) { TiXmlElement *edgeXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_EDGE); edgeXML->SetAttribute(mitk::TubeGraphDefinitions::XML_EDGE_ID, index); std::pair soureTargetPair = tubeGraph->GetVerticesOfAnEdge(tubeGraph->GetEdgeDescriptor(edgeVector[index])); edgeXML->SetAttribute(mitk::TubeGraphDefinitions::XML_EDGE_SOURCE_ID, tubeGraph->GetVertexDescriptor(soureTargetPair.first)); edgeXML->SetAttribute(mitk::TubeGraphDefinitions::XML_EDGE_TARGET_ID, tubeGraph->GetVertexDescriptor(soureTargetPair.second)); // begin elements of the edge std::vector elementVector = edgeVector[index].GetElementVector(); for (unsigned int elementIndex = 0; elementIndex < elementVector.size(); elementIndex++) { TiXmlElement *elementXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ELEMENT); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X, elementVector[elementIndex]->GetCoordinates().GetElement(0)); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y, elementVector[elementIndex]->GetCoordinates().GetElement(1)); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z, elementVector[elementIndex]->GetCoordinates().GetElement(2)); if (dynamic_cast(elementVector[elementIndex])) elementXML->SetDoubleAttribute( mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, (dynamic_cast(elementVector[elementIndex]))->GetDiameter()); else elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, 2); edgeXML->LinkEndChild(elementXML); // elementsXML->LinkEndChild(elementXML); } edgesXML->LinkEndChild(edgeXML); } } // end edges section mainXML->LinkEndChild(edgesXML); TiXmlElement *labelGroupsXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_LABELGROUPS); { // begin label group section std::vector labelGroupVector = tubeGraphProperty->GetLabelGroups(); for (unsigned int index = 0; index < labelGroupVector.size(); index++) { TiXmlElement *labelGroupXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_LABELGROUP); labelGroupXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME, labelGroupVector[index]->labelGroupName); // begin labels of the label group std::vector labelVector = labelGroupVector[index]->labels; for (unsigned int labelIndex = 0; labelIndex < labelVector.size(); labelIndex++) { TiXmlElement *labelXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_LABEL); labelXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_NAME, labelVector[labelIndex]->labelName); labelXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_VISIBILITY, labelVector[labelIndex]->isVisible); labelXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_R, labelVector[labelIndex]->labelColor[0]); labelXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_G, labelVector[labelIndex]->labelColor[1]); labelXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_B, labelVector[labelIndex]->labelColor[2]); labelGroupXML->LinkEndChild(labelXML); } labelGroupsXML->LinkEndChild(labelGroupXML); } } // end labe group section mainXML->LinkEndChild(labelGroupsXML); TiXmlElement *attributionsXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ATTRIBUTIONS); { // begin attributions section std::map tubeToLabelGroup = tubeGraphProperty->GetTubesToLabels(); for (std::map::iterator it = tubeToLabelGroup.begin(); it != tubeToLabelGroup.end(); it++) { TiXmlElement *attributXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ATTRIBUTION); attributXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1, it->first.first.first); attributXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2, it->first.first.second); attributXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME, it->first.second); attributXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_NAME, it->second); attributionsXML->LinkEndChild(attributXML); } } // end attributions section mainXML->LinkEndChild(attributionsXML); TiXmlElement *annotationsXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ANNOTATIONS); { // begin annotations section std::vector annotations = tubeGraphProperty->GetAnnotations(); for (unsigned int index = 0; index < annotations.size(); index++) { TiXmlElement *annotationXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ANNOTATION); annotationXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ANNOTATION_NAME, annotations[index]->name); annotationXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ANNOTATION_DESCRIPTION, annotations[index]->description); annotationXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1, annotations[index]->tube.first); annotationXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2, annotations[index]->tube.second); annotationsXML->LinkEndChild(annotationXML); } } // end annotations section mainXML->LinkEndChild(annotationsXML); } // end document TiXmlPrinter printer; printer.SetStreamPrinting(); documentXML.Accept(&printer); out << printer.Str(); } AbstractFileIO::ConfidenceLevel TubeGraphIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; return Supported; } TubeGraphIO *TubeGraphIO::IOClone() const { return new TubeGraphIO(*this); } } const mitk::BoundingBox::Pointer mitk::TubeGraphIO::ComputeBoundingBox(mitk::TubeGraph::Pointer graph) const { BoundingBox::Pointer boundingBox = BoundingBox::New(); BoundingBox::PointIdentifier pointid = 0; BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); ScalarType nullpoint[] = {0, 0, 0}; BoundingBox::PointType p(nullpoint); // traverse the tree and add each point to the pointscontainer mitk::Point3D pos; std::vector vertexVector = graph->GetVectorOfAllVertices(); for (std::vector::iterator vertex = vertexVector.begin(); vertex != vertexVector.end(); ++vertex) { pos = vertex->GetTubeElement()->GetCoordinates(); p[0] = pos[0]; p[1] = pos[1]; p[2] = pos[2]; pointscontainer->InsertElement(pointid++, p); } std::vector edgeVector = graph->GetVectorOfAllEdges(); for (std::vector::iterator edge = edgeVector.begin(); edge != edgeVector.end(); ++edge) { std::vector allElements = edge->GetElementVector(); for (unsigned int index = 0; index < edge->GetNumberOfElements(); index++) { pos = allElements[index]->GetCoordinates(); p[0] = pos[0]; p[1] = pos[1]; p[2] = pos[2]; pointscontainer->InsertElement(pointid++, p); } } boundingBox->SetPoints(pointscontainer); boundingBox->ComputeBoundingBox(); return boundingBox; }