1 // -*- C++ -*-
2 //
3 // This file is part of YODA -- Yet more Objects for Data Analysis
4 // Copyright (C) 2008-2021 The YODA collaboration (see AUTHORS for details)
5 //
6 #ifndef YODA_POINT3D_H
7 #define YODA_POINT3D_H
8 
9 #include "YODA/Point.h"
10 #include "YODA/Exceptions.h"
11 #include "YODA/Utils/MathUtils.h"
12 #include <utility>
13 
14 namespace YODA {
15 
16 
17   /// A 3D data point to be contained in a Scatter3D
18   class Point3D : public Point {
19   public:
20 
21     /// @name Constructors
22     /// @{
23 
24     // Default constructor
Point3D()25     Point3D() {  }
26 
27 
28     /// Constructor from values with optional symmetric errors
29     Point3D(double x, double y, double z, double ex=0.0, double ey=0.0, double ez=0.0,  std::string source="")
_x(x)30       : _x(x), _y(y), _z(z)
31     {
32       _ex = std::make_pair(ex, ex);
33       _ey = std::make_pair(ey, ey);
34       _ez[source] = std::make_pair(ez, ez);
35     }
36 
37 
38     /// Constructor from values with explicit asymmetric errors
39     Point3D(double x, double y, double z,
40             double exminus,
41             double explus,
42             double eyminus,
43             double eyplus,
44             double ezminus,
45             double ezplus,  std::string source="")
_x(x)46       : _x(x), _y(y), _z(z)
47     {
48       _ex = std::make_pair(exminus, explus);
49       _ey = std::make_pair(eyminus, eyplus);
50       _ez[source] = std::make_pair(ezminus, ezplus);
51     }
52 
53     /// Constructor from asymmetric errors given as vectors
54     Point3D(double x, double y, double z,
55             const std::pair<double,double>& ex,
56             const std::pair<double,double>& ey,
57             const std::pair<double,double>& ez,  std::string source="")
_x(x)58       : _x(x), _y(y), _z(z),
59         _ex(ex), _ey(ey)
60     {
61       _ez[source] = ez;
62     }
63 
64 
65     /// Copy constructor
Point3D(const Point3D & p)66     Point3D(const Point3D& p)
67       : _x(p._x), _y(p._y), _z(p._z),
68         _ex(p._ex), _ey(p._ey), _ez(p._ez)
69     {
70       this->setParent( p.getParent() );
71     }
72 
73 
74     /// Copy assignment
75     Point3D& operator = (const Point3D& p) {
76       _x = p._x;
77       _y = p._y;
78       _z = p._z;
79       _ex = p._ex;
80       _ey = p._ey;
81       _ez = p._ez;
82       this->setParent( p.getParent() );
83       return *this;
84     }
85 
86 
87     /// @}
88 
89 
90   public:
91 
92     /// Space dimension of the point
dim()93     size_t dim() { return 3; }
94 
95 
96     /// @name Value and error accessors
97     /// @{
98 
99     /// Get x value
x()100     double x() const { return _x; }
101 
102     /// Set x value
setX(double x)103     void setX(double x) { _x = x; }
104 
105     /// Get y value
y()106     double y() const { return _y; }
107 
108     /// Set y value
setY(double y)109     void setY(double y) { _y = y; }
110 
111     /// Get z value
z()112     double z() const { return _z;}
113 
114     /// Set z value
setZ(double z)115     void setZ(double z) { _z = z;}
116 
117     /// @todo Uniform "coords" accessor across all Scatters: returning fixed-size tuple?
118 
119     // /// Get x,y,z value tuple
120     // triple<double,double,double> xyz() const { return std::triple(_x, _y, _z); }
121 
122     /// Set x, y and z values
setXYZ(double x,double y,double z)123     void setXYZ(double x, double y, double z) { setX(x); setY(y); setZ(z); }
124 
125     // /// Set x and y values
126     // void setXY(triple<double,double,double> xyz) { setX(xy.first); setY(xy.second); setZ(xy.third); }
127 
128     /// @}
129 
130 
131     /// @name x error accessors
132     /// @{
133 
134     /// Get x-error values
xErrs()135     const std::pair<double,double>& xErrs() const {
136       return _ex;
137     }
138 
139     /// Get negative x-error value
xErrMinus()140     double xErrMinus() const {
141       return _ex.first;
142     }
143 
144     /// Get positive x-error value
xErrPlus()145     double xErrPlus() const {
146       return _ex.second;
147     }
148 
149     /// Get average x-error value
xErrAvg()150     double xErrAvg() const {
151       return (_ex.first + _ex.second)/2.0;
152     }
153 
154     /// Set negative x error
setXErrMinus(double exminus)155     void setXErrMinus(double exminus) {
156       _ex.first = exminus;
157     }
158 
159     /// Set positive x error
setXErrPlus(double explus)160     void setXErrPlus(double explus) {
161       _ex.second = explus;
162     }
163 
164     /// Set symmetric x error
setXErr(double ex)165     void setXErr(double ex) {
166       setXErrMinus(ex);
167       setXErrPlus(ex);
168     }
169 
170     /// Set symmetric x error (alias)
setXErrs(double ex)171     void setXErrs(double ex) {
172       setXErr(ex);
173     }
174 
175     /// Set asymmetric x error
setXErrs(double exminus,double explus)176     void setXErrs(double exminus, double explus) {
177       setXErrMinus(exminus);
178       setXErrPlus(explus);
179     }
180 
181     /// Set asymmetric x error
setXErrs(const std::pair<double,double> & ex)182     void setXErrs(const std::pair<double,double>& ex) {
183       _ex = ex;
184     }
185 
186     /// Get value minus negative x-error
xMin()187     double xMin() const {
188       return _x - _ex.first;
189     }
190 
191     /// Get value plus positive x-error
xMax()192     double xMax() const {
193       return _x + _ex.second;
194     }
195 
196     /// @}
197 
198 
199     /// @name y error accessors
200     /// @{
201 
202     /// Get y-error values
yErrs()203     const std::pair<double,double>& yErrs() const {
204       return _ey;
205     }
206 
207     /// Get negative y-error value
yErrMinus()208     double yErrMinus() const {
209       return _ey.first;
210     }
211 
212     /// Get positive y-error value
yErrPlus()213     double yErrPlus() const {
214       return _ey.second;
215     }
216 
217     /// Get average y-error value
yErrAvg()218     double yErrAvg() const {
219       return (_ey.first + _ey.second)/2.0;
220     }
221 
222     /// Set negative y error
setYErrMinus(double eyminus)223     void setYErrMinus(double eyminus) {
224       _ey.first = eyminus;
225     }
226 
227     /// Set positive y error
setYErrPlus(double eyplus)228     void setYErrPlus(double eyplus) {
229       _ey.second = eyplus;
230     }
231 
232     /// Set symmetric y error
setYErr(double ey)233     void setYErr(double ey) {
234       setYErrMinus(ey);
235       setYErrPlus(ey);
236     }
237 
238     /// Set symmetric y error (alias)
setYErrs(double ey)239     void setYErrs(double ey) {
240       setYErr(ey);
241     }
242 
243     /// Set asymmetric y error
setYErrs(double eyminus,double eyplus)244     void setYErrs(double eyminus, double eyplus) {
245       setYErrMinus(eyminus);
246       setYErrPlus(eyplus);
247     }
248 
249     /// Set asymmetric y error
setYErrs(const std::pair<double,double> & ey)250     void setYErrs(const std::pair<double,double>& ey) {
251       _ey = ey;
252     }
253 
254     /// Get value minus negative y-error
yMin()255     double yMin() const {
256       return _y - _ey.first;
257     }
258 
259     /// Get value plus positive y-error
yMax()260     double yMax() const {
261       return _y + _ey.second;
262     }
263 
264     /// @}
265 
266 
267     /// @name z error accessors
268     /// @{
269 
270 
271     /// Get z-error values
272     const std::pair<double,double>& zErrs( std::string source="") const {
273       if (source!="") getVariationsFromParent();
274       if (!_ez.count(source)) throw RangeError("zErrs has no such key: "+source);
275       return _ez.at(source);
276     }
277 
278     /// Get negative z-error value
279     double zErrMinus( std::string source="") const {
280       if (source!="") getVariationsFromParent();
281       if (!_ez.count(source)) throw RangeError("zErrs has no such key: "+source);
282       return _ez.at(source).first;
283     }
284 
285     /// Get positive z-error value
286     double zErrPlus( std::string source="") const {
287       if (source!="") getVariationsFromParent();
288       if (!_ez.count(source)) throw RangeError("zErrs has no such key: "+source);
289       return _ez.at(source).second;
290     }
291 
292     /// Get average z-error value
293     double zErrAvg( std::string source="") const {
294       if (source!="") getVariationsFromParent();
295       if (!_ez.count(source)) throw RangeError("zErrs has no such key: "+source);
296       return (_ez.at(source).first + _ez.at(source).second)/2.0;
297     }
298 
299     /// Set negative z error
300     void setZErrMinus(double ezminus,  std::string source="") {
301       if (!_ez.count(source)) _ez[source] = std::make_pair(0.,0.);
302       _ez.at(source).first = ezminus;
303     }
304 
305     /// Set positive z error
306     void setZErrPlus(double ezplus,  std::string source="") {
307       if (!_ez.count(source)) _ez[source] = std::make_pair(0.,0.);
308       _ez.at(source).second = ezplus;
309     }
310 
311     /// Set symmetric z error
312     void setZErr(double ez,  std::string source="") {
313       setZErrMinus(ez, source);
314       setZErrPlus(ez, source);
315     }
316 
317     /// Set symmetric z error (alias)
318     void setZErrs(double ez,  std::string source="") {
319       setZErr(ez, source);
320     }
321 
322     /// Set asymmetric z error
323     void setZErrs(double ezminus, double ezplus,  std::string source="") {
324       setZErrMinus(ezminus, source);
325       setZErrPlus(ezplus, source);
326     }
327 
328     /// Set asymmetric z error
329     void setZErrs(const std::pair<double,double>& ez,  std::string source="") {
330       _ez[source] = ez;
331     }
332 
333     /// Get value minus negative z-error
334     double zMin( std::string source="") const {
335       if (source!="") getVariationsFromParent();
336       if (!_ez.count(source)) throw RangeError("zErrs has no such key: "+source);
337       return _z - _ez.at(source).first;
338     }
339 
340     /// Get value plus positive z-error
341     double zMax( std::string source="") const {
342       if (source!="") getVariationsFromParent();
343       if (!_ez.count(source)) throw RangeError("zErrs has no such key: "+source);
344       return _z + _ez.at(source).second;
345     }
346 
347     /// @}
348 
349 
350     /// @name Combined x/y value and error setters
351     /// @{
352 
353     /// Set x value and symmetric error
setX(double x,double ex)354     void setX(double x, double ex) {
355       setX(x);
356       setXErrs(ex);
357     }
358 
359     /// Set x value and asymmetric error
setX(double x,double exminus,double explus)360     void setX(double x, double exminus, double explus) {
361       setX(x);
362       setXErrs(exminus, explus);
363     }
364 
365     /// Set x value and asymmetric error
setX(double x,std::pair<double,double> & ex)366     void setX(double x, std::pair<double,double>& ex) {
367       setX(x);
368       setXErrs(ex);
369     }
370 
371 
372     /// Set y value and symmetric error
setY(double y,double ey)373     void setY(double y, double ey) {
374       setY(y);
375       setYErrs(ey);
376     }
377 
378     /// Set y value and asymmetric error
setY(double y,double eyminus,double eyplus)379     void setY(double y, double eyminus, double eyplus) {
380       setY(y);
381       setYErrs(eyminus, eyplus);
382     }
383 
384     /// Set y value and asymmetric error
setY(double y,std::pair<double,double> & ey)385     void setY(double y, std::pair<double,double>& ey) {
386       setY(y);
387       setYErrs(ey);
388     }
389 
390 
391     /// Set z value and symmetric error
392     void setZ(double z, double ez, std::string source="") {
393       setZ(z);
394       setZErrs(ez, source);
395     }
396 
397     /// Set z value and asymmetric error
398     void setZ(double z, double ezminus, double ezplus, std::string source="") {
399       setZ(z);
400       setZErrs(ezminus, ezplus, source);
401     }
402 
403     /// Set z value and asymmetric error
404     void setZ(double z, std::pair<double,double>& ez, std::string source="") {
405       setZ(z);
406       setZErrs(ez, source);
407     }
408 
409     /// @}
410 
411 
412     // @name Manipulations
413     /// @{
414 
415     /// Scaling of x axis
scaleX(double scalex)416     void scaleX(double scalex) {
417       setX(x()*scalex);
418       setXErrs(xErrMinus()*scalex, xErrPlus()*scalex);
419     }
420 
421     /// Scaling of y axis
scaleY(double scaley)422     void scaleY(double scaley) {
423       setY(y()*scaley);
424       setYErrs(yErrMinus()*scaley, yErrPlus()*scaley);
425     }
426 
427     /// Scaling of z axis
scaleZ(double scalez)428     void scaleZ(double scalez) {
429       setZ(z()*scalez);
430       for (const auto   &source : _ez){
431         setZErrs(zErrMinus()*scalez, zErrPlus()*scalez, source.first);
432       }
433     }
434 
435     /// Scaling of all three axes
scaleXYZ(double scalex,double scaley,double scalez)436     void scaleXYZ(double scalex, double scaley, double scalez) {
437       scaleX(scalex);
438       scaleY(scaley);
439       scaleZ(scalez);
440     }
441 
442     /// Scaling along direction @a i
scale(size_t i,double scale)443     void scale(size_t i, double scale) {
444       switch (i) {
445       case 1: scaleX(scale); break;
446       case 2: scaleY(scale); break;
447       case 3: scaleZ(scale); break;
448       default: throw RangeError("Invalid axis int, must be in range 1..dim");
449       }
450     }
451 
452     /// @}
453 
454 
455     /// @name Integer axis accessor equivalents
456     /// @{
457 
458     /// Get the point value for direction @a i
val(size_t i)459     double val(size_t i) const {
460       switch (i) {
461       case 1: return x();
462       case 2: return y();
463       case 3: return z();
464       default: throw RangeError("Invalid axis int, must be in range 1..dim");
465       }
466     }
467     /// Set the point value for direction @a i
setVal(size_t i,double val)468     void setVal(size_t i, double val) {
469       switch (i) {
470       case 1: setX(val); break;
471       case 2: setY(val); break;
472       case 3: setZ(val); break;
473       default: throw RangeError("Invalid axis int, must be in range 1..dim");
474       }
475     }
476 
477     /// Get error map for direction @a i
errMap()478     const std::map< std::string, std::pair<double,double>> & errMap() const {
479       getVariationsFromParent();
480       return _ez;
481     }
482 
483     // Parse the variations from the parent AO if it exists
484     void getVariationsFromParent() const;
485 
486     // set the "" error source to the sum in quad of the existing variations
updateTotalUncertainty()487     void updateTotalUncertainty() {
488         float totalUp = 0.;
489         float totalDn = 0.;
490         for (const auto& variation : getParent()->variations()) {
491           if (variation=="") continue;
492           float thisUp =  zErrPlus(variation);
493           float thisDn =  zErrMinus(variation);
494           totalUp += thisUp*thisUp;
495           totalDn += thisDn*thisDn;
496         }
497 
498         totalUp = sqrt(totalUp);
499         totalDn = sqrt(totalDn);
500         setErrPlus(3, totalUp);
501         setErrMinus(3, totalDn);
502     }
503 
504     /// Get error values for direction @a i
505     const std::pair<double,double>& errs(size_t i,  std::string source="") const {
506       switch (i) {
507       case 1: return xErrs();
508       case 2: return yErrs();
509       case 3: return zErrs(source);
510       default: throw RangeError("Invalid axis int, must be in range 1..dim");
511       }
512     }
513     /// Get negative error value for direction @a i
514     double errMinus(size_t i,  std::string source="") const {
515       switch (i) {
516       case 1: return xErrMinus();
517       case 2: return yErrMinus();
518       case 3: return zErrMinus(source);
519       default: throw RangeError("Invalid axis int, must be in range 1..dim");
520       }
521     }
522     /// Get positive error value for direction @a i
523     double errPlus(size_t i,  std::string source="") const {
524       switch (i) {
525       case 1: return xErrPlus();
526       case 2: return yErrPlus();
527       case 3: return zErrPlus(source);
528       default: throw RangeError("Invalid axis int, must be in range 1..dim");
529       }
530     }
531     /// Get average error value for direction @a i
532     double errAvg(size_t i,  std::string source="") const {
533       switch (i) {
534       case 1: return xErrAvg();
535       case 2: return yErrAvg();
536       case 3: return zErrAvg(source);
537       default: throw RangeError("Invalid axis int, must be in range 1..dim");
538       }
539     }
540 
541     /// Set negative error for direction @a i
542     void setErrMinus(size_t i, double eminus,  std::string source="") {
543       switch (i) {
544       case 1: setXErrMinus(eminus); break;
545       case 2: setYErrMinus(eminus); break;
546       case 3: setZErrMinus(eminus, source); break;
547       default: throw RangeError("Invalid axis int, must be in range 1..dim");
548       }
549     }
550     /// Set positive error for direction @a i
551     void setErrPlus(size_t i, double eplus,  std::string source="") {
552       switch (i) {
553       case 1: setXErrPlus(eplus); break;
554       case 2: setYErrPlus(eplus); break;
555       case 3: setZErrPlus(eplus, source); break;
556       default: throw RangeError("Invalid axis int, must be in range 1..dim");
557       }
558     }
559 
560     /// Set symmetric error for direction @a i
561     void setErr(size_t i, double e,  std::string source="") {
562       switch (i) {
563       case 1: setXErrs(e); break;
564       case 2: setYErrs(e); break;
565       case 3: setZErrs(e, source); break;
566       default: throw RangeError("Invalid axis int, must be in range 1..dim");
567       }
568     }
569     /// Set asymmetric error for direction @a i
570     void setErrs(size_t i, double eminus, double eplus,  std::string source="") {
571       switch (i) {
572       case 1: setXErrs(eminus, eplus); break;
573       case 2: setYErrs(eminus, eplus); break;
574       case 3: setZErrs(eminus, eplus, source); break;
575       default: throw RangeError("Invalid axis int, must be in range 1..dim");
576       }
577     }
578     /// Set asymmetric error for direction @a i
579     void setErrs(size_t i, std::pair<double,double>& e,  std::string source="") {
580       switch (i) {
581       case 1: setXErrs(e); break;
582       case 2: setYErrs(e); break;
583       case 3: setZErrs(e, source); break;
584       default: throw RangeError("Invalid axis int, must be in range 1..dim");
585       }
586     }
587 
588     /// Set value and symmetric error for direction @a i
589     void set(size_t i, double val, double e,  std::string source="") {
590       switch (i) {
591       case 1: setX(val, e); break;
592       case 2: setY(val, e); break;
593       case 3: setZ(val, e, source); break;
594       default: throw RangeError("Invalid axis int, must be in range 1..dim");
595       }
596     }
597     /// Set value and asymmetric error for direction @a i
598     void set(size_t i, double val, double eminus, double eplus,  std::string source="") {
599       switch (i) {
600       case 1: setX(val, eminus, eplus); break;
601       case 2: setY(val, eminus, eplus); break;
602       case 3: setZ(val, eminus, eplus, source); break;
603       default: throw RangeError("Invalid axis int, must be in range 1..dim");
604       }
605     }
606     /// Set value and asymmetric error for direction @a i
607     void set(size_t i, double val, std::pair<double,double>& e,  std::string source="") {
608       switch (i) {
609       case 1: setX(val, e); break;
610       case 2: setY(val, e); break;
611       case 3: setZ(val, e, source); break;
612       default: throw RangeError("Invalid axis int, must be in range 1..dim");
613       }
614     }
615 
616     /// @}
617 
618 
619   protected:
620 
621     /// @name Value and error variables
622     /// @{
623 
624     double _x;
625     double _y;
626     double _z;
627     std::pair<double,double> _ex;
628     std::pair<double,double> _ey;
629     // a map of the errors for each source. Nominal stored under ""
630     // to ensure backward compatibility
631     std::map< std::string, std::pair<double,double> >_ez;
632 
633     /// @}
634 
635   };
636 
637 
638 
639   /// @name Comparison operators
640   /// @{
641 
642   /// Equality test of x, y & z characteristics only
643   /// @todo Base on a named fuzzyEquals(a,b,tol=1e-3) unbound function
644   inline bool operator==(const Point3D& a, const YODA::Point3D& b) {
645     if (!YODA::fuzzyEquals(a.x(), b.x()) ||
646         !YODA::fuzzyEquals(a.xErrMinus(), b.xErrMinus()) ||
647         !YODA::fuzzyEquals(a.xErrPlus(),  b.xErrPlus()) ) return false;
648     if (!YODA::fuzzyEquals(a.y(), b.y()) ||
649         !YODA::fuzzyEquals(a.yErrMinus(), b.yErrMinus()) ||
650         !YODA::fuzzyEquals(a.yErrPlus(),  b.yErrPlus()) ) return false;
651     if (!YODA::fuzzyEquals(a.z(), b.z()) ||
652         !YODA::fuzzyEquals(a.zErrMinus(), b.zErrMinus()) ||
653         !YODA::fuzzyEquals(a.zErrPlus(),  b.zErrPlus()) ) return false;
654     return true;
655 
656 
657     const bool same_val =  fuzzyEquals(a.x(), b.x()) && fuzzyEquals(a.y(), b.y());
658     const bool same_eminus =  fuzzyEquals(a.xErrMinus(), b.xErrMinus()) &&
659                               fuzzyEquals(a.yErrMinus(), b.yErrMinus());
660     const bool same_eplus =  fuzzyEquals(a.xErrPlus(), b.xErrPlus()) &&
661                              fuzzyEquals(a.yErrPlus(), b.yErrPlus());
662     return same_val && same_eminus && same_eplus;
663   }
664 
665   /// Inequality operator
666   inline bool operator != (const  Point3D& a, const YODA::Point3D& b) {
667     return !(a == b);
668   }
669 
670   /// Less-than operator used to sort bins by x-first ordering
671   inline bool operator < (const  Point3D& a, const YODA::Point3D& b) {
672     if (! fuzzyEquals(a.x(), b.x())) {
673       return a.x() < b.x();
674     }
675     if (!fuzzyEquals(a.y(), b.y())) {
676       return a.y() < b.y();
677     }
678     if (! fuzzyEquals(a.xErrMinus(), b.xErrMinus())) {
679       return a.xErrMinus() < b.xErrMinus();
680     }
681     if (!fuzzyEquals(a.yErrMinus(), b.yErrMinus())) {
682       return a.yErrMinus() < b.yErrMinus();
683     }
684     if (! fuzzyEquals(a.xErrPlus(), b.xErrPlus())) {
685       return a.xErrPlus() < b.xErrPlus();
686     }
687     if (!fuzzyEquals(a.yErrPlus(), b.yErrPlus())) {
688       return a.yErrPlus() < b.yErrPlus();
689     }
690     return false;
691   }
692 
693   /// Less-than-or-equals operator
694   inline bool operator <= (const  Point3D& a, const YODA::Point3D& b) {
695     if (a == b) return true;
696     return a < b;
697   }
698 
699   /// Greater-than operator
700   inline bool operator > (const  Point3D& a, const YODA::Point3D& b) {
701     return !(a <= b);
702   }
703 
704   /// Greater-than-or-equals operator
705   inline bool operator >= (const  Point3D& a, const YODA::Point3D& b) {
706     return !(a < b);
707   }
708 
709   /// @}
710 
711 
712 }
713 
714 #endif
715