Skip to content
Snippets Groups Projects
pose.hpp 25.94 KiB
#pragma once
#ifndef POSE_HPP
#define POSE_HPP 1
#define NUMCPP_NO_USE_BOOST ON
#include <iostream>
#include <numeric>
#include <iostream>
#include <algorithm> // For std::min_element, std::max_element
#include "NumCpp.hpp"
#ifdef _MSC_VER
#include <valarray>
#endif

namespace QOSM::Pose{
    using MatrixX3d = nc::NdArray<double>;
    
    /**
     * @class VectorNx3
     * @brief Represents a collection of N 3D vectors stored as a matrix with N rows and 3 columns.
     *
     * This class extends MatrixX3d and provides various constructors for initializing vectors,
     * as well as operations for manipulating and processing sets of 3D vectors.
     * It supports element-wise arithmetic, row and column extraction, dot and cross products,
     * normalization, and transformations.
     *
     * @note Each row represents a distinct 3D vector, which can be a position vector, unit vector, or Poynting vector.
     */
    class VectorNx3 : public MatrixX3d {
        protected:
            /// Number of points (number of rows)
            size_type N;
        
        public:
            /**
             * @brief Default constructor, initializes a 1x3 vector.
             */
            VectorNx3();
        
            /**
             * @brief Constructs a vector with N rows and 3 columns, initializing all elements to a specific value.
             * @param _N Number of rows (vectors).
             * @param value Initial value for all components (default: 0).
             */
            VectorNx3(const size_type _N, double value = 0.0);
        
            /**
             * @brief Constructs a vector from an existing matrix of size Nx3.
             * @param data Matrix of values.
             */
            VectorNx3(const MatrixX3d data);
        
            /**
             * @brief Constructs a vector from a pointer to an array of doubles.
             * @param ptr Pointer to data.
             * @param size Total number of elements (must be a multiple of 3).
             */
            VectorNx3(const double* ptr, uint32_t size);
        
            /**
             * @brief Returns the number of vectors (rows).
             * @return Number of vectors.
             */
            inline nc::uint32 rows() const;
        
            /**
             * @brief Retrieves a column at a specific index.
             * @param idx Index of the column.
             * @return Column as an Nx1 array.
             */
            inline auto col(nc::uint32 idx);
        
            /**
             * @brief Retrieves a row at a specific index.
             * @param idx Index of the row.
             * @return Row as a 1x3 array.
             */
            inline auto row(nc::uint32 idx);
        
            /**
             * @brief Extracts a column and returns it as a separate VectorNx3 instance.
             * @param idx Index of the column.
             * @return New VectorNx3 containing the column.
             */
            inline auto vcol(nc::uint32 idx);
        
            /**
             * @brief Extracts a row and returns it as a separate VectorNx3 instance.
             * @param idx Index of the row.
             * @param N Number of repetitions (default: 1).
             * @return New VectorNx3 containing the repeated row.
             */
            inline auto vrow(nc::uint32 idx, nc::uint32 N = 1);
        
            /**
             * @brief Repeats a row N times.
             * @param idx Index of the row.
             * @return New instance with the row repeated.
             */
            inline auto rowN(nc::uint32 idx);
        
            /**
             * @brief Inserts a column vector at a specified index.
             * @param idx Column index.
             * @param data Column data (Nx1 matrix).
             */
            inline void putCol(nc::uint32 idx, MatrixX3d &data);
        
            /**
             * @brief Inserts a row vector at a specified index.
             * @param idx Row index.
             * @param data Row data (1x3 matrix).
             */
            inline void putRow(nc::uint32 idx, MatrixX3d &data);
        
            /**
             * @brief Creates a VectorNx3 from an existing MatrixX3d.
             * @param data Matrix of values.
             * @return New VectorNx3 instance.
             */
            static VectorNx3 CreateVectorNx3(const MatrixX3d data);
        
            /**
             * @brief Computes the norm of each row.
             * @return Nx1 matrix containing norms.
             */
            inline MatrixX3d norm();
        
            /**
             * @brief Computes the squared norm of each row.
             * @return Nx1 matrix containing squared norms.
             */
            inline MatrixX3d norm2();
        
            /**
             * @brief Normalizes each row in place.
             * @return Nx1 matrix of normalization factors.
             */
            nc::NdArray<double> normalise();
        
            /**
             * @brief Returns a normalized copy of the vector.
             * @return New VectorNx3 instance with normalized rows.
             */
            VectorNx3 normalised() const;
        
            /**
             * @brief Computes the dot product of each row with another VectorNx3.
             * @warning Both vectors must have the same number of rows.
             * @return Nx1 matrix of dot product values.
             */
            inline MatrixX3d dot(const VectorNx3 &b);
        
            /**
             * @brief Computes the cross product of each row with another VectorNx3.
             * @warning Both vectors must have the same number of rows.
             * @return New VectorNx3 containing cross product results.
             */
            VectorNx3 cross(const VectorNx3 &v2);
        
            /**
             * @brief Flips the direction of all vectors.
             * @return New VectorNx3 with reversed directions.
             */
            VectorNx3 flip();
        
            /** @brief Scalar multiplication. */
            friend VectorNx3 operator*(VectorNx3 lhs, const double &rhs);
            VectorNx3 &operator*=(const double &rhs);
        
            /** @brief Scalar division. */
            friend VectorNx3 operator/(VectorNx3 lhs, const double &rhs);
            VectorNx3 &operator/=(const double &rhs);
        
            /** @brief Scalar addition. */
            friend VectorNx3 operator+(VectorNx3 lhs, const double &rhs);
            VectorNx3 &operator+=(const double &rhs);
        
            /** @brief Scalar subtraction. */
            friend VectorNx3 operator-(VectorNx3 lhs, const double &rhs);
            VectorNx3 &operator-=(const double &rhs);
        };

    #ifndef _MSC_VER
    typedef double v4df __attribute__ ((vector_size (32)));
    #else
    using v4df = std::valarray<double>;
    #endif

    
    /**
     * @class Vec3
     * @brief Represents a 3D vector with various arithmetic and utility operations.
     * It can represent a position vector, a unit vector or a Poyinting vector.
     *
     * This class supports initialization from different data structures, arithmetic operations,
     * and vector operations like dot product, cross product, and normalization.
     */
    class Vec3 {
        protected:
            v4df data = {0., 0., 0., 0.};
        
        public:
            /**
             * @brief Default constructor initializing the vector to (0,0,0).
             */
            Vec3(double x=0., double y=0., double z=0.);
            
            /**
             * @brief Constructor initializing from an nc::NdArray.
             * @param inVector Input array.
             * @param idx Row index to extract the vector from.
             */
            Vec3(const nc::NdArray<double> &inVector, const std::size_t idx);
        
            /**
             * @brief Constructor initializing from a VectorNx3.
             * @param inVector Input matrix.
             * @param idx Row index to extract the vector from.
             */
            Vec3(const VectorNx3 &inVector, const std::size_t idx);
        
            /**
             * @brief Constructor initializing from a 3-element std::array.
             * @param v Input array.
             */
            Vec3(const std::array<double, 3> &v);
        
            /**
             * @brief Constructor initializing from a std::vector.
             * @param vec Input vector (must have at least 3 elements).
             * @throws std::runtime_error if the vector has less than 3 elements.
             */
            Vec3(const std::vector<double>& vec);
        
            /**
             * @brief Constructor initializing from an initializer list.
             * @param values List of 3 values.
             * @throws std::runtime_error if the list has less than 3 elements.
             */
            Vec3(std::initializer_list<double> values);
        
            /**
             * @brief Assigns values using an initializer list.
             * @param values List of 3 values.
             * @throws std::runtime_error if the list has less than 3 elements.
             */
            void assign(std::initializer_list<double> values);
        
            /**
             * @brief Computes the dot product with another vector.
             * @param rhl The other vector.
             * @return The dot product value.
             */
            inline double dot(const Vec3 &rhl) const;
        
            /**
             * @brief Computes the squared norm of the vector.
             * @return The squared norm value.
             */
            inline double norm2() const;
        
            /**
             * @brief Computes the norm (magnitude) of the vector.
             * @return The norm value.
             */
            inline double norm() const;
        
            /**
             * @brief Normalizes the vector in place.
             * @return The original norm before normalization.
             */
            inline double normalise();
        
            /**
             * @brief Returns a normalized copy of the vector.
             * @return A normalized vector.
             */
            inline Vec3 normalised() const;
        
            /**
             * @brief Computes the cross product with another vector.
             * @param rhl The other vector.
             * @return The resulting cross product vector.
             */
            Vec3 cross(const Vec3 &rhl) const;
        
            /**
             * @brief Overloaded arithmetic operators.
             */
            inline Vec3& operator+=(const Vec3& rhl);
            inline Vec3& operator-=(const Vec3& rhl);
            inline Vec3& operator*=(const Vec3& rhl);
            inline Vec3& operator/=(const Vec3& rhl);
            inline Vec3& operator+=(const double& rhl);
            inline Vec3& operator-=(const double& rhl);
            inline Vec3& operator*=(const double& rhl);
            inline Vec3& operator/=(const double& rhl);
            inline double &operator[](short dim);
            inline double operator[](short dim) const;
        
            /**
             * @brief Computes the minimum component of the vector.
             * @return The minimum value.
             */
            double min() const;
        
            /**
             * @brief Computes the maximum component of the vector.
             * @return The maximum value.
             */
            double max() const;
        
            /**
             * @brief Computes the minimum/maximum between two vectors.
             * @return A vector containing the element-wise min/max.
             */
            Vec3 minimum(const Vec3& other) const;
            Vec3 maximum(const Vec3& other) const;
        
            /**
             * @brief Finds the index of the minimum/maximum element.
             * @return Index of the minimum/maximum value.
             */
            int argmin() const;
            int argmax() const;
        
            /**
             * @brief Converts the vector to a VectorNx3 representation.
             * @param N Number of rows.
             * @return A VectorNx3 with repeated rows.
             */
            VectorNx3 toNd(const std::size_t N) const;
        
            /**
             * @brief Accessors for individual components.
             */
            double& getx();
            double& gety();
            double& getz();
        
            /**
             * @brief Mutators for individual components.
             */
            void setx(double &val);
            void sety(double &val);
            void setz(double &val);
        
            /**
             * @brief Creates a copy of the vector.
             * @return A new Vec3 instance with the same values.
             */
            Vec3 copy() const;
        
            /**
             * @brief Overloaded stream output operator.
             */
            friend std::ostream& operator<<(std::ostream& os, const Vec3& o);
        
            /**
             * @brief Converts the vector to a string representation.
             * @return A formatted string representing the vector.
             */
            std::string __str__() const;
        };
        

        /**
     * @class Quaternion
     * @brief Represents a quaternion, using the European convention (w, x, y, z).
     *
     * Quaternions are used for representing rotations in 3D space and provide
     * an efficient way to handle rotations without suffering from gimbal lock.
     * This class supports various constructors, arithmetic operations, and
     * rotation transformations.
     *
     * @note The class uses a four-element vector (v4df) to store the quaternion components.
     */
    class Quaternion {
        protected:
            v4df data = {0., 0., 0., 0.};
        
        public:
            /**
             * @brief Default constructor, initializes a quaternion.
             * @param qw Scalar (real) component, default is 1.
             * @param qx X-component, default is 0.
             * @param qy Y-component, default is 0.
             * @param qz Z-component, default is 0.
             */
            Quaternion(const double& qw = 1., const double& qx = 0., const double& qy = 0., const double& qz = 0.);
        
            /**
             * @brief Constructs a quaternion from a scalar and a 3D vector.
             * @param w Scalar component.
             * @param v 3D vector representing the imaginary part.
             */
            Quaternion(const double& w, const Vec3& v);
        
            /**
             * @brief Constructs a quaternion from an axis-angle representation.
             * @param axis Rotation axis (must be normalized).
             * @param angle Rotation angle in radians (or degrees if deg=true).
             * @param deg Whether the angle is given in degrees (default: false).
             */
            Quaternion(const Vec3& axis, const double& angle, const bool& deg = false);
        
            /**
             * @brief Constructs a quaternion from a rotation vector.
             * @param rotVec A vector representing the axis and angle of rotation.
             * @param deg Whether the rotation vector is in degrees (default: false).
             */
            Quaternion(const Vec3& rotVec, const bool& deg = false);
        
            /**
             * @brief Constructs a quaternion from a 4-element array.
             * @param q An array of 4 double values.
             */
            Quaternion(const std::array<double, 4>& q);
        
            /**
             * @brief Constructs a quaternion from a vector of at least 4 elements.
             * @param q A std::vector of doubles (must have at least 4 elements).
             * @throws std::runtime_error if the vector size is < 4.
             */
            Quaternion(const std::vector<double>& q);
        
            /**
             * @brief Constructs a quaternion from an initializer list.
             * @param values A list of 4 double values.
             * @throws std::runtime_error if the list size is < 4.
             */
            Quaternion(std::initializer_list<double> values);
        
            /**
             * @brief Assigns new values to the quaternion from an initializer list.
             * @param values A list of 4 double values.
             * @throws std::runtime_error if the list size is < 4.
             */
            void assign(std::initializer_list<double> values);
        
            /**
             * @brief Returns the vector (imaginary) part of the quaternion.
             * @return Vec3 The imaginary part (x, y, z).
             */
            inline Vec3 toVec3() const;
        
            /** @brief Accessor for the scalar (real) part of the quaternion. */
            double& getw();
            /** @brief Accessor for the x-component of the quaternion. */
            double& getx();
            /** @brief Accessor for the y-component of the quaternion. */
            double& gety();
            /** @brief Accessor for the z-component of the quaternion. */
            double& getz();
        
            /**
             * @brief Computes the conjugate of the quaternion.
             * @return Quaternion with the same real part but negated imaginary parts.
             */
            inline Quaternion conj() const;
        
            /**
             * @brief Computes the dot product with another quaternion.
             * @param rhl The other quaternion.
             * @return Dot product value.
             */
            inline double dot(const Quaternion& rhl) const;
        
            /**
             * @brief Computes the squared norm of the quaternion.
             * @return The squared norm.
             */
            inline double norm2() const;
        
            /**
             * @brief Computes the norm (magnitude) of the quaternion.
             * @return The norm of the quaternion.
             */
            inline double norm() const;
        
            /**
             * @brief Normalizes the quaternion in place.
             */
            inline void normalise();
        
            /**
             * @brief Returns a normalized copy of the quaternion.
             * @return A normalized quaternion.
             */
            inline Quaternion normalised() const;
        
            /**
             * @brief Rotates a 3D vector using the quaternion.
             * @param v The vector to rotate.
             * @return The rotated vector.
             */
            inline Vec3 rotate(const Vec3& v) const;
        
            /**
             * @brief Overloaded rotation method for modifying the input vector.
             */
            inline Vec3 rotate(Vec3& v) const;
        
            /**
             * @brief Rotates a set of 3D vectors.
             * @param v The vectors to rotate.
             * @return The rotated vectors.
             */
            inline VectorNx3 rotate(const VectorNx3& v) const;
        
            /**
             * @brief Overloaded rotation method for modifying a set of vectors.
             */
            inline VectorNx3 rotate(VectorNx3& v) const;
        };
        
    
    /**
     * @struct Pose
     * @brief Represents a 3D pose consisting of position and orientation.
     *
     * The Pose structure combines a position vector (`ref_r_ref_ori`) and an 
     * orientation quaternion (`q_ref_ori`). It provides multiple constructors 
     * for initialization using initializer lists, arrays, or explicit parameters.
     */
    struct Pose {
        Vec3 ref_r_ref_ori; ///< Position vector (x, y, z) in reference frame.
        Quaternion q_ref_ori; ///< Orientation as a quaternion.

        /**
         * @brief Constructs a Pose from an initializer list.
         * @param values A list of 7 double values (3 for position, 4 for quaternion).
         * @throws std::runtime_error if the list size is not 7.
         */
        Pose(const std::initializer_list<double>& values) {
            if (values.size() != 7)
                throw std::runtime_error("Invalid list size (must be 7)");
            auto it = values.begin();
            ref_r_ref_ori = {*it++, *it++, *it++};
            q_ref_ori = {*it++, *it++, *it++, *it++};
        }

        /**
         * @brief Constructs a Pose from a 7-element array.
         * @param values A std::array of 7 doubles (3 for position, 4 for quaternion).
         */
        Pose(const std::array<double, 7>& values) {
            auto it = values.begin();
            ref_r_ref_ori = {*it++, *it++, *it++};
            q_ref_ori = {*it++, *it++, *it++, *it++};
        }

        /**
         * @brief Constructs a Pose from explicit position and quaternion values.
         * @param v The position vector (default: zero vector).
         * @param q The orientation quaternion (default: identity quaternion).
         */
        Pose(Vec3 v = Vec3(), Quaternion q = Quaternion()) 
            : ref_r_ref_ori(v), q_ref_ori(q) {}
    };


    /**
     * Write the pose of b (written in frame ref) wrt the frame a
     * @param pose_ref_a Pose of a wrt frame ref
     * @param pose_ref_b Pose of b wrt frame ref
     * @return pose_a_b Pose of b wrt frale a
    */
    Pose changeFrame(const Pose &pose_ref_a, const Pose &pose_ref_b);
    
    /**
     * @brief
     * Grid of point on a planar surface
     */
    class MeshGrid{
        private:
            /// @brief Scale to apply on the points coordinates
            double scale;
        public:
            /// @brief Matrices of points (meshgrid)
            MatrixX3d U, V;

            /// @brief number of point along u and v axis
            long nu, nv;

            /**
             * Create a Meshgrid 
             * @param u Meshgrid for u axis
             * @param v Meshgrid for v axis
             * @param scale Scale to apply to the grid
             */
            MeshGrid(MatrixX3d u, MatrixX3d v, const double scale=1.) : scale(scale){
                nu = u.size();
                nv = v.size();
                auto grid = nc::meshgrid(u, v);
                U = grid.first * scale;
                V = grid.second * scale;
            }

            /**
             * Get the number of points in the grid
             * @return the number of points as an integer
             */
            inline auto size(){
                return nu * nv;
            }

            /**
             * Get an 1-D vector of the meshgrid U
             * @return the flatten vector of the mesgrid U
             */
            MatrixX3d flattenU(){
                return U.reshape(nu*nv, 1);
            }

            /**
             * Get an 1-D vector of the meshgrid V
             * @return the flatten vector of the mesgrid V
             */
            MatrixX3d flattenV(){
                return V.reshape(nu*nv, 1);
            }
            
            /**
             * Return a Vector of points for an XY plane located at z
             * @param z position along the z-axis of the XY plane
             * @return a Vector of points lying in the XY plane
             */
            VectorNx3 xyPlane(double z=0.){
                VectorNx3 r_pts(nu*nv);
                auto rows = nc::Slice(0, nu*nv);
                r_pts.put(rows, 0, U.reshape(nu*nv, 1));
                r_pts.put(rows, 1, V.reshape(nu*nv, 1));
                r_pts.put(rows, 2, z * scale);
                return r_pts;
            }
            
            /**
             * Return a Vector of points for an XZ plane located at y
             * @param y position along the y-axis of the XY plane
             * @return a Vector of points lying in the XZ plane
             */
            VectorNx3 xzPlane(double y=0.){
                VectorNx3 r_pts(nu*nv);
                auto rows = nc::Slice(0, nu*nv);
                r_pts.put(rows, 0, U.reshape(nu*nv, 1));
                r_pts.put(rows, 1, y * scale);
                r_pts.put(rows, 2, V.reshape(nu*nv, 1));
                return r_pts;
            }
            
            /**
             * Return a Vector of points for an YZ plane located at x
             * @param x position along the x-axis of the XY plane
             * @return a Vector of points lying in the YZ plane
             */
            VectorNx3 yzPlane(double x=0.){
                VectorNx3 r_pts(nu*nv);
                auto rows = nc::Slice(0, nu*nv);
                r_pts.put(rows, 0, x * scale);
                r_pts.put(rows, 1, U.reshape(nu*nv, 1));
                r_pts.put(rows, 2, V.reshape(nu*nv, 1));
                return r_pts;
            }
            
            /**
             * Return a Vector of points for an YX plane located at z
             * @param z position along the z-axis of the YX plane
             * @return a Vector of points lying in the YX plane
             */
            VectorNx3 yxPlane(double z=0.){
                VectorNx3 r_pts(nu*nv);
                auto rows = nc::Slice(0, nu*nv);
                r_pts.put(rows, 0, V.reshape(nu*nv, 1));
                r_pts.put(rows, 1, U.reshape(nu*nv, 1));
                r_pts.put(rows, 2, z * scale);
                return r_pts;
            }
            
            /**
             * Return a Vector of points for an ZY plane located at x
             * @param x position along the z-axis of the XY plane
             * @return a Vector of points lying in the ZY plane
             */
            VectorNx3 zyPlane(double x=0.){
                VectorNx3 r_pts(nu*nv);
                auto rows = nc::Slice(0, nu*nv);
                r_pts.put(rows, 0, x * scale);
                r_pts.put(rows, 1, V.reshape(nu*nv, 1));
                r_pts.put(rows, 2, U.reshape(nu*nv, 1));
                return r_pts;
            }
            
            /**
             * Return a Vector of points for an ZX plane located at z
             * @param y position along the z-axis of the ZX plane
             * @return a Vector of points lying in the ZX plane
             */
            VectorNx3 zxPlane(double y=0.){
                VectorNx3 r_pts(nu*nv);
                auto rows = nc::Slice(0, nu*nv);
                r_pts.put(rows, 0, V.reshape(nu*nv, 1));
                r_pts.put(rows, 1, y * scale);
                r_pts.put(rows, 2, U.reshape(nu*nv, 1));
                return r_pts;
            }
            
    };
}
#endif