contrib/brl/bbas/bgui/bgui_graph_tableau.cxx
Go to the documentation of this file.
00001 // This is brl/bbas/bgui/bgui_graph_tableau.cxx
00002 #include "bgui_graph_tableau.h"
00003 //:
00004 // \file
00005 #include <vcl_cmath.h> //for fabs()
00006 #include <vcl_sstream.h>
00007 #include <vnl/vnl_numeric_traits.h>
00008 #include <vnl/vnl_math.h>
00009 #include <vgui/vgui.h>
00010 #include <vgui/vgui_gl.h>
00011 #include <vgui/vgui_easy2D_tableau.h>
00012 #include <vgui/vgui_viewer2D_tableau.h>
00013 #include <vgui/vgui_shell_tableau.h>
00014 
00015 void bgui_graph_tableau::init()
00016 {
00017   tt_ = vgui_text_tableau_new();
00018   tt_->set_colour(1,1,1);
00019   easy_ = vgui_easy2D_tableau_new();
00020   easy_->set_foreground(0.0, 1.0f, 0.0);
00021   easy_->set_line_width(2.0f);
00022   n_ = 0;
00023   pos_ = 0;
00024   vals_ = 0;
00025   tic_length_ = 10.0f;
00026   left_offset_ = 75;
00027   top_offset_ = 20;
00028   n_plots_ = 1;
00029 }
00030 
00031 //========================================================================
00032 // Constructors
00033 
00034 bgui_graph_tableau::bgui_graph_tableau(const unsigned gwidth,
00035                                        const unsigned gheight) :
00036   graph_width_(gwidth), graph_height_(gheight), plot_(0)
00037 {   this->init(); }
00038 
00039 // Destructor.
00040 bgui_graph_tableau::~bgui_graph_tableau()
00041 {
00042   this->rem();
00043   this->del();
00044 }
00045 
00046 //: map the x position of a graph point to display coordinates
00047 float bgui_graph_tableau::map_x_to_display(const float xpos)
00048 {
00049   return left_offset_+ (xpos - xmin_)*xscale_;
00050 }
00051 
00052 //: map the y position of a graph point to display coordinates
00053 float bgui_graph_tableau::map_y_to_display(const float ypos)
00054 {
00055   return tic_length_ + graph_height_ - (ypos - yorigin_)*yscale_ + top_offset_;
00056 }
00057 
00058 //: find the nearest discrete y value less than or equal to ymin
00059 static float find_y_origin(const float ymin, const float yinc)
00060 {
00061   if (yinc==0)
00062     return 0;
00063   float nincs = vcl_floor(ymin/yinc);
00064   return nincs*yinc;
00065 }
00066 //-----------------------------------------------------------------------------
00067 //: returns a "nice" tic mark increment, given the scale factor between user's coordinates and screen coordinates.
00068 static float find_increment(float scale, float def = 1.0f)
00069 {
00070   if (scale <= 0)
00071     return def;
00072 
00073   float separation = 50.0f / scale;
00074 
00075   // Find increment > separation
00076 
00077   float inc = 1;
00078 
00079   while (inc < separation)
00080     inc *= 10;
00081 
00082   // Find increment so sep/10 <= inc < sep
00083 
00084   while (inc > separation)
00085     inc /= 10;
00086 
00087   // Find smallest multiple of 1, 2 or 5 * 10^k > separation
00088 
00089   if (2 * inc > separation)
00090     inc *= 2;
00091   else if (5 * inc > separation)
00092     inc *= 5;
00093   else
00094     inc *= 10;
00095 
00096   return inc;
00097 }
00098 
00099 void bgui_graph_tableau::compute_scale()
00100 {
00101   xscale_=1.0f; yscale_=1.0f;
00102   if (vcl_fabs(xmax_-xmin_)>0)
00103     xscale_ = (graph_width_-left_offset_)/(xmax_-xmin_);
00104   if (vcl_fabs(ymax_-ymin_)>0)
00105     yscale_ = (graph_height_-top_offset_)/(ymax_-ymin_);
00106 
00107   yinc_ = find_increment(yscale_);
00108   xinc_ = find_increment(xscale_);
00109   if (yinc_ == 0.0)
00110     yinc_ = 1;
00111   if (xinc_ == 0.0)
00112     xinc_ = 1;
00113   yorigin_ = find_y_origin(ymin_,yinc_);
00114 }
00115 
00116 void bgui_graph_tableau::update(vcl_vector<double> const& pos,
00117                                 vcl_vector<double> const & vals)
00118 {
00119   n_ = pos.size();
00120   pos_ = new float[n_];
00121   vals_ = new float[n_];
00122   ymin_ = xmin_ = vnl_numeric_traits<float>::maxval;
00123   ymax_ = xmax_ = -xmin_;
00124   for (unsigned i = 0; i<n_; ++i)
00125   {
00126     pos_[i]=static_cast<float>(pos[i]);
00127     xmin_ = vnl_math_min(xmin_, pos_[i]);
00128     xmax_ = vnl_math_max(xmax_, pos_[i]);
00129     vals_[i]=static_cast<float>(vals[i]);
00130     ymin_ = vnl_math_min(ymin_, vals_[i]);
00131     ymax_ = vnl_math_max(ymax_, vals_[i]);
00132   }
00133   compute_scale();
00134   draw_graph();
00135 }
00136 
00137 
00138 void bgui_graph_tableau::update(vcl_vector<float> const& pos,
00139                                 vcl_vector<float> const & vals)
00140 {
00141   n_ = pos.size();
00142   pos_ = new float[n_];
00143   vals_ = new float[n_];
00144   for (unsigned i = 0; i<n_; ++i)
00145   {
00146     pos_[i]=pos[i];
00147     xmin_ = vnl_math_min(xmin_, pos_[i]);
00148     xmax_ = vnl_math_max(xmax_, pos_[i]);
00149     vals_[i]=vals[i];
00150     ymin_ = vnl_math_min(ymin_, vals_[i]);
00151     ymax_ = vnl_math_max(ymax_, vals_[i]);
00152   }
00153   compute_scale();
00154   draw_graph();
00155 }
00156 // In the current implementation,
00157 // all plots have to have the same number of positions and values.
00158 // The vector position input is for future development where this
00159 // class takes care of the multiple position case - JLM
00160 void bgui_graph_tableau::update(vcl_vector<vcl_vector<double> > const& pos,
00161                                 vcl_vector<vcl_vector<double> >const & vals)
00162 {
00163   n_plots_ = pos.size();
00164   if (!n_plots_)
00165     return;
00166   n_ = pos[0].size();
00167   mpos_ = pos; mvals_=vals;
00168   double xmin, ymin, xmax, ymax;
00169   xmin = vnl_numeric_traits<double>::maxval;
00170   xmax = -xmin;
00171   ymin = xmin;
00172   ymax = xmax;
00173   for (unsigned p = 0; p<n_plots_; ++p)
00174     for (unsigned i = 0; i<n_; ++i)
00175     {
00176       xmin = vnl_math_min(xmin, pos[p][i]);
00177       xmax = vnl_math_max(xmax, pos[p][i]);
00178       ymin = vnl_math_min(ymin, vals[p][i]);
00179       ymax = vnl_math_max(ymax, vals[p][i]);
00180     }
00181   xmin_ = static_cast<float>(xmin);
00182   xmax_ = static_cast<float>(xmax);
00183   ymin_ = static_cast<float>(ymin);
00184   ymax_ = static_cast<float>(ymax);
00185   compute_scale();
00186   draw_multi_graph();
00187 }
00188 
00189 // Create the graph axes with tic marks. Every other tic is red and longer.
00190 void bgui_graph_tableau::draw_tics()
00191 {
00192   float xs = xmin_;
00193   float ys = yorigin_;
00194   float tl = tic_length_;
00195   unsigned ix = 0, iy = 0;
00196   //The bottom of the display
00197   float y0 = map_y_to_display(yorigin_);
00198   //The tic marks on the horizontal axis
00199   while (xs <= xmax_+xinc_)
00200   {
00201     float xd = map_x_to_display(xs);
00202     if (ix%2!=0)
00203       xtics_.push_back(easy_->add_line(xd, y0, xd, y0-tl));
00204     else
00205     {
00206       easy_->set_foreground(1.0f, 0.0, 0.0);
00207       xtics_.push_back(easy_->add_line(xd, y0, xd, y0-1.5f*tl));
00208       easy_->set_foreground(0.0f, 1.0f, 0.0);
00209     }
00210     vcl_stringstream ts;
00211     ts<<xs;
00212     vcl_string xval = ts.str();
00213     unsigned nchars = xval.size();
00214     float offset = static_cast<float>(nchars)/2;
00215     offset *= 10.0f;
00216     tt_->add(xd-offset, y0+15, xval);
00217     xs += xinc_;
00218     ++ix;
00219   }
00220   xtics_.push_back(easy_->add_line(map_x_to_display(xmin_), y0,
00221                                    map_x_to_display(xmax_+xinc_), y0));
00222   //The tic marks on the vertical axis
00223   //the vertical axis is at one tic length from left edge of graph
00224   while (ys <= ymax_+yinc_)
00225   {
00226     float yd = map_y_to_display(ys);
00227     if (iy%2!=0)
00228       ytics_.push_back(easy_->add_line(left_offset_,yd,
00229                                        tl+left_offset_,yd));
00230     else
00231     {
00232       easy_->set_foreground(1.0f, 0.0, 0.0);
00233       ytics_.push_back(easy_->add_line(left_offset_,yd,
00234                                        1.5f*tl+left_offset_,yd));
00235       easy_->set_foreground(0.0f, 1.0f, 0.0);
00236     }
00237     vcl_stringstream ts;
00238     ts<<ys;
00239     vcl_string yval = ts.str();
00240     float len = yval.size()+1;
00241      len*= 10.0f;
00242     tt_->add(left_offset_-len, yd+5.0f, yval);
00243 
00244     ys += yinc_;
00245     ++iy;
00246   }
00247   ytics_.push_back(easy_->
00248                    add_line(left_offset_,map_y_to_display(yorigin_),
00249                             left_offset_,
00250                             map_y_to_display(ymax_+yinc_)));
00251 
00252   //Make the tics and axes unselectable
00253   for (vcl_vector<vgui_soview2D_lineseg*>::iterator cit = xtics_.begin();
00254        cit!=xtics_.end(); ++cit)
00255     (*cit)->set_selectable(false);
00256   for (vcl_vector<vgui_soview2D_lineseg*>::iterator cit = ytics_.begin();
00257        cit!=ytics_.end(); ++cit)
00258     (*cit)->set_selectable(false);
00259 }
00260 
00261 //Draw the graph including the axes and tic marks
00262 void bgui_graph_tableau::draw_graph()
00263 {
00264   if (n_ == 0)
00265     return;
00266   vcl_vector<float> x(n_), y(n_);
00267   if (m_plot_.size())
00268   {
00269     for (unsigned i =0; i<m_plot_.size(); ++i)
00270       easy_->remove(m_plot_[i]);
00271     m_plot_.clear();
00272     tt_->clear();
00273   }
00274   if (plot_)
00275   {
00276     easy_->remove(plot_);
00277     tt_->clear();
00278     delete plot_;
00279     plot_ = 0;
00280   }
00281   for (unsigned i = 0; i<n_; ++i)
00282   {
00283     x[i]=map_x_to_display(pos_[i]);
00284     y[i]=map_y_to_display(vals_[i]);
00285   }
00286   plot_ = easy_->add_linestrip(n_, &x[0], &y[0]);
00287   plot_->set_selectable(false);
00288   draw_tics();
00289  this->post_redraw();
00290 }
00291 
00292 //Draw the multiple plots including the axes and tic marks
00293 void bgui_graph_tableau::draw_multi_graph()
00294 {
00295   //graph colors, where more than 7 plots repeat colors
00296   float r[7]={1,0,0,1,0,1,1};
00297   float g[7]={0,1,0,0,1,1,1};
00298   float b[7]={0,0,1,1,1,0,1};
00299   if (n_ == 0)
00300     return;
00301   if (m_plot_.size())
00302   {
00303     for (unsigned i =0; i<m_plot_.size(); ++i)
00304       easy_->remove(m_plot_[i]);
00305     m_plot_.clear();
00306     tt_->clear();
00307   }
00308   for (unsigned p = 0; p<n_plots_; ++p)
00309   {
00310     vcl_vector<float> x(n_), y(n_);
00311     for (unsigned i = 0; i<n_; ++i)
00312     {
00313       x[i]=map_x_to_display(static_cast<float>(mpos_[p][i]));
00314       y[i]=map_y_to_display(static_cast<float>(mvals_[p][i]));
00315     }
00316     unsigned c = p%7;
00317     easy_->set_foreground(r[c], g[c], b[c]);
00318     vgui_soview2D_linestrip* ls = easy_->add_linestrip(n_, &x[0], &y[0]);
00319     ls->set_selectable(false);
00320     m_plot_.push_back(ls);
00321   }
00322   draw_tics();
00323   this->post_redraw();
00324 }
00325 
00326 //remove display items and delete the soviews
00327 void bgui_graph_tableau::rem()
00328 {
00329   if (plot_)
00330   {
00331     easy_->remove(plot_);
00332     //delete plot_;
00333     plot_ = 0;
00334   }
00335   for (vcl_vector<vgui_soview2D_lineseg*>::iterator cit = xtics_.begin();
00336        cit!=xtics_.end(); ++cit)
00337     if (*cit)
00338     {
00339       easy_->remove(*cit);
00340      // delete *cit;
00341     }
00342   for (vcl_vector<vgui_soview2D_lineseg*>::iterator cit = ytics_.begin();
00343        cit!=ytics_.end(); ++cit)
00344     if (*cit)
00345     {
00346       easy_->remove(*cit);
00347       //delete *cit;
00348     }
00349   for (unsigned i = 0; i< m_plot_.size(); ++i)
00350     easy_->remove(m_plot_[i]);
00351   m_plot_.clear();
00352 }
00353 
00354 //Delete graph point data
00355 void bgui_graph_tableau::del()
00356 {
00357   if (pos_)
00358   {
00359     delete [] pos_;
00360     pos_ =0;
00361   }
00362   if (vals_)
00363   {
00364     delete [] vals_;
00365     vals_ = 0;
00366   }
00367 }
00368 
00369 //Clear the graph
00370 void bgui_graph_tableau::clear()
00371 {
00372   this->rem();
00373   this->del();
00374   this->post_redraw();
00375 }
00376 
00377 // Provide a popup dialog that contains the graph.  The string info
00378 // contains user defined documentation for the graph.
00379 vgui_dialog* bgui_graph_tableau::popup_graph(vcl_string const& info,
00380                                              const unsigned sizex,
00381                                              const unsigned sizey)
00382 {
00383   unsigned w = graph_width_+25, h = graph_height_+25;
00384   if (sizex>0)
00385     w = sizex;
00386   if (sizey>0)
00387     h = sizey;
00388   vgui_viewer2D_tableau_sptr v = vgui_viewer2D_tableau_new(this);
00389   vgui_shell_tableau_sptr s = vgui_shell_tableau_new(v);
00390   vcl_string temp = info;
00391   vcl_stringstream xinc, yinc, ymin, ymax;
00392   xinc << xinc_; yinc << yinc_; ymin << ymin_; ymax<< ymax_;
00393 #if 0
00394   temp += " xinc:" + xinc.str();
00395   temp += " yinc:" + yinc.str();
00396 #endif
00397   temp += " ymin:" + ymin.str();
00398   temp += " ymax:" + ymax.str();
00399   vgui_dialog* d = new vgui_dialog(temp.c_str());
00400   d->inline_tableau(s, w, h);
00401   return d;
00402 }
00403 
00404 //========================================================================
00405 //: Handles all events for this tableau.
00406 bool bgui_graph_tableau::handle(const vgui_event& event)
00407 {
00408   // Pass events on down to the child tableaux:
00409   if (event.type == vgui_DRAW)
00410   {
00411     vcl_cout << "Graph Handle\n";
00412     easy_->handle(event);
00413     tt_->handle(event);
00414   }
00415   return false;
00416 }
00417