|
CLAM-Development
1.3
|
00001 #ifndef RulerTicks_hxx 00002 #define RulerTicks_hxx 00003 00004 #include <cmath> 00005 #include "Assert.hxx" 00006 namespace CLAM 00007 { 00008 00009 /* 00010 Implements the logic to place graphically several markers 00011 in a ruler so that the markers are placed with a given 00012 minimal separation and corresponds to round numbers 00013 on the mapped domain interval. 00014 So, given a domain value range, a pixel size and a minimal pixel gap, 00015 it gives you the domain offset for the first tick and the intertick gap 00016 also in domain units. 00017 Chosen round numbers are series so that the last non zero 00018 decimal steps in 1, 2 or 5 jumps and include 0. For instance: 00019 - 3.1, 3.2, 3.3... 00020 - 22, 24, 26... 00021 - 0.35, 0.40, 0.45... 00022 */ 00023 class RulerTicks 00024 { 00025 mutable bool _needsUpdate; 00026 00027 double _min; 00028 double _max; 00029 double _minGap; 00030 double _width; 00031 00032 mutable double _markOffset; 00033 mutable double _markGap; 00034 public: 00035 RulerTicks() 00036 : _needsUpdate(true) 00037 , _min(0) 00038 , _max(1) 00039 , _minGap(5) 00040 , _width(100) 00041 , _markOffset(666) 00042 , _markGap(69) 00043 { 00044 } 00046 void setRange(double min, double max) 00047 { 00048 CLAM_ASSERT(min<max, "RulerTicks: Empty or inverse order range"); 00049 _min = min; 00050 _max = max; 00051 _needsUpdate = true; 00052 } 00054 void setWidth(double width) 00055 { 00056 CLAM_ASSERT(width>0, "RulerTicks: Pixel width should be greater than zero"); 00057 _width = width; 00058 _needsUpdate = true; 00059 } 00061 void setMinGap(double minGap) 00062 { 00063 _minGap = minGap; 00064 _needsUpdate = true; 00065 } 00066 double markOffset() const 00067 { 00068 if (_needsUpdate) update(); 00069 return _markOffset; 00070 } 00071 double markGap() const 00072 { 00073 if (_needsUpdate) update(); 00074 return _markGap; 00075 } 00076 void update() const 00077 { 00078 double mappedMinGap = std::fabs(_minGap*(_max-_min)/_width); 00079 _markGap=1; 00080 if (mappedMinGap>=1) 00081 { 00082 while (true) 00083 { 00084 if (_markGap>=mappedMinGap) break; 00085 if (_markGap*2>=mappedMinGap) {_markGap*=2; break;} 00086 if (_markGap*5>=mappedMinGap) {_markGap*=5; break;} 00087 _markGap*=10; 00088 } 00089 } 00090 else 00091 { 00092 while (true) 00093 { 00094 if (_markGap<1e-8) {_markGap=1e-8; break;} 00095 if (_markGap<mappedMinGap) {break;} 00096 if (_markGap/2<mappedMinGap) {_markGap/=2; break;} 00097 if (_markGap/5<mappedMinGap) {_markGap/=5; break;} 00098 _markGap/=10; 00099 } 00100 } 00101 double ceil = std::ceil(_min/_markGap); 00102 _markOffset = (std::fabs(ceil)<1e-5)? 0. : ceil*_markGap; 00103 00104 _needsUpdate = false; 00105 } 00107 double tickValue(unsigned i) const 00108 { 00109 if (_needsUpdate) update(); 00110 return _markOffset+_markGap*i; 00111 } 00113 double pixelTickPos(unsigned i) const 00114 { 00115 return toPixel(tickValue(i)); 00116 } 00117 // Returns the pixel position of a given domain value 00118 double toPixel(double value) const 00119 { 00120 return (value-_min)*_width/(_max-_min); 00121 } 00122 // Returns the number of ticks that will appear 00123 unsigned nTicks() const 00124 { 00125 if (_needsUpdate) update(); 00126 return std::ceil((_max-_markOffset)/_markGap); 00127 } 00128 }; 00129 00130 } // namespace CLAM 00131 00132 #endif//RulerTicks_hxx 00133
1.7.6.1