00001 /* 00002 Copyright (C) 2010 - 2019 Christoph Hänisch 00003 00004 Chair of Medical Engineering (mediTEC) 00005 RWTH Aachen University 00006 Pauwelsstr. 20 00007 52074 Aachen 00008 Germany 00009 00010 See license.txt for more information. 00011 00012 Version 0.6.0 (2019-09-12) 00013 */ 00014 00019 #ifndef TOOLS_H_9034987422 00020 #define TOOLS_H_9034987422 00021 00022 00023 #include <cassert> 00024 #include <cmath> 00025 #include <cstdlib> 00026 #include <limits> 00027 #include <list> 00028 #include <sstream> 00029 #include <string> 00030 #include <tuple> 00031 #include <utility> 00032 #include <vector> 00033 00034 #include <Eigen/Core> 00035 00036 #include "Coordinate.hpp" 00037 00038 #ifdef QT_VERSION 00039 #include <QTransform> 00040 #endif // QT_VERSION 00041 00042 00043 namespace TRTK 00044 { 00045 00046 00047 template <class T> class Coordinate; 00048 00049 00050 namespace Tools 00051 { 00052 00053 00097 #ifdef EIGEN_MACROS_H 00098 #ifdef QT_VERSION 00099 00109 template <class EigenMatrix> 00110 QTransform Eigen3x3_to_QTransfom(const EigenMatrix & eigenMatrix) 00111 { 00112 QTransform qTransform(eigenMatrix(0, 0), eigenMatrix(0, 1), eigenMatrix(0, 2), 00113 eigenMatrix(1, 0), eigenMatrix(1, 1), eigenMatrix(1, 2), 00114 eigenMatrix(2, 0), eigenMatrix(2, 1), eigenMatrix(2, 2)); 00115 00116 return qTransform; 00117 } 00118 00119 #endif // QT_VERSION 00120 #endif // EIGEN_MACROS_H 00121 00122 00154 template <class T> 00155 std::pair<Eigen::Matrix<T, 3, 1>, T> axisAngleFromRotationMatrix(const Eigen::Matrix<T, 3, 3> & matrix) 00156 { 00157 // The implementation is based on the lecture notes of Carlo Tomasi. See [1] for more information. 00158 // [1] https://www2.cs.duke.edu/courses/fall13/compsci527/notes/rodrigues.pdf 00159 00160 using namespace Eigen; 00161 using Matrix3T = Matrix<T, 3, 3>; 00162 using Vector3T = Matrix<T, 3, 1>; 00163 00164 const T eps = 1000 * std::numeric_limits<T>::epsilon(); 00165 const T pi = 3.14159265359; 00166 00167 Matrix3T A = (matrix - matrix.transpose()) / 2; 00168 Vector3T rho = Vector3T(A(2, 1), A(0, 2), A(1, 0)); 00169 T s = rho.norm(); 00170 T c = (matrix.trace() - 1) / 2; 00171 00172 Vector3T axis; 00173 T angle; 00174 00175 if (abs(s - 0) < eps && abs(c - 1) < eps) 00176 { 00177 axis = Vector3T(1, 0, 0); 00178 angle = 0; 00179 } 00180 else if (abs(s - 0) < eps && abs(c + 1) < eps) 00181 { 00182 angle = pi; 00183 Matrix3T B = matrix + Matrix3T::Identity(); 00184 for (int i = 0; i < 3; ++i) 00185 { 00186 axis = B.col(i).normalized(); 00187 if (axis.norm() > 0) break; 00188 } 00189 // Assure uniqueness by forcing the axis onto the half-hemisphere. 00190 if (abs(axis.x()) < eps && abs(axis.y()) < eps && axis.z() < 0 || 00191 abs(axis.x()) < eps && axis.y() < 0 || 00192 axis.x() < 0) 00193 { 00194 angle = -angle; 00195 axis = -axis; 00196 } 00197 } 00198 else 00199 { 00200 using ::atan2; 00201 axis = rho / s; 00202 angle = atan2(s, c); 00203 } 00204 00205 return std::make_pair(axis, angle); 00206 } 00207 00208 00225 template <class T> 00226 std::tuple<T, T, T> cartesian2Spherical(const T & x, const T & y, const T & z) 00227 { 00228 const T pi = 3.1415926535897932384626433; 00229 using ::sqrt; 00230 auto r = sqrt(x * x + y * y + z * z); 00231 T theta; 00232 if (r == 0) 00233 theta = 0; 00234 else 00235 theta = acos(z / r); 00236 auto phi = atan2(y, x); 00237 00238 assert(0 <= r); 00239 assert(0 <= theta && theta <= pi); 00240 assert(-pi <= phi && phi <= pi); 00241 00242 return std::make_tuple(r, theta, phi); 00243 } 00244 00245 00262 template <class T> 00263 Eigen::Matrix<T, 3, 1> cartesian2Spherical(const Eigen::Matrix<T, 3, 1> & point) 00264 { 00265 Eigen::Matrix<T, 3, 1> result; 00266 std::tie(result.x(), result.y(), result.z()) = cartesian2Spherical(point.x(), point.y(), point.z()); 00267 return result; 00268 } 00269 00270 00287 template <class T> 00288 Coordinate<T> cartesian2Spherical(const Coordinate<T> & point) 00289 { 00290 Coordinate<T> result(0, 0, 0); 00291 std::tie(result.x(), result.y(), result.z()) = cartesian2Spherical(point.x(), point.y(), point.z()); 00292 return result; 00293 } 00294 00295 00298 bool fileExists(const char * file_name); 00299 00300 00303 unsigned long long fileLength(const char * file_name); 00304 00305 00308 unsigned long long fileLength(std::ifstream & file_stream); 00309 00310 00313 std::string getCurrentDate(); 00314 00315 00318 std::string getCurrentTime(); 00319 00320 00379 template <class Derived, class Base> 00380 inline bool isClass(Base * base_ptr) 00381 { 00382 const Derived * derived_ptr = dynamic_cast<const Derived *>(base_ptr); 00383 return derived_ptr == NULL ? 0 : 1; 00384 } 00385 00386 00405 template <class Derived, class Base> 00406 inline bool isClass(Base & base) 00407 { 00408 return isClass<Derived>(&base); 00409 00410 /* 00411 // This is an alternative implementation. However, the above one might be 00412 // better to be optimized by the compiler. 00413 00414 // it might not be optimized 00415 00416 try 00417 { 00418 Derived & derived = dynamic_cast<Derived &>(base); 00419 return 1; 00420 } 00421 catch (bad_cast) 00422 { 00423 return 0; 00424 } 00425 */ 00426 } 00427 00428 00471 template <typename Base> 00472 inline bool isDerivedFrom(const Base * base_ptr) 00473 { 00474 return true; 00475 } 00476 00477 00488 template <typename Base> 00489 inline bool isDerivedFrom(const void * base_ptr) 00490 { 00491 return false; 00492 } 00493 00494 00522 template <class T> 00523 inline bool isEqual(const T x, const T y) 00524 { 00525 using std::abs; 00526 using std::numeric_limits; 00527 00528 const T epsilon = 10 * numeric_limits<T>::epsilon() > 1e-13 ? 10 * numeric_limits<T>::epsilon() : 1e-13; 00529 return abs(x - y) < epsilon; // needs "cmath" and "limits" to be included 00530 } 00531 00532 template <> 00533 inline bool isEqual<int>(const int x, const int y) 00534 { 00535 return x == y; 00536 } 00537 00538 00539 00567 template <class T> 00568 inline bool isZero(const T value) 00569 { 00570 return isEqual<T>(value, T(0)); 00571 } 00572 00573 00583 template <class T> 00584 std::vector<T> listToVector(const std::list<T> & lst) 00585 { 00586 int i = 0; 00587 std::vector<T> vec(lst.size()); 00588 typename std::list<T>::const_iterator it; 00589 00590 for (it = lst.begin(); it != lst.end(); ++it) 00591 { 00592 vec[i++] = *it; 00593 } 00594 00595 return vec; 00596 } 00597 00598 00608 template <class Container, class ValueType> // TODO: C++11 --> class ValueType = Container::value_type 00609 inline ValueType mean(const Container & container, ValueType null_value = ValueType()) 00610 { 00611 ValueType mean = null_value; 00612 00613 for (typename Container::const_iterator it = container.begin(); it != container.end(); ++it) 00614 { 00615 mean = mean + *it; 00616 } 00617 00618 return mean / container.size(); 00619 } 00620 00621 00646 template <class WeightsContainer, class ValuesContainer, class ValueType = typename ValuesContainer::value_type> 00647 inline ValueType weightedMean(const WeightsContainer & weights, 00648 const ValuesContainer & values, 00649 ValueType null_value = ValueType(0)) 00650 { 00651 using WeightType = typename WeightsContainer::value_type; // should be a scalar 00652 00653 WeightType sum_of_weights = WeightType(0); 00654 ValueType weighted_sum = null_value; 00655 00656 auto it_weights = weights.begin(); 00657 auto it_values = values.begin(); 00658 00659 while (it_weights != weights.end() && it_values != values.end()) 00660 { 00661 auto weight = *it_weights; 00662 auto value = *it_values; 00663 00664 sum_of_weights += weight; 00665 weighted_sum += weight * value; 00666 00667 ++it_weights; 00668 ++it_values; 00669 } 00670 00671 return weighted_sum / sum_of_weights; 00672 } 00673 00674 00685 template <class Container> 00686 inline auto median(const Container & container) -> std::decay_t<decltype(*begin(container))> 00687 { 00688 using T = std::decay_t<decltype(*begin(container))>; 00689 using std::begin; 00690 using std::end; 00691 00692 auto v = std::vector<T>(begin(container), end(container)); 00693 auto size = v.size(); 00694 00695 if (size == 0) // undefined 00696 { 00697 using std::numeric_limits; 00698 return numeric_limits<T>::signaling_NaN(); 00699 } 00700 else 00701 { 00702 std::sort(begin(v), end(v)); 00703 if (size % 2 == 0) 00704 { 00705 auto it1 = begin(v); 00706 auto it2 = begin(v); 00707 std::advance(it1, size / 2 - 1); 00708 std::advance(it2, size / 2); 00709 return (*it1 + *it2) / 2; 00710 } 00711 else 00712 { 00713 auto it = begin(v); 00714 std::advance(it, size / 2); 00715 return *it; 00716 } 00717 } 00718 } 00719 00720 00731 template <class T> 00732 TRTK::Coordinate<T> orthogonalMatrixToQuaternion(const Eigen::Matrix<T, 3, 3> & matrix) 00733 { 00734 // References: 00735 // 00736 // [1] http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm 00737 // [2] http://en.wikipedia.org/wiki/Rotation_matrix 00738 00739 // Note: Coordinate<T> is of the form (x, y, z, w) whereas quaternions are often notated (w, x, y, z)! 00740 00741 using std::sqrt; 00742 00743 TRTK::Coordinate<T> quaternion(0, 0, 0, 0); 00744 00745 T trace = matrix.trace(); 00746 00747 if (trace > -1) // check because of the computation of the root 00748 { 00749 double s = 0.5 / sqrt(trace + 1); 00750 quaternion.x() = 0.25 / s; 00751 quaternion.y() = (matrix(2,1) - matrix(1,2)) * s; 00752 quaternion.z() = (matrix(0,2) - matrix(2,0)) * s; 00753 quaternion.w() = (matrix(1,0) - matrix(0,1)) * s; 00754 } 00755 else if (matrix(0,0) > matrix(1,1) && matrix(0,0) > matrix(2,2)) 00756 { 00757 double s = 2.0 * sqrt(1 + matrix(0,0) - matrix(1,1) - matrix(2,2)); 00758 quaternion.x() = (matrix(2,1) - matrix(1,2)) / s; 00759 quaternion.y() = 0.25 * s; 00760 quaternion.z() = (matrix(0,1) + matrix(1,0)) / s; 00761 quaternion.w() = (matrix(0,2) + matrix(2,0)) / s; 00762 } 00763 else if (matrix(1,1) > matrix(2,2)) 00764 { 00765 double s = 2 * sqrt(1 + matrix(1,1) - matrix(0,0) - matrix(2,2)); 00766 quaternion.x() = (matrix(0,2) - matrix(2,0)) / s; 00767 quaternion.y() = (matrix(0,1) + matrix(1,0)) / s; 00768 quaternion.z() = 0.25 * s; 00769 quaternion.w() = (matrix(1,2) + matrix(2,1)) / s; 00770 } 00771 else 00772 { 00773 double s = 2 * sqrt(1 + matrix(2,2) - matrix(0,0) - matrix(1,1)); 00774 quaternion.x() = (matrix(1,0) - matrix(0,1)) / s; 00775 quaternion.y() = (matrix(0,2) + matrix(2,0)) / s; 00776 quaternion.z() = (matrix(1,2) + matrix(2,1)) / s; 00777 quaternion.w() = 0.25 * s; 00778 } 00779 00780 return quaternion.normalize(); 00781 } 00782 00783 00805 template <class T> 00806 TRTK::Coordinate<T> orthogonalMatrixToQuaternion2(const Eigen::Matrix<T, 3, 3> & matrix) 00807 { 00808 // See [1] for more details about the implementation. 00809 00810 // The matrix K3 is selfadjoint because it is real and symmetric. Thus we can use an 00811 // appropriate solver. The solver employed below only uses the lower triangular part 00812 // of the matrix hence we can leave the upper part uninitialized. 00813 00814 typedef Eigen::Matrix<T, 4, 4> Matrix4T; 00815 typedef Eigen::Matrix<T, 4, 1> Vector4T; 00816 00817 Matrix4T K3; 00818 00819 K3(0,0) = matrix(0,0) - matrix(1,1) - matrix(2,2); 00820 K3(1,0) = matrix(1,0) + matrix(0,1); 00821 K3(2,0) = matrix(2,0) + matrix(0,2); 00822 K3(3,0) = matrix(1,2) - matrix(2,1); 00823 K3(1,1) = matrix(1,1) - matrix(0,0) - matrix(2,2); 00824 K3(2,1) = matrix(2,1) + matrix(1,2); 00825 K3(3,1) = matrix(2,0) - matrix(0,2); 00826 K3(2,2) = matrix(2,2) - matrix(0,0) - matrix(1,1); 00827 K3(3,2) = matrix(0,1) - matrix(1,0); 00828 K3(3,3) = matrix(0,0) + matrix(1,1) + matrix(2,2); 00829 00830 K3 /= 3; 00831 00832 Eigen::SelfAdjointEigenSolver<Matrix4T> solver(K3); 00833 00834 Vector4T quaternion = solver.eigenvectors().col(3).normalized(); 00835 00836 return Coordinate<T>(quaternion); 00837 } 00838 00839 00850 template <class T> 00851 Eigen::Matrix<T, 3, 3> quaternionToOrthogonalMatrix(T q0, T q1, T q2, T q3) 00852 { 00853 // Revised code. 00854 00855 // Identical to what you can find in the Wikipedia article 00856 // https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation 00857 // This matrix is a left-handed (post-multiplied) rotation matrix. 00858 00859 assert(abs(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3 - 1) < 1e-5); // check for unit quaternion 00860 00861 Eigen::Matrix<T, 3, 3> R; 00862 00863 R(0, 0) = 1 - 2 * (q2 * q2 + q3 * q3); 00864 R(0, 1) = 2 * q1 * q2 - 2 * q0 * q3; 00865 R(0, 2) = 2 * q1 * q3 + 2 * q0 * q2; 00866 00867 R(1, 0) = 2 * q1 * q2 + 2 * q0 * q3; 00868 R(1, 1) = 1 - 2 * (q1 * q1 + q3 * q3); 00869 R(1, 2) = 2 * q2 * q3 - 2 * q0 * q1; 00870 00871 R(2, 0) = 2 * q1 * q3 - 2 * q0 * q2; 00872 R(2, 1) = 2 * q2 * q3 + 2 * q0 * q1; 00873 R(2, 2) = 1 - 2 * (q1 * q1 + q2 * q2); 00874 00875 return R; 00876 } 00877 00878 00889 template <class T> 00890 inline Eigen::Matrix<T, 3, 3> quaternionToOrthogonalMatrix(const TRTK::Coordinate<T> & quaternion) 00891 { 00892 assert(quaternion.size() == 4); 00893 return quaternionToOrthogonalMatrix<T>(quaternion.x(), quaternion.y(), quaternion.z(), quaternion.w()); 00894 } 00895 00896 00918 template <class T> 00919 inline T rand() 00920 { 00921 return static_cast<T>(std::rand()) / static_cast<T>(RAND_MAX); 00922 } 00923 00924 00948 template <class T> 00949 T rand(T a, T b) 00950 { 00951 assert(a <= b); 00952 return static_cast<T>(std::rand()) / static_cast<T>(RAND_MAX) * (b - a) + a; 00953 } 00954 00955 template <> 00956 inline int rand<int>(int a, int b) 00957 { 00958 assert(a <= b); 00959 return int(rand<double>(a, b)); 00960 } 00961 00962 template <> 00963 inline unsigned rand<unsigned>(unsigned a, unsigned b) 00964 { 00965 assert(0 <= a && a <= b); 00966 return unsigned(rand<double>(a, b)); 00967 } 00968 00969 00990 template <class T> 00991 T randn() 00992 { 00993 /* 00994 This algorithm uses the Marsaglia polar method. For more details, please 00995 have a look at "A convenient method for generating normal variables" by 00996 G. Marsaglia and T. A. Bray (SIAM Rev. 6, 260-264, 1964). 00997 */ 00998 00999 // The polar method yields two random variables per computation. So one 01000 // random variable is cached each time and returned on the next call. 01001 01002 using std::log; 01003 using std::sqrt; 01004 01005 static bool cache_is_valid = false; 01006 static T cached_random_variable = 0; 01007 01008 if (!cache_is_valid) 01009 { 01010 T v1, v2, s; 01011 01012 do 01013 { 01014 v1 = rand<T>(-1, 1); 01015 v2 = rand<T>(-1, 1); 01016 01017 s = v1 * v1 + v2 * v2; 01018 } 01019 while (s >= 1 || s == 0); 01020 01021 const T t = sqrt(-2 * log(s) / s); 01022 01023 T random_variable = v1 * t; 01024 01025 cached_random_variable = v2 * t; 01026 cache_is_valid = true; 01027 01028 return random_variable; 01029 } 01030 else 01031 { 01032 cache_is_valid = false; 01033 01034 return cached_random_variable; 01035 } 01036 } 01037 01038 double randn(); // Convenience function for randn<double>(). 01039 01040 01061 template <class T> 01062 inline T randn(T mu, T sigma) 01063 { 01064 return sigma * randn<T>() + mu; 01065 } 01066 01067 template <> 01068 inline int randn(int mu, int sigma) 01069 { 01070 return int(sigma * randn<double>() + mu); 01071 } 01072 01073 01091 template <class T> int sign(T value) 01092 { 01093 return (T(0) < value) - (value < T(0)); 01094 } 01095 01096 01115 template <class T> 01116 Eigen::Matrix<T, 3, 3> rotationMatrix(const Eigen::Matrix<T, 3, 1> & axis_, double angle) 01117 { 01118 using Vector3T = Eigen::Matrix<T, 3, 1>; 01119 using Matrix3T = Eigen::Matrix<T, 3, 3>; 01120 01121 T axis_magnitude = axis_.norm(); 01122 assert(axis_magnitude > 0); // Axis may not be a null vector. 01123 Vector3T axis = axis_ / axis_magnitude; 01124 01125 // Find an orthogonal vector. 01126 01127 const T eps = std::numeric_limits<T>::epsilon(); 01128 01129 auto isNullvector = [&] (Vector3T & vec) -> bool 01130 { 01131 return vec.norm() < 10 * eps; 01132 }; 01133 01134 auto isCollinear = [&] (Vector3T a, Vector3T b) -> bool 01135 { 01136 a.normalize(); 01137 b.normalize(); 01138 return abs(abs(a.dot(b)) - 1) < 10 * eps; 01139 }; 01140 01141 auto vec = Vector3T::Random().normalized(); 01142 while (isNullvector(vec) || isCollinear(vec, axis)) 01143 { 01144 vec = Vector3T::Random().normalized(); 01145 } 01146 01147 auto vec_orthonormal = (vec - vec.dot(axis) * axis).normalized(); // Cf. Gram-Schmidt process 01148 assert(abs(axis.dot(vec_orthonormal)) < 1000 * eps); // Axis and vec_orthonormal must be orthonormal. Implementation error. 01149 01150 // Compute the rotation matrix 01151 01152 // Make a change of basis such that the rotation axis is aligned 01153 // with the x-axis, rotate around this axis and revert the change of 01154 // basis. 01155 01156 Matrix3T M; 01157 M.row(0) = axis; 01158 M.row(1) = vec_orthonormal; 01159 M.row(2) = axis.cross(vec_orthonormal); 01160 01161 Matrix3T R_x; 01162 R_x << 1, 0, 0, 01163 0, cos(angle), -sin(angle), 01164 0, sin(angle), cos(angle); 01165 01166 return M.transpose() * R_x * M; 01167 } 01168 01169 01194 template <class T> 01195 T round(const T number) 01196 { 01197 using std::ceil; 01198 using std::floor; 01199 01200 return (number < 0.0) ? ceil(number - 0.5) : floor(number + 0.5); 01201 } 01202 01203 template <> 01204 inline int round<int>(const int number) 01205 { 01206 return number; 01207 } 01208 01209 01230 template <class T> 01231 std::tuple<T, T, T> spherical2Cartesian(const T & r, const T & theta, const T & phi) 01232 { 01233 using ::sin; 01234 using ::cos; 01235 double x = r * sin(theta) * cos(phi); 01236 double y = r * sin(theta) * sin(phi); 01237 double z = r * cos(theta); 01238 return std::make_tuple(x, y, z); 01239 } 01240 01241 01260 template <class T> 01261 Coordinate<T> spherical2Cartesian(const Coordinate<T> & point) 01262 { 01263 Coordinate<T> result(0, 0, 0); 01264 std::tie(result.x(), result.y(), result.z()) = spherical2Cartesian(point.x(), point.y(), point.z()); 01265 return result; 01266 } 01267 01268 01287 template <class T> 01288 Eigen::Matrix<T, 3, 1> spherical2Cartesian(const Eigen::Matrix<T, 3, 1> & point) 01289 { 01290 Eigen::Matrix<T, 3, 1> result; 01291 std::tie(result.x(), result.y(), result.z()) = spherical2Cartesian(point.x(), point.y(), point.z()); 01292 return result; 01293 } 01294 01295 01335 template <class Container, class ValueType> // TODO: C++11 --> class ValueType = Container::value_type 01336 inline ValueType standardDeviation(const Container & container, ValueType null_value = ValueType()) 01337 { 01338 size_t n = container.size(); 01339 assert (n >= 0); 01340 01341 if (n == 0) return null_value; 01342 01343 ValueType mean = Tools::mean(container, null_value); 01344 ValueType variance = null_value; 01345 01346 for (typename Container::const_iterator it = container.begin(); it != container.end(); ++it) 01347 { 01348 variance = variance + (*it - mean) * (*it - mean); 01349 } 01350 01351 double c4 = 1.0 - 1.0/(4.0*n) - 7.0/(32.0*n*n) - 19.0/(128.0*n*n*n); 01352 01353 using std::sqrt; 01354 01355 return sqrt(variance / (n - 1)) / c4; 01356 } 01357 01358 01376 template <class T> 01377 inline std::string toString (const T & value) 01378 { 01379 std::stringstream ss; 01380 ss << value; 01381 01382 return ss.str(); 01383 } 01384 01385 01400 template <class Container, class ValueType> // TODO: C++11 --> class ValueType = Container::value_type 01401 inline ValueType variance(const Container & container, ValueType null_value = ValueType()) 01402 { 01403 assert(container.size() >= 0); 01404 01405 if (container.size() == 0) return null_value; 01406 01407 ValueType mean = Tools::mean(container, null_value); 01408 ValueType variance = null_value; 01409 01410 for (typename Container::const_iterator it = container.begin(); it != container.end(); ++it) 01411 { 01412 variance = variance + (*it - mean) * (*it - mean); 01413 } 01414 01415 return variance / (container.size() - 1); 01416 } 01417 01418 01428 template <class T> 01429 std::list<T> vectorToList(const std::vector<T> & vec) 01430 { 01431 typename std::list<T> lst; 01432 01433 for (unsigned i = 0; i < vec.size(); ++i) 01434 { 01435 lst.push_back(vec[i]); 01436 } 01437 01438 return lst; 01439 } 01440 01441 01451 template <class T1, class T2> 01452 std::vector<std::pair<T1, T2> > zip(const std::vector<T1> & v1, const std::vector<T2> & v2) 01453 { 01454 size_t size_v1 = v1.size(); 01455 size_t size_v2 = v2.size(); 01456 assert(size_v1 == size_v2); 01457 01458 std::vector<std::pair<T1, T2> > zipped; 01459 zipped.reserve(size_v1); 01460 01461 for (size_t i = 0; i < size_v1; ++i) 01462 { 01463 zipped.push_back(std::make_pair(v1[i], v2[i])); 01464 } 01465 01466 return zipped; 01467 } 01468 01469 01470 } // namspace Tools 01471 01472 01473 } // namespace TRTK 01474 01475 01476 #endif // TOOLS_H_9034987422
Documentation generated by Doxygen