Region.hh

Deil Christoph, 10/11/2012 01:53 PM

Download (16 KB)

 
1
#ifndef ROOT_Tools_Region
2
#define ROOT_Tools_Region
3

    
4
#include <string>
5
#include <vector>
6
#include <TPolyLine.h>
7
#include <TH1D.h>
8
#include <TH2D.h>
9
#include <stash/Coordinate.hh>
10
#include <stash/Lambda.hh>
11

    
12
namespace Sash {
13
  class Time;
14
}
15

    
16
namespace Tools
17
{
18
  class Circle;
19
  class Box;
20

    
21
  /**
22
   * \brief Base class for all regions.
23
   * 
24
   * This is a base class that defines a physical region on the sky,
25
   * with methods to test if a point is inside (Region::Contains(
26
   * point )) or if regions overlap (Region::Overlaps(region )). The
27
   * base-class is abstract, and defines a set of virtual functions
28
   * that may be implemented by sub-classes, such as Tools::Box, or Tools::Circle.
29
   *
30
   * Regions can be:
31
   *  - rotated (about an arbitrary point) via SetRotation()
32
   *  - inverted (rejecting points inside and accepting points outside)  via SetInverted()
33
   *  - grouped into Tools::Compound regions (using inverted regions, you may construct complex shapes)
34
   * 
35
   * \note Since Tools::Region defines an internal rotation transformation
36
   * (about an arbitrary coordinate), sub-classes do not need to deal
37
   * with rotations directly, and can assume that the region is fixed at the
38
   * center position and aligned with the X-Y plane. 
39
   *
40
   * \note that this means that internally, fCentre can be assumed to
41
   * be unrotated, and should be used in methods like IsInside(),
42
   * while GetCentre() returns the true region center on the sky
43
   * (after applying the rotation transformation).
44
   *
45
   * \ingroup utilities
46
   *
47
   * \author Heidelberg team + CEA team
48
   */
49
  class Region : public TNamed, public TAttLine, public TAttFill
50
  {
51

    
52
  public:
53

    
54
    typedef std::vector<const Tools::Region*> RegionList;
55
    typedef std::vector<const Tools::Region*>::const_iterator RegionListIterator;
56
    typedef std::vector< std::vector< Stash::Coordinate > > VertexList;
57

    
58
    Region(const Stash::Coordinate& cor =
59
           Stash::Coordinate(0,0,Stash::System::GetDummySystem()),
60
           std::string name = "Region", 
61
           std::string type = "",
62
           Stash::Lambda phi = Stash::Lambda(0,Stash::Angle::Degrees));
63

    
64
    virtual ~Region(){;}
65

    
66
    double GetDistanceToCentre(const Stash::Coordinate& cor) const;
67

    
68
    /**
69
     * \brief Get the true center (after rotation is applied) of the region.
70
     *
71
     * This is the inverse-transform of fCentre.
72
     */
73
    Stash::Coordinate GetTrueCentre() const {return InverseTransform(fCentre);}
74

    
75
    /**
76
     * \brief Get center of the region (before rotation is applied, as given during construction).
77
     */
78
    Stash::Coordinate GetCentre() const {return fCentre;}
79

    
80
    /**
81
     * \brief Get coordinate system of the region center.
82
     */
83
    const Stash::SystemRef& GetSystem() const { return fCentre.GetSystem(); }
84

    
85
    /**
86
     * \brief Move the region to another part of the sky.
87
     */
88
    virtual void MoveTo(Stash::Coordinate newcen) {
89
      // generally move the center (doesn't work the same for some
90
      // region subtypes though, e.g. polygons where the center is not
91
      // strictly relavant). For those, it must be re-implemented
92
      fCentre = newcen.GetCoordinate(*fCentre.GetSystem()); 
93
    }
94

    
95
    bool Overlaps(const Tools::Region& reg) const;
96

    
97
    /**
98
     * \brief String representation of region type.
99
     */
100
    std::string GetType() { return fType; }
101
    double GetEquivalentCircleRadius() const;
102

    
103
    /**
104
     * \brief accuracy is used in calculations that sample points over
105
     * the region (e.g. to calculate overlaps or zenith distributions,
106
     * etc). Increasing it speeds things up at the expense of some accuracy.
107
     */
108
    void SetAccuracy( double maxerror ) { fAccuracy= maxerror;}
109

    
110
    Stash::Lambda GetRotationAngle() const { return fRotationAngle; }
111
    Stash::Coordinate GetRotationPoint() const { return fRotationPoint; }
112

    
113

    
114
    void SetRotation( Stash::Lambda phi=Stash::Lambda(0,Stash::Angle::Degrees), 
115
                      Stash::Coordinate point=Stash::Coordinate(0,0,Stash::System::GetDummySystem()) );
116

    
117
    /**
118
     * Control whether region is inverted or not (inverted means
119
     * points outside will return true Contains())
120
     */
121
    void SetInverted(bool inv=true) { fIsInverted=inv; } 
122
    bool IsInverted() const { return fIsInverted;}
123

    
124
    /**
125
     * \brief Grow the boundary of the region by a given amount of space angle.
126
     *
127
     * \note Has to be implemented by subclasses.
128
     */
129
    virtual Tools::Region* GrowBoundary(const double degrees,
130
                                        const std::string name="_grown") const =0;
131

    
132
    
133
    bool Contains(const Stash::Coordinate& dir) const;
134
    virtual bool IsCompletelyContained(const Tools::Region& reg) const;
135
    virtual Stash::Coordinate GetCentreOfGravity() const;
136

    
137
    // These Fill functions are used by the BgStats class to fill the
138
    // expected distributions of events in zenith/offset/azimuth.
139
    virtual void FillOffsetDistribution(const Stash::Coordinate& pos,
140
                                        TH1 &, double psicut=-1 ) const;
141
    virtual void FillZenithOrAzimuthDistribution( TH1 &, const Sash::Time, 
142
                                                  const Sash::Time, bool fillZenith=true) const;
143
    virtual void FillZenithOffsetDistribution(const Stash::Coordinate& pos,
144
                                              TH2 &, const Sash::Time, const Sash::Time, 
145
                                              double psicut=-1 ) const;
146
    virtual void Fill2AxesDistanceDistribution(const Stash::Coordinate& pos,
147
                                               TH2 &, double psicut=-1 ) const;
148
    virtual double GetArea() const =0;  ///< Returns area of region in square degrees
149
    virtual double GetBound2() const=0; ///< Return width of bounding box
150
    virtual double GetBound1() const=0; ///< Return length of bounding box
151
    virtual double GetRadialBound()const=0; ///< Return radius of circle containing reg
152

    
153
    Tools::Box* GetBoundingBox() const;
154
    Tools::Circle* GetBoundingCircle() const;
155

    
156
    double GetMonteCarloArea(int samples_per_square_deg=1e6) const;
157

    
158
    /**
159
     * \brief Return FITS region string representation of this region.
160
     *
161
     * Subclasses should implement this such that it returns a valid
162
     * FITS region string. Used by the output streamer and Print()
163
     */
164
    virtual std::string AsFITSRegionString() const {
165
      return "# "+ std::string(GetName())+ ": AsFITSRegionString() is not "
166
        "implemented for this region type ";
167
    }
168

    
169
    /**
170
     * \brief Return VertexList representation of this region.
171
     *
172
     * To be implemented by subclasses: shold return a
173
     * representation/approximatin of the region as a continuous list
174
     * of coordinates that follows the boundaries of the region (this
175
     * is used for drawing the regions in the correct transformation)
176
     *
177
     * The vertex list returned is in the native system of the region. 
178
     */
179
    virtual VertexList AsVertexList() const{
180
      VertexList blanklist;
181
      return blanklist;
182
    };
183

    
184

    
185
  protected:
186
    Stash::Coordinate fCentre; ///< Centre of Region
187
    std::string GetFITSSystemName() const;
188
    double fAccuracy; ///< accuracy of grid or monte-carlo operations 1.0=normal, larger is more accurate (more steps used in loops)
189

    
190
    Stash::Coordinate Transform( const Stash::Coordinate &src ) const;
191
    Stash::Coordinate InverseTransform( const Stash::Coordinate &src ) const;
192
    Stash::Coordinate GetPointAtOffsetFromCenter( double offsX, double offsY ) const ;
193
    void GetOffsetOfPointFromCenter( Stash::Coordinate point, double &offsX, double &offsY, bool untransformed=false ) const ;
194
    Stash::Coordinate GetPointAtRadius( Stash::Beta rad, Stash::Lambda phi ) const;
195

    
196
    /**
197
     * Returns the optimal number of spatial steps for a region of a
198
     * given size, taking into accunt the accuracy leve; \param
199
     * radius: approximate radius of region in deg
200
     */
201
    int GetNumSpaceSteps(double radius) const { 
202
      return 100*2.0*radius*fAccuracy;
203
    }
204

    
205
    /**
206
     * Returns the optimal number of spatial steps for looping over a
207
     * time range
208
     */
209
    int GetNumTimeSteps() const {
210
      return 20*fAccuracy;
211
    }
212

    
213

    
214
    /**
215
     * \brief Check if dir is inside this region.
216
     *
217
     * Must be implemented by the sub-classes. This function should
218
     * return true if the point is inside the *un-rotated* region (the
219
     * rotation is taken into account automatically by Contains()
220
     */
221
    virtual bool IsInside( const Stash::Coordinate& dir ) const = 0;    
222

    
223

    
224
  private:
225

    
226
    Stash::Coordinate fRotationPoint; ///< Center of rotation for Transform()
227
    Stash::Lambda fRotationAngle; ///< Rotation Angle for Transform()
228

    
229
    std::string fType; ///< type name
230
    bool fIsInverted; ///< is this a negative region? (Contains() gives points outside)
231

    
232
    ClassDef(Tools::Region, 5);
233

    
234
  };
235

    
236
  /**
237
   * Defines a segment of an annulus.
238
   *
239
   * The annulus is defined by inner (r1) and outer (r2) radius. Radii are
240
   * in sky degrees.
241
   * The azimuthal coverage of the segment is given by two angles (restricted
242
   * in range as 0 <= phi <= 360 deg). 
243
   *
244
   * Note that a segment is always defined counter-clockwise, i.e. phi2 > phi1
245
   * in general. Zero crossing is possible, e.g. a half ring in the north is
246
   * defined by setting phi1=270, phi2=90 (phi2 < phi1 in this case).
247
   *
248
   * A full ring can be defined by setting phi1=0, phi2=360.
249
   */
250
  class CircleSegment: public Region
251
  {
252

    
253

    
254
  public:
255
    ~CircleSegment();
256
    CircleSegment(const Stash::Coordinate& cor =
257
                  Stash::Coordinate(0,0,Stash::System::GetDummySystem()),
258
                  double r1 = 0.,double r2 = 0., 
259
                  double phi1 = 0., double phi2 = 360.,
260
                  std::string name = "Segment");
261

    
262
    double GetR1() const {return fR1;};
263
    double GetR2() const {return fR2;};
264
    double GetPhi1() const {return fPhi1;};
265
    double GetPhi2() const {return fPhi2;};
266
  
267
    void SetR1(double val){fR1 = val;}
268
    void SetR2(double val){fR2 = val;}
269
    virtual void SetPhi1(double val){fPhi1=val;}
270
    virtual void SetPhi2(double val){fPhi2=val;}
271
    
272
    virtual Tools::CircleSegment* GrowBoundary(const double degrees,
273
                                               const std::string name="_grown") const;
274

    
275
    virtual double GetArea() const;
276

    
277
    virtual double GetBound2() const { return fR2*2.0; } 
278
    virtual double GetBound1() const { return fR2*2.0/Transform(fCentre).GetBeta().Cos(); }
279

    
280
    virtual double GetRadialBound() const { return fR2; }
281

    
282
    virtual std::string AsFITSRegionString() const;
283
    virtual VertexList AsVertexList() const;
284

    
285
  protected:
286
    virtual bool IsInside(const Stash::Coordinate& dir) const;
287

    
288
  private:
289

    
290
    double fR1; 
291
    double fR2; 
292
    double fPhi1;
293
    double fPhi2;
294
    mutable TPolyLine* fLine; //!
295

    
296
    ClassDef(Tools::CircleSegment, 3);
297
  };
298

    
299
  /**
300
   * A Ring with inner and outer radius. 
301
   * 
302
   * Same as a CircleSegment from phi=0-360, but with some speed optimizations.
303
   */
304
  class Ring : public CircleSegment {
305

    
306
  public:
307
    Ring(const Stash::Coordinate &pos 
308
         = Stash::Coordinate(0,0,Stash::System::GetDummySystem()),
309
         double r1=0, double r2=0, std::string name = "Ring") 
310
      : CircleSegment( pos, r1, r2, 0.0, 360.0, name ) {
311
      
312
    }
313

    
314
    void SetPhi1(double ) { 
315
      //      std::cout << "Ring: Can't set Phi1 for a Ring!" << std::endl;
316
    } 
317
    void SetPhi2(double ) { 
318
      //      std::cout << "Ring: Can't set Phi2 for a Ring!" << std::endl;
319
    } 
320
    
321
    virtual std::string AsFITSRegionString() const;
322

    
323
  protected:
324
    virtual bool IsInside( const Stash::Coordinate& dir ) const;
325
    
326
    ClassDef(Tools::Ring, 1);
327
  };
328

    
329

    
330
  /**
331
   * A circular region.
332
   *
333
   * Same as Ring, but with r1=0 for speed optimisations.
334
   */
335
  class Circle : public Ring {
336
  public:
337
    Circle(const Stash::Coordinate &pos 
338
           = Stash::Coordinate(0,0,Stash::System::GetDummySystem()),
339
         double radius=0, std::string name = "Ring") 
340
      : Ring( pos, 0.0, radius, name ) {
341
      
342
    }
343
    virtual std::string AsFITSRegionString() const;
344

    
345
  protected:
346
    virtual bool IsInside( const Stash::Coordinate& dir ) const;
347

    
348
    
349
    ClassDef(Tools::Circle, 1);
350
  };
351

    
352

    
353

    
354
  /**
355
   * Defines a rectangular region. L1 and L2 are the length and width
356
   * of the region.
357
   */
358
  class Box: public Region
359
  {
360

    
361
  public:
362

    
363
    ~Box();
364
     Box(const Stash::Coordinate& cor =
365
         Stash::Coordinate(0,0,Stash::System::GetDummySystem()),
366
         double l1 = 0.,double l2 = 0., 
367
         Stash::Lambda phi = Stash::Lambda(0,Stash::Angle::Degrees),
368
         std::string name = "Box");
369
 
370
     virtual VertexList AsVertexList() const;
371
 
372
     double GetL1() const {return fL1;};
373
     double GetL2() const {return fL2;};
374
     void SetL1(double val){fL1 = val;};
375
     void SetL2(double val){fL2 = val;};
376
 
377
    virtual Tools::Box* GrowBoundary(const double degrees,
378
                                     const std::string name="_grown") const;
379
    virtual double GetArea() const { return fL1*fL2; }
380
    virtual double GetRadialBound() const { 
381
      return sqrt(fL1*fL1/4.0+fL2*fL2/4.0); 
382
    }    
383
    
384
    virtual double GetBound1() const;
385
    virtual double GetBound2() const;
386

    
387
    Stash::Coordinate GetRandomSample();
388

    
389
    virtual std::string AsFITSRegionString() const;
390

    
391
   protected:
392
    virtual bool IsInside(const Stash::Coordinate& dir) const;
393
    
394
  private:
395
    
396

    
397
    double fL1;        /// length in degrees
398
    double fL2;        /// width in degrees
399

    
400
    ClassDef(Tools::Box, 4);
401

    
402
  };
403

    
404
  /**
405
   * A region which is a union of multiple simple regions. Compounds
406
   * can be thought of as region "groups", and may contain secondary
407
   * Compounds as well. This allows a hierarchy of regions to be
408
   * defined. Like all Regions, each Compound can be rotated (which
409
   * will rotate all regions inside the compound as a single group).
410
   *
411
   * \todo implement a method to add a negative region AddNegativeRegion() to allow
412
   * constructive geometry.
413
   */
414
  class Compound : public Region 
415
  {
416
    
417
  public:
418
    ~Compound() {;}
419

    
420
    Compound( std::string name = "Compound",const Stash::Coordinate& cor=
421
              Stash::Coordinate(0,0,Stash::System::GetDummySystem()) )
422
      : Region(cor, name){;}
423

    
424
    Compound* Clone(const char* newname) const;
425
    void AddRegion( const Tools::Region *reg );
426
    void Reset();
427
    void ClearRegionList() { fRegionList.clear(); }
428

    
429
    virtual Tools::Compound* GrowBoundary(const double degrees,
430
                                          const std::string name="_grown") const;
431

    
432
    virtual VertexList AsVertexList() const;
433
    virtual bool IsCompletelyContained(const Tools::Region& reg) const;
434

    
435
    virtual void FillZenithOffsetDistribution(const Stash::Coordinate& pos,
436
                                              TH2 & , const Sash::Time, 
437
                                              const Sash::Time, 
438
                                              double psicut=-1.) const;
439
    virtual void FillZenithOrAzimuthDistribution( TH1 &, const Sash::Time, 
440
                                                  const Sash::Time,
441
                                                  bool fillZenith=true) const;
442
    virtual void FillOffsetDistribution(const Stash::Coordinate& pos, TH1&, double psicut=-1.) const;
443
    virtual double GetArea() const;
444
    virtual double GetBound2() const;
445
    virtual double GetBound1()const;
446
    virtual double GetRadialBound() const;
447
    virtual std::string AsFITSRegionString() const;
448
    
449
    RegionList GetRegions() const {return fRegionList;};
450
    
451
    int GetNumRegions() const { return fRegionList.size(); }
452
    bool IsEmptyRegion() const { return (GetNumRegions() == 0); }
453

    
454
    
455
    virtual void MoveTo( Stash::Coordinate newpos );
456
    /**
457
     * Move the reference position to the Center-of-gravity
458
     * (doesn't move the region itself, just the point from which
459
     * bounding boxes are calculated.) Use MoveTo() instead to move the region
460
     */
461
    void RecenterOnCOG() { fCentre = GetCentreOfGravity(); }
462
    void Recenter(Stash::Coordinate refpos) { fCentre = refpos; }
463

    
464

    
465
  protected:
466
    virtual bool IsInside( const Stash::Coordinate &dir ) const;
467
    RegionList fRegionList;
468

    
469

    
470
  private:
471

    
472
    ClassDef(Tools::Compound, 3);
473

    
474
  };
475

    
476

    
477
  /**
478
   * A Compound, where Contains() only returns points common to all
479
   * regions inside it.  
480
   *
481
   * \note that currently the AsVertexList() method
482
   * (and thus drawing on a SkyHist) draws all the sub-regions, not
483
   * just the intersection.
484
   */
485
  class CompoundIntersection : public Compound
486
  {
487
  public:
488
    CompoundIntersection(std::string name = "Compound",const Stash::Coordinate& cor=
489
                         Stash::Coordinate(0,0,Stash::System::GetDummySystem()))
490
      : Compound( name, cor ) { ; } 
491
    virtual ~CompoundIntersection(){;}
492
    
493
  protected:
494
    virtual bool IsInside( const Stash::Coordinate &dir ) const;
495

    
496
    ClassDef(Tools::CompoundIntersection, 0);
497
  };
498

    
499

    
500

    
501

    
502
}
503

    
504

    
505
std::ostream& operator<<( std::ostream &stream, 
506
                          const Tools::Region& );
507

    
508

    
509

    
510

    
511

    
512

    
513

    
514

    
515

    
516

    
517

    
518

    
519
#endif