line.h

Back to Line

pastel/geometry/shape/

// Description: Line

#ifndef PASTELGEOMETRY_LINE_H
#define PASTELGEOMETRY_LINE_H

#include "pastel/sys/vector.h"
#include "pastel/sys/tuple.h"

#include <boost/operators.hpp>

namespace Pastel
{

    //! A straight line.
    /*!
   For performance reasons it must be required that
   the line direction always be of unit length.
   This is not enforced by the system but is required
   by the algorithms.
   */
    template <typename Real, int N = Dynamic>
    class Line
        : boost::multipliable<Line<Real, N>, Real
        , boost::dividable<Line<Real, N>, Real
        , boost::addable<Line<Real, N>, Vector<Real, N>
        , boost::subtractable<Line<Real, N>, Vector<Real, N>
        > > > >
    {
    public:
        using Real_ = Real;
        static constexpr int N_ = N;

        // Using default copy constructor.
        // Using default assignment.
        // Using default destructor.

        //! Construct a line parallel to x-axis.
        Line()
        : position_(0)
        , direction_(unitAxis<Real, N>(0))
        , inverseDirection_(inverse(direction_))
        {
            PASTEL_STATIC_ASSERT(N != Dynamic);
        }

        //! Construct a line parallel to x-axis.
        explicit Line(integer dimension)
        : position_(ofDimension(dimension), 0)
        , direction_(unitAxis<Real, N>(dimension, 0))
        , inverseDirection_(inverse(direction_))
        {
        }

        //! Construct a line given position and direction.
        Line(const Vector<Real, N>& position,
            const Vector<Real, N>& unitDirection)
        : position_(position)
        , direction_(unitDirection)
        , inverseDirection_(inverse(unitDirection))
        {
            PASTEL_STATIC_ASSERT(N != Dynamic);
        }

        //! Construct a line given position and direction.
        Line(integer dimension, 
            const Vector<Real, N>& position,
            const Vector<Real, N>& unitDirection)
        : position_(position)
        , direction_(unitDirection)
        , inverseDirection_(inverse(unitDirection))
        {
            PENSURE_OP(dimension, ==, position.n());
            PENSURE_OP(dimension, ==, unitDirection.n());
        }

        // Used for concept checking.
        ~Line()
        {
            PASTEL_STATIC_ASSERT(N == Dynamic || N > 0);
        }

        //! Swaps two lines.
        void swap(Line<Real, N>& that)
        {
            using std::swap;
            using std::swap;

            swap(position_, that.position_);
            swap(direction_, that.direction_);
            swap(inverseDirection_, that.inverseDirection_);
        }

        //! Sets the position and direction of the line.
        void set(const Vector<Real, N>& position,
            const Vector<Real, N>& unitDirection)
        {
            setPosition(position);
            setDirection(unitDirection);
        }

        //! Returns the dimension of the line.
        integer n() const
        {
            return position_.n();
        }

        //! Sets the position of the line.
        void setPosition(const Vector<Real, N>& position)
        {
            PENSURE_OP(position_.n(), ==, position.n());

            position_ = position;
        }

        //! Returns the position of the line.
        const Vector<Real, N>& position() const
        {
            return position_;
        }

        //! Sets the direction of the line.
        void setDirection(const Vector<Real, N>& unitDirection)
        {
            PENSURE_OP(direction_.n(), ==, unitDirection.n());

            direction_ = unitDirection;

            inverseDirection_ =
                inverse(unitDirection);
        }

        //! Returns the direction of the line.
        const Vector<Real, N>& direction() const
        {
            return direction_;
        }

        //! Returns the direction of the line inverted.
        /*!
       Inversion is done component-wise. This caching
       is meant to enable faster algorithms since
       division is costly to compute.
       */
        const Vector<Real, N>& inverseDirection() const
        {
            return inverseDirection_;
        }

        //! Returns the point (position + t * direction).
        Vector<Real, N> at(const Real& t) const
        {
            return position_ + direction_ * t;
        }

        //! Translates the line by the given vector.
        Line<Real, N>& operator+=(const Vector<Real, N>& that)
        {
            position_ += that;

            return *this;
        }

        //! Translates the line backwards by the given vector.
        Line<Real, N>& operator-=(const Vector<Real, N>& that)
        {
            position_ -= that;

            return *this;
        }

        //! Scales up the line without affecting position.
        Line<Real, N>& operator*=(const Real& that)
        {
            // Do nothing.

            unused(that);

            return *this;
        }

        //! Scales down the line without affecting position.
        Line<Real, N>& operator/=(const Real& that)
        {
            PENSURE_OP(that, !=, 0);

            // Do nothing.

            return *this;
        }

    private:
        Vector<Real, N> position_;
        Vector<Real, N> direction_;
        Vector<Real, N> inverseDirection_;
    };

    using Line1 = Line<dreal, 1>;
    using Line2 = Line<dreal, 2>;
    using Line3 = Line<dreal, 3>;
    using Line4 = Line<dreal, 4>;
    using LineD = Line<dreal, Dynamic>;

}

#endif