File indexing completed on 2025-07-15 09:14:10
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
0021 namespace YODA {
0022
0023
0024
0025 class Point {
0026 public:
0027
0028 using ValuePair = 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
0049
0050
0051
0052
0053
0054
0055
0056 virtual void setErr(const size_t i, const double e) = 0;
0057
0058
0059 virtual void setErrs(const size_t i, const double eminus, const double eplus) = 0;
0060
0061
0062 virtual void setErrs(const size_t i, const std::pair<double,double>& e) = 0;
0063
0064
0065
0066
0067 virtual void setErrMinus(const size_t i, const double eminus) = 0;
0068
0069
0070
0071
0072 virtual void setErrPlus(const size_t i, const double eplus) = 0;
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089 virtual void set(const size_t i, const double val, const double e) = 0;
0090
0091
0092 virtual void set(const size_t i, const double val, const double eminus, const double eplus) = 0;
0093
0094
0095 virtual void set(const size_t i, const double val, const std::pair<double,double>& e) = 0;
0096
0097
0098
0099
0100
0101
0102
0103
0104 virtual void scale(const size_t i, const double scale) = 0;
0105
0106
0107
0108
0109
0110
0111 };
0112
0113
0114
0115
0116
0117
0118 template<size_t N>
0119 class PointBase : public Point {
0120 protected:
0121
0122
0123 using Pair = std::pair<double,double>;
0124 using ValList = std::initializer_list<double>;
0125 using PairList = std::initializer_list<Pair>;
0126
0127
0128 template<typename Arr>
0129 using containedType = std::decay_t<decltype(*std::declval<Arr>().begin())>;
0130
0131
0132 template<typename Arr>
0133 using containsPair = typename std::is_same<containedType<Arr>, Pair>;
0134
0135
0136 template<typename T>
0137 using isIterable = std::enable_if_t<Iterable<T>::value>;
0138
0139
0140 template<typename T, typename U>
0141 using isIterableWithPair = std::enable_if_t<(Iterable<T>::value && Iterable<U>::value && containsPair<U>::value)>;
0142
0143 public:
0144
0145
0146 using NdVal = typename Utils::ndarray<double, N>;
0147 using NdValPair = typename Utils::ndarray<std::pair<double,double>, N>;
0148 using DataSize = std::integral_constant<size_t, 3*N>;
0149
0150
0151
0152
0153
0154
0155 PointBase() {
0156 clear();
0157 }
0158
0159
0160
0161 template <typename ValRange = ValList, typename = isIterable<ValRange>>
0162 PointBase(ValRange&& val) : _val(std::forward<ValRange>(val)) { }
0163
0164
0165
0166 template <typename ValRange = ValList,
0167 typename PairRange = PairList,
0168 typename = isIterableWithPair<ValRange,PairRange>>
0169 PointBase(ValRange&& val, PairRange&& errs)
0170 : _val(std::forward<ValRange>(val)), _errs(std::forward<PairRange>(errs)) { }
0171
0172
0173 template <typename ValRange = ValList, typename = isIterable<ValRange>>
0174 PointBase(ValRange&& val, ValRange&& errs)
0175 : _val(std::forward<ValRange>(val)) {
0176 if (val.size() != N || errs.size() != N)
0177 throw RangeError("Expected " + std::to_string(N) + " dimensions.");
0178 size_t i = 0;
0179 auto it = std::begin(errs);
0180 const auto& itEnd = std::end(errs);
0181 for (; it != itEnd; ++it) {
0182 _errs[i++] = std::make_pair(*it, *it);
0183 }
0184 }
0185
0186
0187
0188 template <typename ValRange = ValList, typename = isIterable<ValRange>>
0189 PointBase(ValRange&& val, ValRange&& errsdn, ValRange&& errsup)
0190 : _val(std::forward<ValRange>(val)) {
0191 if (val.size() != N || errsdn.size() != N || errsup.size() != N)
0192 throw RangeError("Expected " + std::to_string(N) + " dimensions.");
0193 size_t i = 0;
0194 auto itdn = std::begin(errsdn);
0195 auto itup = std::begin(errsup);
0196 const auto& itEnd = std::end(errsdn);
0197 for (; itdn != itEnd; ) {
0198 _errs[i++] = std::make_pair(*itdn++, *itup++);
0199 }
0200 }
0201
0202 PointBase(const PointBase& p) : _val(p._val), _errs(p._errs) { }
0203
0204 PointBase(PointBase&& p) : _val(std::move(p._val)), _errs(std::move(p._errs)) { }
0205
0206
0207
0208
0209 size_t dim() const { return N; }
0210
0211
0212
0213
0214 void _renderYODA(std::ostream& os, const int width = 13) const noexcept {
0215
0216 for (size_t i = 0; i < N; ++i) {
0217 os << std::setw(width) << std::left << _val[i] << "\t"
0218 << std::setw(width) << std::left << _errs[i].first << "\t"
0219 << std::setw(width) << std::left << _errs[i].second << "\t";
0220 }
0221 os << "\n";
0222
0223 }
0224
0225
0226
0227
0228
0229
0230
0231 void clear() {
0232 for (size_t i = 0; i < N; ++i) {
0233 _val[i] = 0;
0234 _errs[i] = {0.,0.};
0235 }
0236 }
0237
0238
0239 PointBase& operator = (const PointBase& p) {
0240 if (this != &p) {
0241 _val = p._val;
0242 _errs = p._errs;
0243 }
0244 return *this;
0245 }
0246
0247
0248 PointBase& operator = (PointBase&& p) {
0249 if (this != &p) {
0250 _val = std::move(p._val);
0251 _errs = std::move(p._errs);
0252 }
0253 return *this;
0254 }
0255
0256
0257
0258
0259
0260
0261 public:
0262
0263
0264
0265
0266
0267 NdVal& vals() { return _val; }
0268
0269
0270 const NdVal& vals() const { return _val; }
0271
0272
0273 double val(size_t i) const {
0274 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0275 return _val[i];
0276 }
0277
0278
0279 void setVal(const NdVal& val) {
0280 _val = val;
0281 }
0282
0283
0284 void setVal(const size_t i, const double val) {
0285 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0286 _val[i] = val;
0287 }
0288
0289
0290
0291
0292
0293
0294
0295
0296 NdValPair& errs() {
0297 return _errs;
0298 }
0299
0300
0301 const NdValPair& errs() const {
0302 return _errs;
0303 }
0304
0305
0306 Pair errs(const size_t i) const {
0307 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0308 return _errs[i];
0309 }
0310
0311
0312 double errMinus(const size_t i) const {
0313 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0314 return _errs[i].first;
0315 }
0316
0317
0318 double errPlus(const size_t i) const {
0319 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0320 return _errs[i].second;
0321 }
0322
0323
0324 double errAvg(const size_t i) const {
0325 return 0.5*(errMinus(i) + errPlus(i));
0326 }
0327
0328
0329 double min(const size_t i) const {
0330 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0331 return _val[i] - _errs[i].first;
0332 }
0333
0334
0335 double max(const size_t i) const {
0336 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0337 return _val[i] + _errs[i].second;
0338 }
0339
0340
0341 void setErr(const size_t i, const double e) {
0342 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0343 const double err = fabs(e);
0344 _errs[i] = { err, err};
0345 }
0346
0347
0348 void setErrs(const size_t i, const double eminus, const double eplus) {
0349 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0350 _errs[i] = { eminus, eplus};
0351 }
0352
0353
0354 void setErrs(const size_t i, const std::pair<double,double>& e) {
0355 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0356 _errs[i] = e;
0357 }
0358
0359
0360 void setErrMinus(const size_t i, const double eminus) {
0361 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0362 _errs[i].first = eminus;
0363 }
0364
0365
0366 void setErrPlus(const size_t i, const double eplus) {
0367 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0368 _errs[i].second = eplus;
0369 }
0370
0371
0372
0373
0374
0375
0376 void set(const size_t i, const double val, const double e) {
0377 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0378 const double err = fabs(e);
0379 _val[i] = val;
0380 _errs[i] = {err,err};
0381 }
0382
0383 void set(const size_t i, const double val, const double eminus, const double eplus) {
0384 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0385 _val[i] = val;
0386 _errs[i].first = eminus;
0387 _errs[i].second = eplus;
0388 }
0389
0390 void set(const size_t i, const double val, const std::pair<double,double>& e) {
0391 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0392 _val[i] = val;
0393 _errs[i] = e;
0394 }
0395
0396
0397
0398
0399
0400
0401
0402 void scaleVal(const size_t i, const double scale) {
0403 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0404 _val[i] *= scale;
0405 }
0406
0407
0408 void scaleErr(const size_t i, const double scale) {
0409 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0410 _errs[i].first *= scale;
0411 _errs[i].second *= scale;
0412 }
0413
0414
0415 void scale(const size_t i, const double scale) {
0416 scaleVal(i, scale);
0417 scaleErr(i, scale);
0418 }
0419
0420
0421 void scale(const NdVal& scales) {
0422 for (size_t i = 0; i < N; ++i) {
0423 scale(i, scales[i]);
0424 }
0425 }
0426
0427
0428 void scale(const Trf<N>& trf) {
0429 trf.transform(_val, _errs);
0430 }
0431
0432
0433
0434 void scale(const size_t i, const Trf<N>& trf) {
0435 if (i >= N) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0436 trf.transform(_val[i], _errs[i]);
0437 }
0438
0439 void transform(const size_t i, const Trf<N>& trf) {
0440 scale(i, trf);
0441 }
0442
0443
0444
0445
0446
0447
0448 std::vector<double> _serializeContent() const noexcept {
0449 std::vector<double> rtn;
0450 rtn.reserve(DataSize::value);
0451 rtn.insert(rtn.end(), _val.begin(), _val.end());
0452 for (auto err : _errs) {
0453 rtn.push_back(std::move(err.first));
0454 rtn.push_back(std::move(err.second));
0455 }
0456 return rtn;
0457 }
0458
0459 void _deserializeContent(const std::vector<double>& data) {
0460
0461 if (data.size() != DataSize::value)
0462 throw UserError("Length of serialized data should be "+std::to_string(DataSize::value)+"!");
0463
0464 for (size_t i = 0; i < N; ++i) {
0465 _val[i] = data[i];
0466 _errs[i] = { data[N+i], data[2*N+i] };
0467 }
0468
0469 }
0470
0471
0472
0473 protected:
0474
0475
0476
0477
0478 NdVal _val;
0479 NdValPair _errs;
0480
0481
0482
0483 };
0484
0485
0486
0487
0488
0489
0490
0491 template <size_t N>
0492 inline bool operator==(const PointBase<N>& a, const PointBase<N>& b) {
0493
0494 for (size_t i = 0; i < N; ++i) {
0495 if ( !fuzzyEquals(a.vals()[i], b.vals()[i]) ) return false;
0496 if ( !fuzzyEquals(a.errs()[i].first, b.errs()[i].first)) return false;
0497 if ( !fuzzyEquals(a.errs()[i].second, b.errs()[i].second)) return false;
0498 }
0499 return true;
0500 }
0501
0502
0503 template <size_t N>
0504 inline bool operator!=(const PointBase<N>& a, const PointBase<N>& b) {
0505 return !(a == b);
0506 }
0507
0508
0509
0510 template <size_t N>
0511 inline bool operator<(const PointBase<N>& a, const PointBase<N>& b) {
0512 #define LT_IF_NOT_EQ(a,b) { if (!fuzzyEquals(a, b)) return a < b; }
0513 for (size_t i = 0; i < N; ++i) {
0514 LT_IF_NOT_EQ(a.vals()[i], b.vals()[i]);
0515 LT_IF_NOT_EQ(a.errs()[i].first, b.errs()[i].first);
0516 LT_IF_NOT_EQ(a.errs()[i].second, b.errs()[i].second);
0517 }
0518 #undef LT_IF_NOT_EQ
0519 return false;
0520 }
0521
0522
0523 template <size_t N>
0524 inline bool operator<=(const PointBase<N>& a, const PointBase<N>& b) {
0525 if (a == b) return true;
0526 return a < b;
0527 }
0528
0529
0530 template <size_t N>
0531 inline bool operator>(const PointBase<N>& a, const PointBase<N>& b) {
0532 return !(a <= b);
0533 }
0534
0535
0536 template <size_t N>
0537 inline bool operator>=(const PointBase<N>& a, const PointBase<N>& b) {
0538 return !(a < b);
0539 }
0540
0541
0542
0543 template <size_t N>
0544 class PointND : public PointBase<N> {
0545 using BaseT = PointBase<N>;
0546 using BaseT::BaseT;
0547 };
0548
0549
0550
0551
0552 template <>
0553 class PointND<1> : public PointBase<1>,
0554 public XDirectionMixin<PointND<1>> {
0555 public:
0556
0557 using BaseT = PointBase<1>;
0558 using BaseT::BaseT;
0559
0560
0561
0562
0563
0564 PointND(double x, double ex=0.0)
0565 : BaseT({x}, {{ex, ex}}) { }
0566
0567
0568
0569 PointND(double x, double exminus, double explus)
0570 : BaseT({x}, {{exminus, explus}}) { }
0571
0572
0573
0574 PointND(double x, const std::pair<double,double>& ex)
0575 : BaseT( {x}, {ex}) { }
0576
0577
0578
0579 PointND(const BaseT& other) : BaseT(other) {}
0580
0581
0582 PointND(BaseT&& other) : BaseT(std::move(other)) {}
0583
0584
0585
0586
0587
0588
0589
0590
0591 double val(size_t i=0) const {
0592 if (i >= 1) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0593 return _val[i];
0594 }
0595
0596
0597
0598 };
0599
0600
0601
0602
0603 template <>
0604 class PointND<2> : public PointBase<2>,
0605 public XDirectionMixin<PointND<2>>,
0606 public YDirectionMixin<PointND<2>> {
0607 public:
0608
0609 using BaseT = PointBase<2>;
0610 using BaseT::BaseT;
0611
0612
0613
0614
0615
0616 PointND(double x, double y, double ex=0.0, double ey=0.0)
0617 : BaseT( {x,y}, {{ex,ex}, {ey,ey}}) { }
0618
0619
0620
0621 PointND(double x, double y,
0622 double exminus, double explus,
0623 double eyminus, double eyplus)
0624 : BaseT({x,y}, {{exminus,explus}, {eyminus,eyplus}}) { }
0625
0626
0627
0628 PointND(double x, double y, const std::pair<double,double>& ex, const std::pair<double,double>& ey)
0629 : BaseT({x,y}, {ex, ey}) { }
0630
0631
0632
0633 PointND(const BaseT& other) : BaseT(other) { }
0634
0635
0636 PointND(BaseT&& other) : BaseT(std::move(other)) { }
0637
0638
0639
0640
0641
0642
0643
0644
0645 void scaleXY(double scalex, double scaley) {
0646 scaleX(scalex);
0647 scaleY(scaley);
0648 }
0649
0650
0651
0652 };
0653
0654
0655
0656
0657 template <>
0658 class PointND<3> : public PointBase<3>,
0659 public XDirectionMixin<PointND<3>>,
0660 public YDirectionMixin<PointND<3>>,
0661 public ZDirectionMixin<PointND<3>> {
0662 public:
0663
0664 using BaseT = PointBase<3>;
0665 using BaseT::BaseT;
0666
0667
0668
0669
0670
0671 PointND(double x, double y, double z, double ex=0.0, double ey=0.0, double ez=0.0)
0672 : BaseT({x,y,z}, {{ex,ex}, {ey,ey}, {ez,ez}}) { }
0673
0674
0675
0676 PointND(double x, double y, double z,
0677 double exminus, double explus,
0678 double eyminus, double eyplus,
0679 double ezminus, double ezplus)
0680 : BaseT({x,y,z}, {{exminus,explus}, {eyminus,eyplus}, {ezminus,ezplus}}) { }
0681
0682
0683 PointND(double x, double y, double z,
0684 const std::pair<double,double>& ex,
0685 const std::pair<double,double>& ey,
0686 const std::pair<double,double>& ez)
0687 : BaseT({x,y,z}, {ex,ey,ez}) { }
0688
0689
0690
0691 PointND(const BaseT& other) : BaseT(other) { }
0692
0693
0694 PointND(BaseT&& other) : BaseT(std::move(other)) { }
0695
0696
0697
0698
0699
0700
0701
0702 void scaleXYZ(double scalex, double scaley, double scalez) {
0703 scaleX(scalex);
0704 scaleY(scaley);
0705 scaleZ(scalez);
0706 }
0707
0708
0709
0710 };
0711
0712
0713
0714 using Point1D = PointND<1>;
0715 using Point2D = PointND<2>;
0716 using Point3D = PointND<3>;
0717 using Point4D = PointND<4>;
0718
0719 }
0720
0721 #endif