/*! \file vectors.h * \brief Coordinate transforms and vector arithmetics. */ #ifndef VECTORS_H #define VECTORS_H #include #include #ifndef M_PI_2 #define M_PI_2 (1.570796326794896619231321691639751442098584699687552910487) #endif #include "qpms_types.h" #include "qpms_error.h" //static inline double vectors_h_sq(double x) {return x*x;} static const cart2_t CART2_ZERO = {0, 0}; static const cart3_t CART3_ZERO = {0, 0, 0}; /// 2D vector addition. static inline cart2_t cart2_add(const cart2_t a, const cart2_t b) { cart2_t res = {a.x+b.x, a.y+b.y}; return res; } /// 2D vector substraction. static inline cart2_t cart2_substract(const cart2_t a, const cart2_t b) { cart2_t res = {a.x-b.x, a.y-b.y}; return res; } /// 2D vector scaling. static inline cart2_t cart2_scale(const double c, const cart2_t v) { cart2_t res = {c * v.x, c * v.y}; return res; } /// 2D vector dot product. static inline double cart2_dot(const cart2_t a, const cart2_t b) { return a.x * b.x + a.y * b.y; } static inline double cart2_normsq(const cart2_t a) { return cart2_dot(a, a); } /// 2D vector euclidian norm. static inline double cart2norm(const cart2_t v) { return hypot(v.x, v.y); //sqrt(v.x*v.x + v.y*v.y); } /// 2D cartesian to polar coordinates conversion. See @ref coord_conversions. static inline pol_t cart2pol(const cart2_t cart) { pol_t pol; pol.r = cart2norm(cart); pol.phi = atan2(cart.y, cart.x); return pol; } /// Polar to spherical coordinates conversion. See @ref coord_conversions. static inline sph_t pol2sph_equator(const pol_t pol) { sph_t sph; sph.r = pol.r; sph.phi = pol.phi; sph.theta = M_PI_2; return sph; } /// 2D cartesian to spherical coordinates conversion. See @ref coord_conversions. static inline sph_t cart22sph(const cart2_t cart) { sph_t sph; sph.r = cart2norm(cart); sph.theta = M_PI_2; sph.phi = atan2(cart.y, cart.x); return sph; } /// 1D cartesian to spherical coordinates conversion. See @ref coord_conversions. static inline sph_t cart12sph_zaxis(double z) { sph_t sph = {fabs(z), z < 0 ? M_PI : 0, 0}; return sph; } /// 1D to 3D cartesian coordinates conversion. See @ref coord_conversions. static inline cart3_t cart12cart3z(double z) { cart3_t c = {0, 0, z}; return c; } /// 2D to 3D cartesian coordinates conversion. See @ref coord_conversions. static inline cart3_t cart22cart3xy(const cart2_t a) { cart3_t c; c.x = a.x; c.y = a.y; c.z = 0; return c; } static inline cart2_t cart3xy2cart2(const cart3_t a) { cart2_t c = {a.x, a.y}; return c; } /// 3D vector dot product. static inline double cart3_dot(const cart3_t a, const cart3_t b) { return a.x * b.x + a.y * b.y + a.z * b.z; } /// 3D vector product a.k.a. cross product. static inline cart3_t cart3_vectorprod(const cart3_t a, const cart3_t b) { cart3_t c = { .x = a.y * b.z - a.z * b.y, .y = a.z * b.x - a.x * b.z, .z = a.x * b.y - a.y * b.x, }; return c; } /// Scalar triple product \f$ a \cdot ( b \times c ) \f$. static inline double cart3_tripleprod(const cart3_t a, const cart3_t b, const cart3_t c) { return cart3_dot(a, cart3_vectorprod(b, c)); } /// 3D vector euclidian norm squared. static inline double cart3_normsq(const cart3_t a) { return cart3_dot(a, a); } /// 3D vector euclidian norm. static inline double cart3norm(const cart3_t v) { return sqrt(cart3_normsq(v)); } /// 3D cartesian to spherical coordinates conversion. See @ref coord_conversions. static inline sph_t cart2sph(const cart3_t cart) { sph_t sph; sph.r = cart3norm(cart); sph.theta = sph.r ? acos(cart.z / sph.r) : M_PI_2; sph.phi = atan2(cart.y, cart.x); return sph; } /// Spherical to 3D cartesian coordinates conversion. See @ref coord_conversions. static inline cart3_t sph2cart(const sph_t sph) { cart3_t cart; double sin_th = #ifdef QPMS_VECTORS_NICE_TRANSFORMATIONS (sph.theta == M_PI) ? 0 : #endif sin(sph.theta); cart.x = sph.r * sin_th * cos(sph.phi); cart.y = sph.r * sin_th * sin(sph.phi); cart.z = sph.r * cos(sph.theta); return cart; } /// Polar to 2D cartesian coordinates conversion. See @ref coord_conversions. static inline cart2_t pol2cart(const pol_t pol) { cart2_t cart; cart.x = pol.r * cos(pol.phi); cart.y = pol.r * sin(pol.phi); return cart; } /// Polar to 3D cartesian coordinates conversion. See @ref coord_conversions. static inline cart3_t pol2cart3_equator(const pol_t pol) { cart2_t c = pol2cart(pol); cart3_t cart3 = {c.x, c.y, 0}; return cart3; } /// 3D vector addition. static inline cart3_t cart3_add(const cart3_t a, const cart3_t b) { cart3_t res = {a.x+b.x, a.y+b.y, a.z+b.z}; return res; } /// 3D vector substraction. static inline cart3_t cart3_substract(const cart3_t a, const cart3_t b) { cart3_t res = {a.x-b.x, a.y-b.y, a.z-b.z}; return res; } /// 3D vector scaling static inline cart3_t cart3_scale(const double c, const cart3_t v) { cart3_t res = {c * v.x, c * v.y, c * v.z}; return res; } /// 3D vector division by scalar (N.B. argument order). static inline cart3_t cart3_divscale( const cart3_t v, const double c) { cart3_t res = {v.x / c, v.y / c, v.z / c}; return res; } /// Euclidian distance between two 3D points. static inline double cart3_dist(const cart3_t a, const cart3_t b) { return cart3norm(cart3_substract(a,b)); } static inline _Bool cart3_isclose(const cart3_t a, const cart3_t b, double rtol, double atol) { return cart3_dist(a,b) <= atol + rtol * (cart3norm(b) + cart3norm(a)) * .5; } /// Complex 3D vector scaling. static inline ccart3_t ccart3_scale(const _Complex double c, const ccart3_t v) { ccart3_t res = {c * v.x, c * v.y, c * v.z}; return res; } /// Complex 3D vector adition. static inline ccart3_t ccart3_add(const ccart3_t a, const ccart3_t b) { ccart3_t res = {a.x+b.x, a.y+b.y, a.z+b.z}; return res; } /// Complex 3D vector substraction. static inline ccart3_t ccart3_substract(const ccart3_t a, const ccart3_t b) { ccart3_t res = {a.x-b.x, a.y-b.y, a.z-b.z}; return res; } /// Complex 3D cartesian vector "dot product" without conjugation. static inline _Complex double ccart3_dotnc(const ccart3_t a, const ccart3_t b) { return a.x * b.x + a.y * b.y + a.z * b.z; } /// Convert cart3_t to ccart3_t. static inline ccart3_t cart32ccart3(cart3_t c){ ccart3_t res = {c.x, c.y, c.z}; return res; } /// Complex 3D vector (geographic coordinates) addition. static inline csphvec_t csphvec_add(const csphvec_t a, const csphvec_t b) { csphvec_t res = {a.rc + b.rc, a.thetac + b.thetac, a.phic + b.phic}; return res; } /// Complex 3D vector (geographic coordinates) substraction. static inline csphvec_t csphvec_substract(const csphvec_t a, const csphvec_t b) { csphvec_t res = {a.rc - b.rc, a.thetac - b.thetac, a.phic - b.phic}; return res; } /// Complex 3D vector (geographic coordinates) scaling. static inline csphvec_t csphvec_scale(_Complex double c, const csphvec_t v) { csphvec_t res = {c * v.rc, c * v.thetac, c * v.phic}; return res; } /// Complex 3D vector (geographic coordinates) "dot product" without conjugation. static inline _Complex double csphvec_dotnc(const csphvec_t a, const csphvec_t b) { //N.B. no complex conjugation done here return a.rc * b.rc + a.thetac * b.thetac + a.phic * b.phic; } /// Spherical coordinate system scaling. static inline sph_t sph_scale(double c, const sph_t s) { sph_t res = {c * s.r, s.theta, s.phi}; return res; } /// "Complex spherical" coordinate system scaling. static inline csph_t sph_cscale(_Complex double c, const sph_t s) { csph_t res = {c * s.r, s.theta, s.phi}; return res; } /// Coordinate transform of a vector in local geographic to global cartesian system. // equivalent to sph_loccart2cart in qpms_p.py static inline ccart3_t csphvec2ccart(const csphvec_t sphvec, const sph_t at) { const double st = sin(at.theta); const double ct = cos(at.theta); const double sf = sin(at.phi); const double cf = cos(at.phi); const double rx = st * cf; const double ry = st * sf; const double rz = ct; const double tx = ct * cf; const double ty = ct * sf; const double tz = -st; const double fx = -sf; const double fy = cf; const double fz = 0.; ccart3_t res; res.x = rx * sphvec.rc + tx * sphvec.thetac + fx * sphvec.phic; res.y = ry * sphvec.rc + ty * sphvec.thetac + fy * sphvec.phic; res.z = rz * sphvec.rc + tz * sphvec.thetac + fz * sphvec.phic; return res; } /// Coordinate transform of a vector in local geographic to global cartesian system. /** * Same as csphvec2ccart, but with csph_t as second argument. * (The radial part (which is the only complex part of csph_t) * of the second argument does not play role in the * transformation, so this is completely legit */ static inline ccart3_t csphvec2ccart_csph(const csphvec_t sphvec, const csph_t at) { const sph_t atreal = {0 /*not used*/, at.theta, at.phi}; return csphvec2ccart(sphvec, atreal); } /// Coordinate transform of a vector in global cartesian to local geographic system. static inline csphvec_t ccart2csphvec(const ccart3_t cartvec, const sph_t at) { // this chunk is copy-pasted from csphvec2cart, so there should be a better way... const double st = sin(at.theta); const double ct = cos(at.theta); const double sf = sin(at.phi); const double cf = cos(at.phi); const double rx = st * cf; const double ry = st * sf; const double rz = ct; const double tx = ct * cf; const double ty = ct * sf; const double tz = -st; const double fx = -sf; const double fy = cf; const double fz = 0.; csphvec_t res; res.rc = rx * cartvec.x + ry * cartvec.y + rz * cartvec.z; res.thetac = tx * cartvec.x + ty * cartvec.y + tz * cartvec.z; res.phic = fx * cartvec.x + fy * cartvec.y + fz * cartvec.z; return res; } /// Convert sph_t to csph_t. static inline csph_t sph2csph(sph_t s) { csph_t cs = {s.r, s.theta, s.phi}; return cs; } /// Convert csph_t to sph_t, discarding the imaginary part of radial component. static inline sph_t csph2sph(csph_t s) { sph_t rs = {creal(s.r), s.theta, s.phi}; return rs; } /// Lossy coordinate transform of ccart3_t to csph_t. /** The angle and real part of the radial coordinate are determined * from the real components of \a \cart. The imaginary part of the radial * coordinate is then determined as the length of the imaginary * part of \a cart *projected onto* the real part of \a cart. * * N.B. this obviously makes not much sense for purely imaginary vectors * (and will cause NANs). TODO handle this better, as purely imaginary * vectors could make sense e.g. for evanescent waves. */ static inline csph_t ccart2csph(const ccart3_t cart) { cart3_t rcart = {creal(cart.x), creal(cart.y), creal(cart.z)}; cart3_t icart = {cimag(cart.x), cimag(cart.y), cimag(cart.z)}; csph_t sph = sph2csph(cart2sph(rcart)); sph.r += I * cart3_dot(icart,rcart) / cart3norm(rcart); return sph; } /// Real 3D cartesian to spherical (complex r) coordinates conversion. See @ref coord_conversions. static inline csph_t cart2csph(const cart3_t cart) { csph_t sph; const double r = cart3norm(cart); sph.r = r; sph.theta = sph.r ? acos(cart.z / r) : M_PI_2; sph.phi = atan2(cart.y, cart.x); return sph; } /// Coordinate transform of csph_t to ccart3_t static inline ccart3_t csph2ccart(const csph_t sph) { ccart3_t cart; double sin_th = #ifdef QPMS_VECTORS_NICE_TRANSFORMATIONS (sph.theta == M_PI) ? 0 : #endif sin(sph.theta); cart.x = sph.r * sin_th * cos(sph.phi); cart.y = sph.r * sin_th * sin(sph.phi); cart.z = sph.r * cos(sph.theta); return cart; } void print_csphvec(csphvec_t); void print_ccart3(ccart3_t); void print_cart3(cart3_t); void print_sph(sph_t); // kahan sums for various types... TODO make generic code using macros /// Kanan sum initialisation for ccart3_t. static inline void ccart3_kahaninit(ccart3_t *sum, ccart3_t *compensation) { sum->x = sum->y = sum->z = compensation->x = compensation->y = compensation->z = 0; } /// Kanan sum initialisation for csphvec_t. static inline void csphvec_kahaninit(csphvec_t *sum, csphvec_t *compensation) { sum->rc = sum->thetac = sum->phic = compensation->rc = compensation->thetac = compensation->phic = 0; } /// Add element to Kahan sum (ccart3_t). static inline void ccart3_kahanadd(ccart3_t *sum, ccart3_t *compensation, const ccart3_t input) { ccart3_t comped_input = ccart3_substract(input, *compensation); ccart3_t nsum = ccart3_add(*sum, comped_input); *compensation = ccart3_substract(ccart3_substract(nsum, *sum), comped_input); *sum = nsum; } /// Add element to Kahan sum (csphvec_t). static inline void csphvec_kahanadd(csphvec_t *sum, csphvec_t *compensation, const csphvec_t input) { csphvec_t comped_input = csphvec_substract(input, *compensation); csphvec_t nsum = csphvec_add(*sum, comped_input); *compensation = csphvec_substract(csphvec_substract(nsum, *sum), comped_input); *sum = nsum; } /// Euclidian norm of a vector in geographic coordinates. static inline double csphvec_norm(const csphvec_t a) { return sqrt(creal(a.rc * conj(a.rc) + a.thetac * conj(a.thetac) + a.phic * conj(a.phic))); } static inline double csphvec_reldiff_abstol(const csphvec_t a, const csphvec_t b, double tolerance) { double anorm = csphvec_norm(a); double bnorm = csphvec_norm(b); if (anorm <= tolerance && bnorm <= tolerance) return 0; return csphvec_norm(csphvec_substract(a,b)) / (anorm + bnorm); } static inline double csphvec_reldiff(const csphvec_t a, const csphvec_t b) { return csphvec_reldiff_abstol(a, b, 0); } /*! \page coord_conversions Coordinate systems and default conversions * * The coordinate system transformations are defined as following: * * \section coordtf_same_d Equal-dimension coordinate tranforms * \subsection sph_cart3 Spherical and 3D cartesian coordinates * * \f$ x = r \sin \theta \cos \phi \f$, * * \f$ y = r \sin \theta \sin \phi \f$, * * \f$ z = r \cos \theta \f$. * \subsection pol_cart2 Polar and 2D cartesian coordinates * * \f$ x = r \cos \phi \f$, * * \f$ y = r \sin \phi \f$. * * \section coordtf_123 Lower to higher dimension conversions. * * The 1D coordinate is identified with the \a z 3D cartesian coordinate. * * The 2D cartesian coordinates \a x, \a y are identified with the \a x, \a y * 3D cartesian coordinates. * * For the sake of consistency, default conversion between * 1D and 2D coordinates is not allowed and yields NAN values. * * \section coordtf_321 Higher to lower dimension conversions. * Default conversions from higher to lower-dimensional coordinate * systems are not allowed. Any projections have to be done explicitly. */ /// Conversion from anycoord_point_t to explicitly spherical coordinates. /** See @ref coord_conversions for the conversion definitions. */ static inline sph_t anycoord2sph(anycoord_point_t p, qpms_coord_system_t t) { switch(t & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: return p.sph; break; case QPMS_COORDS_POL: return pol2sph_equator(p.pol); break; case QPMS_COORDS_CART3: return cart2sph(p.cart3); break; case QPMS_COORDS_CART2: return cart22sph(p.cart2); break; case QPMS_COORDS_CART1: return cart12sph_zaxis(p.z); break; } QPMS_WTF; } /// Conversion from anycoord_point_t to explicitly 3D cartesian coordinates. /** See @ref coord_conversions for the conversion definitions. */ static inline cart3_t anycoord2cart3(anycoord_point_t p, qpms_coord_system_t t) { switch(t & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: return sph2cart(p.sph); break; case QPMS_COORDS_POL: return pol2cart3_equator(p.pol); break; case QPMS_COORDS_CART3: return p.cart3; break; case QPMS_COORDS_CART2: return cart22cart3xy(p.cart2); break; case QPMS_COORDS_CART1: return cart12cart3z(p.z); break; } QPMS_WTF; } /// Cartesian norm of anycoord_point_t. // The implementation is simple and stupid, do not use for heavy computations. static inline double anycoord_norm(anycoord_point_t p, qpms_coord_system_t t) { return cart3norm(anycoord2cart3(p, t)); } #if 0 // Convenience identifiers for return values. static const cart3_t CART3_INVALID = {NAN, NAN, NAN}; static const cart2_t CART2_INVALID = {NAN, NAN}; static const double CART1_INVALID = NAN; static const sph_t SPH_INVALID = {NAN, NAN, NAN}; static const pol_t POL_INVALID = {NAN, NAN}; #endif /// Conversion from anycoord_point_t to explicitly polar coordinates. /** See @ref coord_conversions for the conversion definitions. */ static inline pol_t anycoord2pol(anycoord_point_t p, qpms_coord_system_t t) { switch(t & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: case QPMS_COORDS_CART3: QPMS_PR_ERROR("Implicit conversion from 3D to 2D" " coordinates not allowed"); break; case QPMS_COORDS_POL: return p.pol; break; case QPMS_COORDS_CART2: return cart2pol(p.cart2); break; case QPMS_COORDS_CART1: QPMS_PR_ERROR("Implicit conversion from 1D to 2D" " coordinates not allowed"); break; } QPMS_WTF; } /// Conversion from anycoord_point_t to explicitly 2D cartesian coordinates. /** See @ref coord_conversions for the conversion definitions. */ static inline cart2_t anycoord2cart2(anycoord_point_t p, qpms_coord_system_t t) { switch(t & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: case QPMS_COORDS_CART3: QPMS_PR_ERROR("Implicit conversion from 3D to 2D" " coordinates not allowed"); break; case QPMS_COORDS_POL: return pol2cart(p.pol); break; case QPMS_COORDS_CART2: return p.cart2; break; case QPMS_COORDS_CART1: QPMS_PR_ERROR("Implicit conversion from 1D to 2D" " coordinates not allowed"); break; } QPMS_WTF; } /// Conversion from anycoord_point_t to explicitly 1D cartesian coordinates. /** See @ref coord_conversions for the conversion definitions. */ static inline double anycoord2cart1(anycoord_point_t p, qpms_coord_system_t t) { if ((t & QPMS_COORDS_BITRANGE) == QPMS_COORDS_CART1) return p.z; else QPMS_PR_ERROR("Implicit conversion from nD (n > 1)" " to 1D not allowed."); } /// Coordinate conversion of point arrays (something to something). /** The dest and src arrays must not overlap */ static inline void qpms_array_coord_transform(void *dest, qpms_coord_system_t tdest, const void *src, qpms_coord_system_t tsrc, size_t nmemb) { switch(tdest & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: { sph_t *d = (sph_t *) dest; switch (tsrc & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: { const sph_t *s = (const sph_t*) src; for(size_t i = 0; i < nmemb; ++i) d[i] = s[i]; return; } break; case QPMS_COORDS_CART3: { const cart3_t *s = (const cart3_t*) src; for(size_t i = 0; i < nmemb; ++i) d[i] = cart2sph(s[i]); return; } break; case QPMS_COORDS_POL: { const pol_t *s = (const pol_t*) src; for(size_t i = 0; i < nmemb; ++i) d[i] = pol2sph_equator(s[i]); return; } break; case QPMS_COORDS_CART2: { const cart2_t *s = (const cart2_t*) src; for(size_t i = 0; i < nmemb; ++i) d[i] = cart22sph(s[i]); return; } break; case QPMS_COORDS_CART1: { const double *s = (const double *) src; for(size_t i = 0; i < nmemb; ++i) d[i] = cart12sph_zaxis(s[i]); return; } break; } QPMS_WTF; } break; case QPMS_COORDS_CART3: { cart3_t *d = (cart3_t *) dest; switch (tsrc & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: { const sph_t *s = (const sph_t *) src; for(size_t i = 0; i < nmemb; ++i) d[i] = sph2cart(s[i]); return; } break; case QPMS_COORDS_CART3: { const cart3_t *s = (const cart3_t *) src; for(size_t i = 0; i < nmemb; ++i) d[i] = s[i]; return; } break; case QPMS_COORDS_POL: { const pol_t *s = (const pol_t *) src; for(size_t i = 0; i < nmemb; ++i) d[i] = pol2cart3_equator(s[i]); return; } break; case QPMS_COORDS_CART2: { const cart2_t *s = (const cart2_t *) src; for(size_t i = 0; i < nmemb; ++i) d[i] = cart22cart3xy(s[i]); return; } break; case QPMS_COORDS_CART1: { const double *s = (const double *)src; for(size_t i = 0; i < nmemb; ++i) d[i] = cart12cart3z(s[i]); return; } break; } QPMS_WTF; } break; case QPMS_COORDS_POL: { pol_t *d = (pol_t *) dest; switch (tsrc & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: case QPMS_COORDS_CART3: QPMS_PR_ERROR("Implicit conversion from 3D to 2D coordinates not allowed"); break; case QPMS_COORDS_POL: { const pol_t *s = (const pol_t *) src; for(size_t i = 0; i < nmemb; ++i) d[i] = s[i]; return; } break; case QPMS_COORDS_CART2: { const cart2_t *s = (const cart2_t *) src; for(size_t i = 0; i < nmemb; ++i) d[i] = cart2pol(s[i]); return; } break; case QPMS_COORDS_CART1: QPMS_PR_ERROR("Implicit conversion from 3D to 1D coordinates not allowed"); break; } QPMS_WTF; } break; case QPMS_COORDS_CART2: { cart2_t *d = (cart2_t *) dest; switch (tsrc & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: case QPMS_COORDS_CART3: QPMS_PR_ERROR("Implicit conversion from 3D to 2D coordinates not allowed"); break; case QPMS_COORDS_POL: { const pol_t *s = (const pol_t *) src; for(size_t i = 0; i < nmemb; ++i) d[i] = pol2cart(s[i]); return; } break; case QPMS_COORDS_CART2: { const cart2_t *s = (const cart2_t *) src; for(size_t i = 0; i < nmemb; ++i) d[i] = s[i]; return; } break; case QPMS_COORDS_CART1: QPMS_PR_ERROR("Implicit conversion from 3D to 1D coordinates not allowed"); break; } QPMS_WTF; } break; case QPMS_COORDS_CART1: { double *d = (double *) dest; switch (tsrc & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: case QPMS_COORDS_CART3: QPMS_PR_ERROR("Implicit conversion from 3D to 2D coordinates not allowed"); break; case QPMS_COORDS_POL: case QPMS_COORDS_CART2: QPMS_PR_ERROR("Implicit conversion from 3D to 1D coordinates not allowed"); break; case QPMS_COORDS_CART1: { const double *s = (const double *) src; for(size_t i = 0; i < nmemb; ++i) d[i] = s[i]; return; } break; } QPMS_WTF; } break; } QPMS_WTF; } /// Coordinate conversion of point arrays (anycoord_point_t to something). /** The dest and src arrays must not overlap */ static inline void anycoord_arr2something(void *dest, qpms_coord_system_t tdest, const anycoord_point_t *src, qpms_coord_system_t tsrc, size_t nmemb) { if(nmemb) { switch(tdest & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: { sph_t *d = (sph_t *) dest; switch (tsrc & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: for(size_t i = 0; i < nmemb; ++i) d[i] = src[i].sph; return; break; case QPMS_COORDS_CART3: for(size_t i = 0; i < nmemb; ++i) d[i] = cart2sph(src[i].cart3); return; break; case QPMS_COORDS_POL: for(size_t i = 0; i < nmemb; ++i) d[i] = pol2sph_equator(src[i].pol); return; break; case QPMS_COORDS_CART2: for(size_t i = 0; i < nmemb; ++i) d[i] = cart22sph(src[i].cart2); return; break; case QPMS_COORDS_CART1: for(size_t i = 0; i < nmemb; ++i) d[i] = cart12sph_zaxis(src[i].z); return; break; } QPMS_WTF; } break; case QPMS_COORDS_CART3: { cart3_t *d = (cart3_t *) dest; switch (tsrc & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: for(size_t i = 0; i < nmemb; ++i) d[i] = sph2cart(src[i].sph); return; break; case QPMS_COORDS_CART3: for(size_t i = 0; i < nmemb; ++i) d[i] = src[i].cart3; return; break; case QPMS_COORDS_POL: for(size_t i = 0; i < nmemb; ++i) d[i] = pol2cart3_equator(src[i].pol); return; break; case QPMS_COORDS_CART2: for(size_t i = 0; i < nmemb; ++i) d[i] = cart22cart3xy(src[i].cart2); return; break; case QPMS_COORDS_CART1: for(size_t i = 0; i < nmemb; ++i) d[i] = cart12cart3z(src[i].z); return; break; } QPMS_WTF; } break; case QPMS_COORDS_POL: { pol_t *d = (pol_t *) dest; switch (tsrc & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: case QPMS_COORDS_CART3: QPMS_PR_ERROR("Implicit conversion from 3D to 2D coordinates not allowed"); break; case QPMS_COORDS_POL: for(size_t i = 0; i < nmemb; ++i) d[i] = src[i].pol; return; break; case QPMS_COORDS_CART2: for(size_t i = 0; i < nmemb; ++i) d[i] = cart2pol(src[i].cart2); return; break; case QPMS_COORDS_CART1: QPMS_PR_ERROR("Implicit conversion from 3D to 1D coordinates not allowed"); break; } QPMS_WTF; } break; case QPMS_COORDS_CART2: { cart2_t *d = (cart2_t *) dest; switch (tsrc & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: case QPMS_COORDS_CART3: QPMS_PR_ERROR("Implicit conversion from 3D to 2D coordinates not allowed"); break; case QPMS_COORDS_POL: for(size_t i = 0; i < nmemb; ++i) d[i] = pol2cart(src[i].pol); return; break; case QPMS_COORDS_CART2: for(size_t i = 0; i < nmemb; ++i) d[i] = src[i].cart2; return; break; case QPMS_COORDS_CART1: QPMS_PR_ERROR("Implicit conversion from 3D to 1D coordinates not allowed"); break; } QPMS_WTF; } break; case QPMS_COORDS_CART1: { double *d = (double *) dest; switch (tsrc & QPMS_COORDS_BITRANGE) { case QPMS_COORDS_SPH: case QPMS_COORDS_CART3: QPMS_PR_ERROR("Implicit conversion from 3D to 2D coordinates not allowed"); break; case QPMS_COORDS_POL: case QPMS_COORDS_CART2: QPMS_PR_ERROR("Implicit conversion from 3D to 1D coordinates not allowed"); break; case QPMS_COORDS_CART1: for(size_t i = 0; i < nmemb; ++i) d[i] = src[i].z; return; break; } QPMS_WTF; } break; } QPMS_WTF; } } /// Converts cart3_t to array of doubles. static inline void cart3_to_double_array(double a[], cart3_t b) { a[0] = b.x; a[1] = b.y; a[2] = b.z; } /// Converts array of doubles to cart3_t. static inline cart3_t cart3_from_double_array(const double a[]) { cart3_t b = {.x = a[0], .y = a[1], .z = a[1]}; return b; } /// Converts cart2_t to array of doubles. static inline void cart2_to_double_array(double a[], cart2_t b) { a[0] = b.x; a[1] = b.y; } /// Converts array of doubles to cart2_t static inline cart2_t cart2_from_double_array(const double a[]) { cart2_t b = {.x = a[0], .y = a[1]}; return b; } typedef double matrix3d[3][3]; typedef double matrix2d[2][2]; typedef _Complex double cmatrix3d[3][3]; typedef _Complex double cmatrix2d[2][2]; #endif //VECTORS_H