Axis.cpp

Go to the documentation of this file.
00001 #pragma ident "$Id: Axis.cpp 2091 2009-08-21 14:24:47Z afarris $"
00002 
00004 
00005 //============================================================================
00006 //
00007 //  This file is part of GPSTk, the GPS Toolkit.
00008 //
00009 //  The GPSTk is free software; you can redistribute it and/or modify
00010 //  it under the terms of the GNU Lesser General Public License as published
00011 //  by the Free Software Foundation; either version 2.1 of the License, or
00012 //  any later version.
00013 //
00014 //  The GPSTk is distributed in the hope that it will be useful,
00015 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 //  GNU Lesser General Public License for more details.
00018 //
00019 //  You should have received a copy of the GNU Lesser General Public
00020 //  License along with GPSTk; if not, write to the Free Software Foundation,
00021 //  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //  
00023 //  Copyright 2004, The University of Texas at Austin
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     // Save the angle, normalized to be between 0 and 360
00063     line_direction = fmod(angle,vdraw::TWO_PI);
00064     if(line_direction < 0) line_direction += 360.0;
00065 
00066     // Direction of the line, lets keep cos/sin to a minimum
00067     cosdir = cos(angle);
00068     sindir = sin(angle);
00069 
00070     // Ticks are 90 degrees rotated
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     // Set up the frame
00088     this->f = ff;
00089 
00090     // Draw the base line.
00091     double ex,ey;
00092     fromRatio(ex,ey,1.0);
00093     f.line(Line(x, y, ex, ey, axis_style.line_style));
00094 
00095     // Draw the tics and labels
00096     if(axis_style.logarithmic)
00097     {
00098       // TODO Draw logarithmic ticks/labels
00099     }
00100     else
00101     {
00102       double pos, val, distpos, distval;
00103       guessTickInfo(pos,val,distpos,distval);
00104       while(pos <= 1.0001)
00105       {
00106         // FIXME remove til next comment
00107         fromRatio(ex,ey,pos);
00108         // Draw necessary ticks
00109         axisTick(pos, distpos);
00110         // Draw label
00111         if(axis_style.draw_labels)
00112           label(val, axis_style.label_position, axis_style.label_style);
00113         // Increment
00114         pos += distpos;
00115         val += distval;
00116       }
00117       //std::cout << "fail pos:" << pos << std::endl;
00118     }
00119 
00120     // Forget the frame. Or remember for optional calls?
00121     //this->f = 0;
00122   }
00123 
00124   void Axis::axisTickRecursive(bool draw, int depth, double ratio, double dist, double length)
00125   {
00126     // Base case: 
00127     if(depth < 0) return;
00128     // Bounds checking
00129     if(ratio > 1.0001) return;
00130     if(ratio < 0.0)
00131     {
00132       if(ratio+dist < 0.0) return;
00133       draw = false;
00134     }
00135     // Draw
00136     if(draw)
00137     {
00138       drawTick(ratio,length);
00139     }
00140     // Recurse
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     // See function header comments about these values.
00208     startpos = 0.0;
00209     startval = axesMin;
00210     distpos = tickdx/aw;
00211     distval = tickdx;
00212     //printf("--><%f,%f> (%f,%f) [%f,%f,%f,%f] %f\n",
00213     //  min,max,axesMin,axesMax,startpos,startval,distpos,distval,tickdx);
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       // This returns -1 if it fails, i.e. label_format was bad.
00243     }
00244     const char * c;
00245     if(n<0) // No label_format, or it failed
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";  //c="%4.f";
00251       else if(pval >= 0.1)    c="%g";  //c="%.1f";
00252       else if(pval >= 0.01)   c="%g";  //c="%.2f";
00253       else                    c="%4.e";  
00254       // FIXME Fix labels for values with small later numbers, for example if
00255       // value is like 1.3003003003 and want it displayed as 1.3
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     // Quadrant
00276     //  2 | 1
00277     // ---|---
00278     //  3 | 4 
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       // Simply draw it to the left or right, horizontal
00295       // Set alignment
00296       if(cos(line_direction-(direction*vdraw::HALF_PI)) > 0) 
00297         t.setAlignment(Text::LEFT);
00298       else
00299         t.setAlignment(Text::RIGHT);
00300       // Bump down some based on text height
00301       t.setPosition(t.x,t.y-f.up()*style.getPointSize()/2); 
00302     }
00303     else if(line_direction == LEFT || line_direction == RIGHT)
00304     {
00305       // Simply draw it above or below, horizontal, centered
00306       // Set alignment
00307       t.setAlignment(Text::CENTER);
00308       // Bump down some based on text height
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       // Either make the text horizontal regardless of the line's directions or
00315       // angle the text to be 90 degrees off the direction of the line, in the
00316       // direction specified.  Make sure text is right-side-up. 
00317       // FIXME If above is made to mean "Above on the page" then...
00318       // if(cosdir<0) above = !above;
00319       if(false) // axis_style.label_horizontal
00320       {
00321         bool bumpdown = false;
00322         // Horizontal labels
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         // TODO Angled labels
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     // Adjust these as necessary... to get point away from the ticks themselves.
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     // Determine optimal position based on which way the text will be 
00368     // Option for flat (horizontal on page) text vs angled?
00369     // TODO Angled things need changes?
00370     return true; 
00371   }
00372 
00373 }
00374 
00375 /* 
00376  * New (and improved!) notes on drawing ticks:
00377  * - Draw line
00378  * - Determine:
00379  *   * tick start position 
00380  *   * distance between ticks
00381  * - Recursively draw ticks
00382  *   * (Logarithmic version comes later)
00383  *
00384  * - Misc:
00385  *   * (?) Remember (or be able to recreate) tick positions
00386  *     in order to draw grid lines?  
00387  *     - This is most important with logarithmic plots
00388  *     - Potentially useful otherwise
00389  *     
00390  * - Grid notes:
00391  *   * Depth?
00392  *   * How long to draw them
00393  *   * StrokeStyle for each depth?  
00394  *     ~ Some dashed 
00395  *     ~ Some lines
00396  *     ~ Colors are important (blend with BG)
00397  *
00398  */
00399 

Generated on Thu Jul 29 03:30:51 2010 for GPS ToolKit Software Library by  doxygen 1.3.9.1