00001 #pragma ident "$Id: Axis.cpp 2091 2009-08-21 14:24:47Z afarris $"
00002
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <cmath>
00028 #include <cstdio>
00029 #include "Axis.hpp"
00030
00031 namespace vplot
00032 {
00033 const double Axis::RIGHT = 0;
00034 const double Axis::UP = Axis::RIGHT+vdraw::HALF_PI;
00035 const double Axis::LEFT = Axis::UP+vdraw::HALF_PI;
00036 const double Axis::DOWN = Axis::LEFT+vdraw::HALF_PI;
00037 const double Axis::N = Axis::UP;
00038 const double Axis::E = Axis::RIGHT;
00039 const double Axis::S = Axis::DOWN;
00040 const double Axis::W = Axis::LEFT;
00041 const double Axis::NORTH = Axis::N;
00042 const double Axis::EAST = Axis::E;
00043 const double Axis::SOUTH = Axis::S;
00044 const double Axis::WEST = Axis::W;
00045
00046 void Axis::init(double ix, double iy, double length, double direction, double imin, double imax, const AxisStyle &style)
00047 {
00048 setPosition(ix,iy);
00049 setLength(length);
00050 setAngle(direction);
00051 setRange(imin,imax);
00052 axis_style = style;
00053 gap=0.0;
00054 if(direction==NORTH) axis_style.label_position=AxisStyle::ABOVE;
00055 else if(direction==EAST) axis_style.label_position=AxisStyle::BELOW;
00056 else if(direction==SOUTH) axis_style.label_position=AxisStyle::BELOW;
00057 else if(direction==WEST) axis_style.label_position=AxisStyle::ABOVE;
00058 }
00059
00060 void Axis::setAngle(double angle)
00061 {
00062
00063 line_direction = fmod(angle,vdraw::TWO_PI);
00064 if(line_direction < 0) line_direction += 360.0;
00065
00066
00067 cosdir = cos(angle);
00068 sindir = sin(angle);
00069
00070
00071 costic = cos(angle+vdraw::HALF_PI);
00072 sintic = sin(angle+vdraw::HALF_PI);
00073 }
00074
00075 bool Axis::pointFromValue(double &x, double &y, double value)
00076 {
00077 if(value < min || value > max) return false;
00078 double ratio = (value-min)/(max-min);
00079 fromRatio(x,y,ratio);
00080 return true;
00081 }
00082
00083 void Axis::drawToFrame(const vdraw::Frame &ff)
00084 {
00085 using namespace vdraw;
00086
00087
00088 this->f = ff;
00089
00090
00091 double ex,ey;
00092 fromRatio(ex,ey,1.0);
00093 f.line(Line(x, y, ex, ey, axis_style.line_style));
00094
00095
00096 if(axis_style.logarithmic)
00097 {
00098
00099 }
00100 else
00101 {
00102 double pos, val, distpos, distval;
00103 guessTickInfo(pos,val,distpos,distval);
00104 while(pos <= 1.0001)
00105 {
00106
00107 fromRatio(ex,ey,pos);
00108
00109 axisTick(pos, distpos);
00110
00111 if(axis_style.draw_labels)
00112 label(val, axis_style.label_position, axis_style.label_style);
00113
00114 pos += distpos;
00115 val += distval;
00116 }
00117
00118 }
00119
00120
00121
00122 }
00123
00124 void Axis::axisTickRecursive(bool draw, int depth, double ratio, double dist, double length)
00125 {
00126
00127 if(depth < 0) return;
00128
00129 if(ratio > 1.0001) return;
00130 if(ratio < 0.0)
00131 {
00132 if(ratio+dist < 0.0) return;
00133 draw = false;
00134 }
00135
00136 if(draw)
00137 {
00138 drawTick(ratio,length);
00139 }
00140
00141 if(axis_style.subticks_per_tick < 1)
00142 return;
00143 double t = dist / (1.0+axis_style.subticks_per_tick);
00144 axisTickRecursive(false,depth-1,ratio ,t,length*axis_style.tick_depth_multiplier);
00145 for(int i=1;i<=axis_style.subticks_per_tick;i++)
00146 axisTickRecursive(true ,depth-1,ratio+i*t,t,length*axis_style.tick_depth_multiplier);
00147 }
00148
00149 void Axis::drawTick(double ratio, double length)
00150 {
00151 double x1,y1,x2,y2;
00152 fromTic(x1,y1,x2,y2,ratio,length);
00153 f.line(vdraw::Line(x1,y1,x2,y2,axis_style.tick_style));
00154 }
00155
00156 void Axis::fromTic(double &x1, double &y1, double &x2, double &y2,
00157 double distratio, double length)
00158 {
00159 double tx,ty;
00160 fromRatio(tx,ty,distratio);
00161 bool ta = axis_style.tick_position!=AxisStyle::BELOW;
00162 bool tb = axis_style.tick_position!=AxisStyle::ABOVE;
00163 x1 = tx; x2 = tx; y1 = ty; y2 = ty;
00164 if(ta&&tb) length=length/2;
00165 if(ta)
00166 {
00167 x1 = tx+costic*length;
00168 y1 = ty+sintic*length;
00169 }
00170 if(tb)
00171 {
00172 x2 = tx-costic*length;
00173 y2 = ty-sintic*length;
00174 }
00175 }
00176
00177 void Axis::guessTickInfo(double &startpos, double &startval, double &distpos, double &distval)
00178 {
00179 double tickdx;
00180
00181 if(gap<=0.0)
00182 tickdx = pow(10.0,floor(log10(fabs(max-min))));
00183 else
00184 tickdx = gap;
00185
00186 if (fmod(max,tickdx)!= 0)
00187 {
00188 if (max>0)
00189 axesMax = max-fmod(max,tickdx)+tickdx;
00190 else
00191 axesMax = max-fmod(max,tickdx);
00192 }
00193 else
00194 axesMax = max;
00195 if (fmod(min,tickdx) != 0)
00196 {
00197 if (min<0)
00198 axesMin = min-fmod(min,tickdx)-tickdx;
00199 else
00200 axesMin = min-fmod(min,tickdx);
00201 }
00202 else
00203 axesMin = min;
00204
00205 double w = max-min;
00206 double aw = axesMax-axesMin;
00207
00208 startpos = 0.0;
00209 startval = axesMin;
00210 distpos = tickdx/aw;
00211 distval = tickdx;
00212
00213
00214 if(axis_style.tight_bounds)
00215 {
00216 startpos = (axesMin-min)/w;
00217 distpos = tickdx/w;
00218 }
00219 else
00220 {
00221 max = axesMax;
00222 min = axesMin;
00223 w = aw;
00224 }
00225 if((w / tickdx) < 4 )
00226 {
00227 distpos /= 4;
00228 distval /= 4;
00229 }
00230 }
00231
00232 bool Axis::label(double value, int direction, const vdraw::TextStyle &style)
00233 {
00234 using namespace vdraw;
00235 double x,y,rotation;
00236 if(!labelPoint(x,y,rotation,value,direction)) return false;
00237 char buffer [33];
00238 int n=-1;
00239 if(axis_style.label_format.size() > 0)
00240 {
00241 n = std::sprintf(buffer,axis_style.label_format.c_str(),value);
00242
00243 }
00244 const char * c;
00245 if(n<0)
00246 {
00247 double pval = (value<0?-value:value);
00248 if(pval == 0) c="0";
00249 else if(pval >= 100000) c="%4g";
00250 else if(pval >= 1) c="%g";
00251 else if(pval >= 0.1) c="%g";
00252 else if(pval >= 0.01) c="%g";
00253 else c="%4.e";
00254
00255
00256 }
00257 std::sprintf(buffer,c,value);
00258 std::string textString = std::string(buffer);
00259 Text t(textString.c_str(),x,y,style);
00260 return label(t,value,direction,style);
00261 }
00262
00263 bool Axis::label(const char *str, double value, int direction, const vdraw::TextStyle &style)
00264 {
00265 using namespace vdraw;
00266 double x,y,rotation;
00267 if(!labelPoint(x,y,rotation,value,direction)) return false;
00268 Text t(str,x,y,style);
00269 return label(t,value,direction,style);
00270 }
00271
00272 bool Axis::label(vdraw::Text &t, double value, int direction, const vdraw::TextStyle &style)
00273 {
00274 using namespace vdraw;
00275
00276
00277
00278
00279 short quad = 0;
00280 if(sindir>0)
00281 {
00282 if(cosdir>0) quad = 1;
00283 else quad = 2;
00284 }
00285 else
00286 {
00287 if(cosdir>0) quad = 4;
00288 else quad = 3;
00289 }
00290 bool above = direction==AxisStyle::ABOVE;
00291
00292 if(line_direction == UP || line_direction == DOWN)
00293 {
00294
00295
00296 if(cos(line_direction-(direction*vdraw::HALF_PI)) > 0)
00297 t.setAlignment(Text::LEFT);
00298 else
00299 t.setAlignment(Text::RIGHT);
00300
00301 t.setPosition(t.x,t.y-f.up()*style.getPointSize()/2);
00302 }
00303 else if(line_direction == LEFT || line_direction == RIGHT)
00304 {
00305
00306
00307 t.setAlignment(Text::CENTER);
00308
00309 if(sindir < 0.0 || (sindir == 0.0 && direction == AxisStyle::BELOW))
00310 t.setPosition(t.x,t.y-f.up()*style.getPointSize());
00311 }
00312 else
00313 {
00314
00315
00316
00317
00318
00319 if(false)
00320 {
00321 bool bumpdown = false;
00322
00323 switch(quad)
00324 {
00325 case 1:
00326 if(above) { t.setAlignment(Text::RIGHT); }
00327 else { t.setAlignment(Text::LEFT); bumpdown=true; }
00328 break;
00329 case 2:
00330 if(above) { t.setAlignment(Text::RIGHT); bumpdown=true;}
00331 else { t.setAlignment(Text::LEFT); }
00332 break;
00333 case 3:
00334 if(above) { t.setAlignment(Text::LEFT); bumpdown=true; }
00335 else { t.setAlignment(Text::RIGHT); }
00336 break;
00337 case 4:
00338 if(above) { t.setAlignment(Text::LEFT); }
00339 else { t.setAlignment(Text::RIGHT); bumpdown=true; }
00340 break;
00341 }
00342 if(bumpdown) t.setPosition(t.x,t.y-f.up()*style.getPointSize());
00343 }
00344 else
00345 {
00346
00347 }
00348 }
00349 f.text(t);
00350 return true;
00351 }
00352
00353 bool Axis::labelPoint(double &x, double &y, double &rotation, double value, int direction)
00354 {
00355 if(!pointFromValue(x,y,value)) return false;
00356 double ticklen = axis_style.major_tick_length;
00357 if(axis_style.tick_position == AxisStyle::CENTER) ticklen/=2;
00358
00359 if((direction==AxisStyle::ABOVE && axis_style.tick_position==AxisStyle::BELOW) ||
00360 (direction==AxisStyle::BELOW && axis_style.tick_position==AxisStyle::ABOVE))
00361 ticklen = 0.4*axis_style.label_style.getPointSize();
00362 else
00363 ticklen += 3;
00364 x += (direction==AxisStyle::ABOVE?1:-1)*costic*ticklen*1.2;
00365 y += f.up()*(direction==AxisStyle::ABOVE?1:-1)*sintic*ticklen*1.2;
00366 rotation = line_direction-vdraw::HALF_PI;
00367
00368
00369
00370 return true;
00371 }
00372
00373 }
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399