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