Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-06-17 08:37:37

0001 // -*- C++ -*-
0002 //
0003 // This file is part of YODA -- Yet more Objects for Data Analysis
0004 // Copyright (C) 2008-2025 The YODA collaboration (see AUTHORS for details)
0005 //
0006 #ifndef YODA_POINT_H
0007 #define YODA_POINT_H
0008 
0009 #include "YODA/AnalysisObject.h"
0010 #include "YODA/Exceptions.h"
0011 #include "YODA/Transformation.h"
0012 #include "YODA/Utils/MathUtils.h"
0013 #include "YODA/Utils/PointUtils.h"
0014 #include "YODA/Utils/Traits.h"
0015 #include "YODA/Utils/ndarray.h"
0016 #include <utility>
0017 #include <algorithm>
0018 #include <iostream>
0019 
0020 namespace YODA {
0021 
0022 
0023   /// Base class for all Point*Ds, providing generic access to their numerical properties
0024   class Point {
0025   public:
0026 
0027     using ValuePair = std::pair<double,double>;
0028     using Pair = std::pair<double,double>;
0029 
0030 
0031     /// Virtual destructor for inheritance
0032     virtual ~Point() {};
0033 
0034 
0035     /// @name Core properties
0036     /// @{
0037 
0038     /// Space dimension of the point
0039     virtual size_t dim() const = 0;
0040 
0041     /// Get the point value for direction @a i
0042     virtual double val(size_t i) const = 0;
0043 
0044     /// Set the point value for direction @a i
0045     virtual void setVal(const size_t i, const double val) = 0;
0046 
0047     /// Clear the point values and errors
0048     virtual void clear() = 0;
0049     /// @}
0050 
0051 
0052     /// @name Errors
0053     /// @{
0054 
0055     /// Get error values for direction @a i
0056     //virtual const std::pair<double,double>& errs(size_t i) const = 0;
0057     /// Set symmetric error for direction @a i
0058     virtual void setErr(const size_t i, const double e) = 0;
0059 
0060     /// Set asymmetric error for direction @a i
0061     virtual void setErrs(const size_t i, const double eminus, const double eplus) = 0;
0062 
0063     /// Set error pair for direction @a i
0064     virtual void setErrs(const size_t i, const std::pair<double,double>& e) = 0;
0065 
0066     /// Get negative error value for direction @a i
0067     //virtual double errMinus(size_t i) const = 0;
0068     /// Set negative error for direction @a i
0069     virtual void setErrMinus(const size_t i, const double eminus) = 0;
0070 
0071     /// Get positive error value for direction @a i
0072     //virtual double errPlus(size_t i) const = 0;
0073     /// Set positive error for direction @a i
0074     virtual void setErrPlus(const size_t i, const double eplus) = 0;
0075 
0076     /// Get error values along axis @a i
0077     virtual Pair errs(const size_t i) const = 0;
0078 
0079     /// Get the minus error along axis @a i
0080     virtual double errMinus(const size_t i) const = 0;
0081 
0082     /// Get the plus error along axis @a i
0083     virtual double errPlus(const size_t i) const = 0;
0084 
0085     /// Get average error value for direction @a i
0086     virtual double errAvg(const size_t i) const = 0;
0087 
0088     /// Get value minus negative error along axis @a i
0089     virtual double min(const size_t i) const = 0;
0090 
0091     /// Get value plus positive error along axis @a i
0092     virtual double max(const size_t i) const = 0;
0093 
0094     /// @}
0095 
0096 
0097     /// @name Combined value and error setters
0098     /// @{
0099 
0100     /// Set value and symmetric error for direction @a i
0101     virtual void set(const size_t i, const double val, const double e) = 0;
0102 
0103     /// Set value and asymmetric error for direction @a i
0104     virtual void set(const size_t i, const double val, const double eminus, const double eplus) = 0;
0105 
0106     /// Set value and asymmetric error for direction @a i
0107     virtual void set(const size_t i, const double val, const std::pair<double,double>& e) = 0;
0108 
0109     /// @}
0110 
0111 
0112     /// @name Manipulations
0113     /// @{
0114 
0115     /// Scaling of direction @a i
0116     virtual void scale(const size_t i, const double scale) = 0;
0117 
0118     /// Scaling value along direction @a i
0119     virtual void scaleVal(const size_t i, const double scale) = 0;
0120 
0121     /// Scaling error along direction @a i
0122     virtual void scaleErr(const size_t i, const double scale) = 0;
0123 
0124     //template<typename FN>
0125     //void scale(size_t i, FN f) = 0;
0126 
0127     /// @}
0128 
0129   };
0130 
0131 
0132 
0133 
0134 
0135   /// The base for an N-dimensional data point to be contained in a Scatter<N>
0136   template<size_t N>
0137   class PointBase : public Point {
0138   protected:
0139 
0140      // Convenient aliases
0141      using Pair = std::pair<double,double>;
0142      using ValList = std::initializer_list<double>;
0143      using PairList = std::initializer_list<Pair>;
0144 
0145      // extract the content type of an array Arr
0146      template<typename Arr>
0147      using containedType = std::decay_t<decltype(*std::declval<Arr>().begin())>;
0148 
0149      // check if content type of an array Arr is Pair
0150      template<typename Arr>
0151      using containsPair = typename std::is_same<containedType<Arr>, Pair>;
0152 
0153      // succeeds if T is an iterable container
0154      template<typename T>
0155      using isIterable = std::enable_if_t<Iterable<T>::value>;
0156 
0157      // succeeds if T is an iterable container and its content type is Pair
0158      template<typename T, typename U>
0159      using isIterableWithPair = std::enable_if_t<(Iterable<T>::value && Iterable<U>::value && containsPair<U>::value)>;
0160 
0161   public:
0162 
0163     // Typedefs
0164     using NdVal = typename Utils::ndarray<double, N>;
0165     using NdValPair = typename Utils::ndarray<std::pair<double,double>, N>;
0166     using DataSize = std::integral_constant<size_t, 3*N>;
0167 
0168 
0169     /// @name Constructors
0170     /// @{
0171 
0172     /// @brief Default constructor
0173     PointBase() {
0174       clear();
0175     }
0176 
0177 
0178     /// @brief Constructor from position values without errors
0179     template <typename ValRange = ValList, typename = isIterable<ValRange>>
0180     PointBase(ValRange&& val) : _val(std::forward<ValRange>(val)) { }
0181 
0182 
0183     /// Constructor from values and a set of asymmetric errors
0184     template <typename ValRange = ValList,
0185               typename PairRange = PairList,
0186               typename = isIterableWithPair<ValRange,PairRange>>
0187     PointBase(ValRange&& val, PairRange&& errs)
0188       : _val(std::forward<ValRange>(val)), _errs(std::forward<PairRange>(errs)) { }
0189 
0190     /// Constructor from values and a set of symmetric errors
0191     template <typename ValRange = ValList, typename = isIterable<ValRange>>
0192     PointBase(ValRange&& val, ValRange&& errs)
0193        : _val(std::forward<ValRange>(val)) {
0194       if (val.size() != N || errs.size() != N)
0195         throw RangeError("Expected " + std::to_string(N) + " dimensions.");
0196       size_t i = 0;
0197       auto it = std::begin(errs);
0198       const auto& itEnd = std::end(errs);
0199       for (; it != itEnd; ++it) {
0200         _errs[i++] = std::make_pair(*it, *it);
0201       }
0202     }
0203 
0204 
0205     /// Constructor from values and a set of asymmetric errors
0206     template <typename ValRange = ValList, typename = isIterable<ValRange>>
0207     PointBase(ValRange&& val, ValRange&& errsdn, ValRange&& errsup)
0208        : _val(std::forward<ValRange>(val)) {
0209       if (val.size() != N || errsdn.size() != N || errsup.size() != N)
0210         throw RangeError("Expected " + std::to_string(N) + " dimensions.");
0211       size_t i = 0;
0212       auto itdn = std::begin(errsdn);
0213       auto itup = std::begin(errsup);
0214       const auto& itEnd = std::end(errsdn);
0215       for (; itdn != itEnd; ) {
0216         _errs[i++] = std::make_pair(*itdn++, *itup++);
0217       }
0218     }
0219 
0220     PointBase(const PointBase& p) : _val(p._val), _errs(p._errs) { }
0221 
0222     PointBase(PointBase&& p) : _val(std::move(p._val)), _errs(std::move(p._errs)) { }
0223 
0224     /// @}
0225 
0226     /// Space dimension of the point
0227     size_t dim() const { return N; }
0228 
0229     /// @name I/O
0230     /// @{
0231 
0232     void _renderYODA(std::ostream& os, const int width = 13) const noexcept {
0233 
0234       for (size_t i = 0; i < N; ++i) {
0235         os << std::setw(width) << std::left << _val[i] << "\t"
0236            << std::setw(width) << std::left << _errs[i].first  << "\t"
0237            << std::setw(width) << std::left << _errs[i].second << "\t";
0238       }
0239       os << "\n";
0240 
0241     }
0242 
0243     /// @}
0244 
0245     /// @name Modifiers
0246     /// @{
0247 
0248     /// Clear the point values and errors
0249     void clear() {
0250       for (size_t i = 0; i < N; ++i) {
0251         _val[i] = 0;
0252         _errs[i] = {0.,0.};
0253       }
0254     }
0255 
0256     /// Assignment operator
0257     PointBase& operator = (const PointBase& p) {
0258       if (this != &p) {
0259         _val = p._val;
0260         _errs = p._errs;
0261       }
0262       return *this;
0263     }
0264 
0265     /// Assignment operator
0266     PointBase& operator = (PointBase&& p) {
0267       if (this != &p) {
0268         _val = std::move(p._val);
0269         _errs = std::move(p._errs);
0270       }
0271       return *this;
0272     }
0273 
0274     /// @todo addError, addErrors, setErrors
0275 
0276     /// @}
0277 
0278 
0279   public:
0280 
0281     /// @name Coordinate accessors
0282     /// @{
0283 
0284     /// Get the coordinate vector
0285     NdVal& vals() { return _val; }
0286 
0287     /// Get the coordinate vector (const version)
0288     const NdVal& vals() const { return _val; }
0289 
0290     /// Get the value along direction @a i
0291     double val(size_t i) const {
0292       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0293       return _val[i];
0294     }
0295 
0296     /// Set the coordinate vector
0297     void setVal(const NdVal& val) {
0298       _val = val;
0299     }
0300 
0301     /// Set a specific coordinate
0302     void setVal(const size_t i, const double val) {
0303       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0304       _val[i] = val;
0305     }
0306 
0307     /// @}
0308 
0309 
0310     /// @name Error accessors
0311     /// @{
0312 
0313     /// Get error values
0314     NdValPair& errs() {
0315       return _errs;
0316     }
0317 
0318     /// Get error values (const version)
0319     const NdValPair& errs() const {
0320       return _errs;
0321     }
0322 
0323     /// Get error values along axis @a i
0324     Pair errs(const size_t i) const {
0325       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0326       return _errs[i];
0327     }
0328 
0329     /// Get the minus error along axis @a i
0330     double errMinus(const size_t i) const {
0331       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0332       return _errs[i].first;
0333     }
0334 
0335     /// Get the plus error along axis @a i
0336     double errPlus(const size_t i) const {
0337       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0338       return _errs[i].second;
0339     }
0340 
0341     // Get the average error along axis @a i
0342     double errAvg(const size_t i) const {
0343       return 0.5*(errMinus(i) + errPlus(i));
0344     }
0345 
0346     /// Get value minus negative error along axis @a i
0347     double min(const size_t i) const {
0348       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0349       return _val[i] - _errs[i].first;
0350     }
0351 
0352     /// Get value plus positive error along axis @a i
0353     double max(const size_t i) const {
0354       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0355       return _val[i] + _errs[i].second;
0356     }
0357 
0358     /// Set a symmetric error pair along axis @a i
0359     void setErr(const size_t i, const double e) {
0360       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0361       const double err = fabs(e);
0362       _errs[i] = { err, err};
0363     }
0364 
0365     /// Set an asymmetric error pair along axis @a i
0366     void setErrs(const size_t i, const double eminus, const double eplus) {
0367       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0368       _errs[i] = { eminus, eplus};
0369     }
0370 
0371     /// Set a specific error pair along axis @a i
0372     void setErrs(const size_t i, const std::pair<double,double>& e) {
0373       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0374       _errs[i] = e;
0375     }
0376 
0377     /// Set a specific minus error along axis @a i
0378     void setErrMinus(const size_t i, const double eminus) {
0379       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0380       _errs[i].first = eminus;
0381     }
0382 
0383     /// Set a specific plus error along axis @a i
0384     void setErrPlus(const size_t i, const double eplus) {
0385       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0386       _errs[i].second = eplus;
0387     }
0388 
0389     /// @}
0390 
0391     /// @name Combined value and error setters
0392     /// @{
0393 
0394     void set(const size_t i, const double val, const double e) {
0395       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0396       const double err = fabs(e);
0397       _val[i] = val;
0398       _errs[i] = {err,err};
0399     }
0400 
0401     void set(const size_t i, const double val, const double eminus, const double eplus) {
0402       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0403       _val[i] = val;
0404       _errs[i].first = eminus;
0405       _errs[i].second = eplus;
0406     }
0407 
0408     void set(const size_t i, const double val, const std::pair<double,double>& e) {
0409       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0410       _val[i] = val;
0411       _errs[i] = e;
0412     }
0413 
0414     /// @}
0415 
0416     /// @name Scaling and transformations
0417     /// @{
0418 
0419     /// Scaling value along direction @a i
0420     void scaleVal(const size_t i, const double scale) {
0421       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0422       _val[i] *= scale;
0423     }
0424 
0425     /// Scaling error along direction @a i
0426     void scaleErr(const size_t i, const double scale) {
0427       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0428       _errs[i].first  *= scale;
0429       _errs[i].second *= scale;
0430     }
0431 
0432     /// Scaling along direction @a i
0433     void scale(const size_t i, const double scale) {
0434       scaleVal(i, scale);
0435       scaleErr(i, scale);
0436     }
0437 
0438     /// Uniform scaling
0439     void scale(const NdVal& scales) {
0440       for (size_t i = 0; i < N; ++i) {
0441         scale(i, scales[i]);
0442       }
0443     }
0444 
0445     /// Generalised transformations with functors
0446     void scale(const Trf<N>& trf) {
0447       trf.transform(_val, _errs);
0448     }
0449 
0450     /// Generalised transformations with functors
0451     /// along axis @a i
0452     void scale(const size_t i, const Trf<N>& trf) {
0453       if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0454       trf.transform(_val[i], _errs[i]);
0455     }
0456     //
0457     void transform(const size_t i, const Trf<N>& trf) {
0458       scale(i, trf);
0459     }
0460 
0461     /// @}
0462 
0463     /// @name MPI (de-)serialisation
0464     /// @{
0465 
0466     std::vector<double> _serializeContent() const noexcept {
0467       std::vector<double> rtn;
0468       rtn.reserve(DataSize::value);
0469       rtn.insert(rtn.end(), _val.begin(),  _val.end());
0470       for (auto err : _errs) {
0471         rtn.push_back(std::move(err.first));
0472         rtn.push_back(std::move(err.second));
0473       }
0474       return rtn;
0475     }
0476 
0477     void _deserializeContent(const std::vector<double>& data) {
0478 
0479       if (data.size() != DataSize::value)
0480         throw UserError("Length of serialized data should be "+std::to_string(DataSize::value)+"!");
0481 
0482       for (size_t i = 0; i < N; ++i) {
0483         _val[i] = data[i];
0484         _errs[i] = { data[N+i], data[2*N+i] };
0485       }
0486 
0487     }
0488 
0489     /// @}
0490 
0491   protected:
0492 
0493     /// @name Value and error variables
0494     /// @{
0495 
0496     NdVal _val;
0497     NdValPair _errs;
0498 
0499     /// @}
0500 
0501   };
0502 
0503 
0504 
0505   /// @name Comparison operators
0506   /// @{
0507 
0508   /// Equality test
0509   template <size_t N>
0510   inline bool operator==(const PointBase<N>& a, const PointBase<N>& b) {
0511     // Compare valitions
0512     for (size_t i = 0; i < N; ++i) {
0513       if ( !fuzzyEquals(a.vals()[i],        b.vals()[i]) )        return false;
0514       if ( !fuzzyEquals(a.errs()[i].first,  b.errs()[i].first))  return false;
0515       if ( !fuzzyEquals(a.errs()[i].second, b.errs()[i].second)) return false;
0516     }
0517     return true;
0518   }
0519 
0520   /// Inequality test
0521   template <size_t N>
0522   inline bool operator!=(const PointBase<N>& a, const PointBase<N>& b) {
0523     return !(a == b);
0524   }
0525 
0526 
0527   /// Less-than operator used to sort points
0528   template <size_t N>
0529   inline bool operator<(const PointBase<N>& a, const PointBase<N>& b) {
0530     #define LT_IF_NOT_EQ(a,b) { if (!fuzzyEquals(a, b)) return a < b; }
0531     for (size_t i = 0; i < N; ++i) {
0532       LT_IF_NOT_EQ(a.vals()[i],        b.vals()[i]);
0533       LT_IF_NOT_EQ(a.errs()[i].first,  b.errs()[i].first);
0534       LT_IF_NOT_EQ(a.errs()[i].second, b.errs()[i].second);
0535     }
0536     #undef LT_IF_NOT_EQ
0537     return false;
0538   }
0539 
0540   /// Less-than-or-equals operator used to sort points
0541   template <size_t N>
0542   inline bool operator<=(const PointBase<N>& a, const PointBase<N>& b) {
0543     if (a == b) return true;
0544     return a < b;
0545   }
0546 
0547   /// Greater-than operator used to sort points
0548   template <size_t N>
0549   inline bool operator>(const PointBase<N>& a, const PointBase<N>& b) {
0550     return !(a <= b);
0551   }
0552 
0553   /// Greater-than-or-equals operator used to sort points
0554   template <size_t N>
0555   inline bool operator>=(const PointBase<N>& a, const PointBase<N>& b) {
0556     return !(a < b);
0557   }
0558 
0559   /// @}
0560 
0561   template <size_t N>
0562   class PointND : public PointBase<N> {
0563     using BaseT = PointBase<N>;
0564     using BaseT::BaseT;
0565   };
0566 
0567 
0568 
0569   /// A 1D data point to be contained in a Scatter1D
0570   template <>
0571   class PointND<1> : public PointBase<1>,
0572                      public XDirectionMixin<PointND<1>> {
0573   public:
0574 
0575     using BaseT = PointBase<1>;
0576     using BaseT::BaseT;
0577 
0578     /// @name Constructors
0579     /// @{
0580 
0581     /// Constructor from values with optional symmetric errors
0582     PointND(double x, double ex=0.0)
0583       : BaseT({x}, {{ex, ex}}) {  }
0584 
0585 
0586     /// Constructor from values with explicit asymmetric errors
0587     PointND(double x, double exminus, double explus)
0588       : BaseT({x}, {{exminus, explus}}) {  }
0589 
0590 
0591     /// Constructor from values with asymmetric errors
0592     PointND(double x, const std::pair<double,double>& ex)
0593       : BaseT( {x}, {ex}) {  }
0594 
0595 
0596     /// Copy constructor
0597     PointND(const BaseT& other) : BaseT(other) {}
0598 
0599     /// Move constructor
0600     PointND(BaseT&& other) : BaseT(std::move(other)) {}
0601 
0602     /// @}
0603 
0604 
0605     /// @name Coordinate accessors
0606     /// @{
0607 
0608     /// Get the value along direction @a i
0609     double val(size_t i=0) const {
0610       if (i >= 1) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0611       return _val[i];
0612     }
0613 
0614     /// @}
0615 
0616   };
0617 
0618 
0619 
0620   /// A 2D data point to be contained in a Scatter2D
0621   template <>
0622   class PointND<2> : public PointBase<2>,
0623                      public XDirectionMixin<PointND<2>>,
0624                      public YDirectionMixin<PointND<2>> {
0625   public:
0626 
0627     using BaseT = PointBase<2>;
0628     using BaseT::BaseT;
0629 
0630     /// @name Constructors
0631     /// @{
0632 
0633     /// Constructor from values with optional symmetric errors
0634     PointND(double x, double y, double ex=0.0, double ey=0.0)
0635       : BaseT( {x,y}, {{ex,ex}, {ey,ey}}) {  }
0636 
0637 
0638     /// Constructor from values with explicit asymmetric errors
0639     PointND(double x, double y,
0640                  double exminus, double explus,
0641                  double eyminus, double eyplus)
0642       : BaseT({x,y}, {{exminus,explus}, {eyminus,eyplus}}) {  }
0643 
0644 
0645     /// Constructor from values with asymmetric errors on both x and y
0646     PointND(double x, double y, const std::pair<double,double>& ex, const std::pair<double,double>& ey)
0647       : BaseT({x,y}, {ex, ey}) {  }
0648 
0649 
0650     /// Copy constructor
0651     PointND(const BaseT& other) : BaseT(other) { }
0652 
0653     /// Move constructor
0654     PointND(BaseT&& other) : BaseT(std::move(other)) { }
0655 
0656     /// @}
0657 
0658 
0659     /// @name Manipulations
0660     /// @{
0661 
0662     /// Scaling of both axes
0663     void scaleXY(double scalex, double scaley) {
0664       scaleX(scalex);
0665       scaleY(scaley);
0666     }
0667 
0668     /// @}
0669 
0670   };
0671 
0672 
0673 
0674   /// A 3D data point to be contained in a Scatter3D
0675   template <>
0676   class PointND<3> : public PointBase<3>,
0677                      public XDirectionMixin<PointND<3>>,
0678                      public YDirectionMixin<PointND<3>>,
0679                      public ZDirectionMixin<PointND<3>> {
0680   public:
0681 
0682     using BaseT = PointBase<3>;
0683     using BaseT::BaseT;
0684 
0685     /// @name Constructors
0686     /// @{
0687 
0688     /// Constructor from values with optional symmetric errors
0689     PointND(double x, double y, double z, double ex=0.0, double ey=0.0, double ez=0.0)
0690       : BaseT({x,y,z}, {{ex,ex}, {ey,ey}, {ez,ez}}) { }
0691 
0692 
0693     /// Constructor from values with explicit asymmetric errors
0694     PointND(double x, double y, double z,
0695                  double exminus, double explus,
0696                  double eyminus, double eyplus,
0697                  double ezminus, double ezplus)
0698       : BaseT({x,y,z}, {{exminus,explus}, {eyminus,eyplus}, {ezminus,ezplus}}) { }
0699 
0700     /// Constructor from asymmetric errors given as vectors
0701     PointND(double x, double y, double z,
0702                  const std::pair<double,double>& ex,
0703                  const std::pair<double,double>& ey,
0704                  const std::pair<double,double>& ez)
0705       : BaseT({x,y,z}, {ex,ey,ez}) {  }
0706 
0707 
0708     /// Copy constructor
0709     PointND(const BaseT& other) : BaseT(other) { }
0710 
0711     /// Move constructor
0712     PointND(BaseT&& other) : BaseT(std::move(other)) { }
0713 
0714     /// @}
0715 
0716     /// @name Manipulations
0717     /// @{
0718 
0719     /// Scaling of both axes
0720     void scaleXYZ(double scalex, double scaley, double scalez) {
0721       scaleX(scalex);
0722       scaleY(scaley);
0723       scaleZ(scalez);
0724     }
0725 
0726     /// @}
0727 
0728   };
0729 
0730 
0731   /// @brief User-familiar alias
0732   using Point1D = PointND<1>;
0733   using Point2D = PointND<2>;
0734   using Point3D = PointND<3>;
0735   using Point4D = PointND<4>;
0736 
0737 }
0738 
0739 #endif