File indexing completed on 2026-06-17 08:37:37
0001
0002
0003
0004
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
0024 class Point {
0025 public:
0026
0027 using ValuePair = std::pair<double,double>;
0028 using Pair = std::pair<double,double>;
0029
0030
0031
0032 virtual ~Point() {};
0033
0034
0035
0036
0037
0038
0039 virtual size_t dim() const = 0;
0040
0041
0042 virtual double val(size_t i) const = 0;
0043
0044
0045 virtual void setVal(const size_t i, const double val) = 0;
0046
0047
0048 virtual void clear() = 0;
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058 virtual void setErr(const size_t i, const double e) = 0;
0059
0060
0061 virtual void setErrs(const size_t i, const double eminus, const double eplus) = 0;
0062
0063
0064 virtual void setErrs(const size_t i, const std::pair<double,double>& e) = 0;
0065
0066
0067
0068
0069 virtual void setErrMinus(const size_t i, const double eminus) = 0;
0070
0071
0072
0073
0074 virtual void setErrPlus(const size_t i, const double eplus) = 0;
0075
0076
0077 virtual Pair errs(const size_t i) const = 0;
0078
0079
0080 virtual double errMinus(const size_t i) const = 0;
0081
0082
0083 virtual double errPlus(const size_t i) const = 0;
0084
0085
0086 virtual double errAvg(const size_t i) const = 0;
0087
0088
0089 virtual double min(const size_t i) const = 0;
0090
0091
0092 virtual double max(const size_t i) const = 0;
0093
0094
0095
0096
0097
0098
0099
0100
0101 virtual void set(const size_t i, const double val, const double e) = 0;
0102
0103
0104 virtual void set(const size_t i, const double val, const double eminus, const double eplus) = 0;
0105
0106
0107 virtual void set(const size_t i, const double val, const std::pair<double,double>& e) = 0;
0108
0109
0110
0111
0112
0113
0114
0115
0116 virtual void scale(const size_t i, const double scale) = 0;
0117
0118
0119 virtual void scaleVal(const size_t i, const double scale) = 0;
0120
0121
0122 virtual void scaleErr(const size_t i, const double scale) = 0;
0123
0124
0125
0126
0127
0128
0129 };
0130
0131
0132
0133
0134
0135
0136 template<size_t N>
0137 class PointBase : public Point {
0138 protected:
0139
0140
0141 using Pair = std::pair<double,double>;
0142 using ValList = std::initializer_list<double>;
0143 using PairList = std::initializer_list<Pair>;
0144
0145
0146 template<typename Arr>
0147 using containedType = std::decay_t<decltype(*std::declval<Arr>().begin())>;
0148
0149
0150 template<typename Arr>
0151 using containsPair = typename std::is_same<containedType<Arr>, Pair>;
0152
0153
0154 template<typename T>
0155 using isIterable = std::enable_if_t<Iterable<T>::value>;
0156
0157
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
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
0170
0171
0172
0173 PointBase() {
0174 clear();
0175 }
0176
0177
0178
0179 template <typename ValRange = ValList, typename = isIterable<ValRange>>
0180 PointBase(ValRange&& val) : _val(std::forward<ValRange>(val)) { }
0181
0182
0183
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
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
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
0227 size_t dim() const { return N; }
0228
0229
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
0246
0247
0248
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
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
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
0275
0276
0277
0278
0279 public:
0280
0281
0282
0283
0284
0285 NdVal& vals() { return _val; }
0286
0287
0288 const NdVal& vals() const { return _val; }
0289
0290
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
0297 void setVal(const NdVal& val) {
0298 _val = val;
0299 }
0300
0301
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
0311
0312
0313
0314 NdValPair& errs() {
0315 return _errs;
0316 }
0317
0318
0319 const NdValPair& errs() const {
0320 return _errs;
0321 }
0322
0323
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
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
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
0342 double errAvg(const size_t i) const {
0343 return 0.5*(errMinus(i) + errPlus(i));
0344 }
0345
0346
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
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
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
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
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
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
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
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
0417
0418
0419
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
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
0433 void scale(const size_t i, const double scale) {
0434 scaleVal(i, scale);
0435 scaleErr(i, scale);
0436 }
0437
0438
0439 void scale(const NdVal& scales) {
0440 for (size_t i = 0; i < N; ++i) {
0441 scale(i, scales[i]);
0442 }
0443 }
0444
0445
0446 void scale(const Trf<N>& trf) {
0447 trf.transform(_val, _errs);
0448 }
0449
0450
0451
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
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
0494
0495
0496 NdVal _val;
0497 NdValPair _errs;
0498
0499
0500
0501 };
0502
0503
0504
0505
0506
0507
0508
0509 template <size_t N>
0510 inline bool operator==(const PointBase<N>& a, const PointBase<N>& b) {
0511
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
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
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
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
0548 template <size_t N>
0549 inline bool operator>(const PointBase<N>& a, const PointBase<N>& b) {
0550 return !(a <= b);
0551 }
0552
0553
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
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
0579
0580
0581
0582 PointND(double x, double ex=0.0)
0583 : BaseT({x}, {{ex, ex}}) { }
0584
0585
0586
0587 PointND(double x, double exminus, double explus)
0588 : BaseT({x}, {{exminus, explus}}) { }
0589
0590
0591
0592 PointND(double x, const std::pair<double,double>& ex)
0593 : BaseT( {x}, {ex}) { }
0594
0595
0596
0597 PointND(const BaseT& other) : BaseT(other) {}
0598
0599
0600 PointND(BaseT&& other) : BaseT(std::move(other)) {}
0601
0602
0603
0604
0605
0606
0607
0608
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
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
0631
0632
0633
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
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
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
0651 PointND(const BaseT& other) : BaseT(other) { }
0652
0653
0654 PointND(BaseT&& other) : BaseT(std::move(other)) { }
0655
0656
0657
0658
0659
0660
0661
0662
0663 void scaleXY(double scalex, double scaley) {
0664 scaleX(scalex);
0665 scaleY(scaley);
0666 }
0667
0668
0669
0670 };
0671
0672
0673
0674
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
0686
0687
0688
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
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
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
0709 PointND(const BaseT& other) : BaseT(other) { }
0710
0711
0712 PointND(BaseT&& other) : BaseT(std::move(other)) { }
0713
0714
0715
0716
0717
0718
0719
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
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