00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "plotter.h"
00029 #include "display_internal.h"
00030
00031 #include <limits>
00032 #include <iostream>
00033 #include <fstream>
00034 #include <iomanip>
00035 #include <boost/unordered_map.hpp>
00036
00037 using namespace std;
00038
00039 namespace pangolin
00040 {
00041
00042 extern __thread PangolinGl* context;
00043
00044 static void* font = GLUT_BITMAP_HELVETICA_12;
00045
00046 DataSequence::DataSequence(unsigned int buffer_size, unsigned size, float val )
00047 : y(buffer_size,size,val), n(0), sum_y(0), sum_y_sq(0),
00048 min_y(numeric_limits<float>::max()),
00049 max_y(numeric_limits<float>::min())
00050 {
00051
00052 }
00053
00054 void DataSequence::Add(float val)
00055 {
00056 y.push_back(val);
00057 ++n;
00058 firstn = n-y.size();
00059 min_y = std::min(min_y,val);
00060 max_y = std::max(max_y,val);
00061 sum_y += val;
00062 sum_y_sq += val*val;
00063 }
00064
00065 void DataSequence::Clear()
00066 {
00067 y.clear();
00068 n = 0;
00069 firstn = 0;
00070 sum_y = 0;
00071 sum_y_sq = 0;
00072 min_y = numeric_limits<float>::max();
00073 max_y = numeric_limits<float>::min();
00074 }
00075
00076 float DataSequence::operator[](unsigned int i) const
00077 {
00078 if( !HasData(i) )
00079 {
00080 throw DataUnavailableException("Out of range");
00081 }
00082 return y[(i-firstn) % y.size()];
00083 }
00084
00085 DataLog::DataLog(unsigned int buffer_size)
00086 : buffer_size(buffer_size), x(0)
00087 {
00088 }
00089
00090 void DataLog::Clear()
00091 {
00092 x = 0;
00093 sequences.clear();
00094 }
00095
00096 void DataLog::Save(std::string filename)
00097 {
00098 if( sequences.size() > 0 )
00099 {
00100 ofstream f(filename.c_str());
00101 for( int n=sequences[0].firstn; n < sequences[0].n; ++n )
00102 {
00103 f << setprecision(12) << sequences[0][n];
00104 for( unsigned s=1; s < sequences.size(); ++s )
00105 f << ", " << sequences[s][n];
00106 f << endl;
00107 }
00108 f.close();
00109 }
00110 }
00111
00112 void DataLog::Log(const vector<float> & vals)
00113 {
00114 Log(vals.size(), &vals[0]);
00115 }
00116
00117 void DataLog::Log(unsigned int N, const float * vals)
00118 {
00119
00120 for( unsigned int i= sequences.size(); i < N; ++i )
00121 sequences.push_back(DataSequence(buffer_size,x,0));
00122
00123
00124 for( unsigned int i=0; i<sequences.size(); ++i )
00125 sequences[i].Add(vals[i]);
00126
00127
00128 for( unsigned int i=N; i<sequences.size(); ++i )
00129 sequences[i].Add(0.0f);
00130
00131 ++x;
00132 }
00133
00134 void DataLog::SetLabels(const std::vector<std::string> & new_labels)
00135 {
00136
00137 for( unsigned int i= labels.size(); i < new_labels.size(); ++i )
00138 labels.push_back(std::string("N/A"));
00139
00140
00141 for( unsigned int i=0; i<labels.size(); ++i )
00142 labels[i] = new_labels[i];
00143 }
00144
00145 void DataLog::Log(float v)
00146 {
00147 const float vs[] = {v};
00148 Log(1,vs);
00149 }
00150
00151 void DataLog::Log(float v1, float v2)
00152 {
00153 const float vs[] = {v1,v2};
00154 Log(2,vs);
00155 }
00156
00157 void DataLog::Log(float v1, float v2, float v3)
00158 {
00159 const float vs[] = {v1,v2,v3};
00160 Log(3,vs);
00161 }
00162 void DataLog::Log(float v1, float v2, float v3, float v4)
00163 {
00164 const float vs[] = {v1,v2,v3,v4};
00165 Log(4,vs);
00166 }
00167 void DataLog::Log(float v1, float v2, float v3, float v4, float v5)
00168 {
00169 const float vs[] = {v1,v2,v3,v4,v5};
00170 Log(5,vs);
00171 }
00172 void DataLog::Log(float v1, float v2, float v3, float v4, float v5, float v6)
00173 {
00174 const float vs[] = {v1,v2,v3,v4,v5,v6};
00175 Log(6,vs);
00176 }
00177
00178 Plotter::Plotter(DataLog* log, float left, float right, float bottom, float top, float tickx, float ticky)
00179 : log(log), track_front(true), draw_mode(0), plot_mode(TIME_SERIES)
00180 {
00181 this->handler = this;
00182 int_x[0] = int_x_dflt[0] = left;
00183 int_x[1] = int_x_dflt[1] = right;
00184 int_y[0] = int_y_dflt[0] = bottom;
00185 int_y[1] = int_y_dflt[1] = top;
00186 ticks[0] = tickx;
00187 ticks[1] = ticky;
00188
00189 for( unsigned int i=0; i<show_n; ++i )
00190 show[i] = true;
00191 }
00192
00193 void Plotter::DrawTicks()
00194 {
00195 glColor3fv(colour_tk);
00196 const int tx[2] =
00197 {
00198 (int)ceil(int_x[0] / ticks[0]),
00199 (int)ceil(int_x[1] / ticks[0])
00200 };
00201 const int ty[2] =
00202 {
00203 (int)ceil(int_y[0] / ticks[1]),
00204 (int)ceil(int_y[1] / ticks[1])
00205 };
00206
00207 if( tx[1] - tx[0] < v.w/4 )
00208 {
00209 for( int i=tx[0]; i<tx[1]; ++i )
00210 {
00211 glBegin(GL_LINE_STRIP);
00212 glVertex2f(i*ticks[0], int_y[0]);
00213 glVertex2f(i*ticks[0], int_y[1]);
00214 glEnd();
00215 }
00216 }
00217
00218 if( ty[1] - ty[0] < v.h/4 )
00219 {
00220 for( int i=ty[0]; i<ty[1]; ++i )
00221 {
00222 glBegin(GL_LINE_STRIP);
00223 glVertex2f(int_x[0], i*ticks[1]);
00224 glVertex2f(int_x[1], i*ticks[1]);
00225 glEnd();
00226 }
00227 }
00228
00229 glColor3fv(colour_ax);
00230 glBegin(GL_LINE_STRIP);
00231 glVertex2f(0, int_y[0]);
00232 glVertex2f(0, int_y[1]);
00233 glEnd();
00234 glBegin(GL_LINE_STRIP);
00235 glVertex2f(int_x[0],0);
00236 glVertex2f(int_x[1],0);
00237 glEnd();
00238 }
00239
00240 void Plotter::DrawSequence(const DataSequence& seq)
00241 {
00242 const int seqint_x[2] = {seq.firstn, seq.n };
00243 const int valid_int_x[2] =
00244 {
00245 std::max(seqint_x[0],(int)int_x[0]),
00246 std::min(seqint_x[1],(int)int_x[1])
00247 };
00248 glBegin(draw_modes[draw_mode]);
00249 for( int x=valid_int_x[0]; x<valid_int_x[1]; ++x )
00250 glVertex2f(x,seq[x]);
00251 glEnd();
00252 }
00253
00254 void Plotter::DrawSequenceHistogram(const std::vector<DataSequence>& seq)
00255 {
00256 size_t vec_size
00257 = std::min((unsigned)log->x, log->buffer_size);
00258 int idx_subtract
00259 = std::max(0,(int)(log->x)-(int)(log->buffer_size));
00260 vector<float> accum_vec(vec_size,0);
00261
00262 for(int s=log->sequences.size()-1; s >=0; --s )
00263 {
00264 if( (s > 9) || show[s] )
00265 {
00266
00267 const int seqint_x[2] = {seq.at(s).firstn, seq.at(s).n };
00268 const int valid_int_x[2] =
00269 {
00270 std::max(seqint_x[0],(int)int_x[0]),
00271 std::min(seqint_x[1],(int)int_x[1])
00272 };
00273
00274
00275 glBegin(GL_TRIANGLE_STRIP);
00276 glColor3fv(plot_colours[s%num_plot_colours]);
00277
00278
00279 for( int x=valid_int_x[0]; x<valid_int_x[1]; ++x )
00280 {
00281 float val = seq.at(s)[x];
00282
00283 float & accum = accum_vec.at(x-idx_subtract);
00284 float before_val = accum;
00285 accum += val;
00286 glVertex2f(x-0.5,before_val);
00287 glVertex2f(x-0.5,accum);
00288 glVertex2f(x+0.5,before_val);
00289 glVertex2f(x+0.5,accum);
00290 }
00291 glEnd();
00292 }
00293 }
00294 }
00295
00296 void Plotter::DrawSequence(const DataSequence& x,const DataSequence& y)
00297 {
00298 const unsigned minn = max(x.firstn,y.firstn);
00299 const unsigned maxn = min(x.n,y.n);
00300
00301 glBegin(draw_modes[draw_mode]);
00302 for( unsigned n=minn; n < maxn; ++n )
00303 glVertex2f(x[n],y[n]);
00304 glEnd();
00305 }
00306
00307 void Plotter::Render()
00308 {
00309 if( track_front )
00310 {
00311 const float d = int_x[1] - log->x;
00312 int_x[0] -= d;
00313 int_x[1] -= d;
00314 }
00315
00316 ActivateScissorAndClear();
00317 glMatrixMode(GL_PROJECTION);
00318 glLoadIdentity();
00319 gluOrtho2D(int_x[0], int_x[1], int_y[0], int_y[1]);
00320 glMatrixMode(GL_MODELVIEW);
00321 glLoadIdentity();
00322 glEnable(GL_LINE_SMOOTH);
00323
00324 DrawTicks();
00325
00326 if( log && log->sequences.size() > 0 )
00327 {
00328 if( plot_mode==XY )
00329 {
00330 for( unsigned int s=0; s < log->sequences.size() / 2; ++s )
00331 {
00332 if( (s > 9) || show[s] )
00333 {
00334 glColor3fv(plot_colours[s%num_plot_colours]);
00335 DrawSequence(log->sequences[2*s],log->sequences[2*s+1]);
00336 }
00337 }
00338 }
00339 else if( plot_mode==TIME_SERIES)
00340 {
00341 for( unsigned int s=0; s < log->sequences.size(); ++s )
00342 {
00343 if( (s > 9) || show[s] )
00344 {
00345 glColor3fv(plot_colours[s%num_plot_colours]);
00346 DrawSequence(log->sequences[s]);
00347 }
00348 }
00349 }
00350 else if( plot_mode==STACKED_HISTOGRAM )
00351 {
00352
00353
00354 DrawSequenceHistogram(log->sequences);
00355
00356 }
00357 else
00358 {
00359 assert(false);
00360 }
00361 }
00362
00363 if( mouse_state & MouseButtonLeft )
00364 {
00365 if( plot_mode==XY )
00366 {
00367 glColor3fv(colour_ms);
00368 glBegin(GL_LINE_STRIP);
00369 glVertex2f(mouse_xy[0],int_y[0]);
00370 glVertex2f(mouse_xy[0],int_y[1]);
00371 glEnd();
00372 glBegin(GL_LINE_STRIP);
00373 glVertex2f(int_x[0],mouse_xy[1]);
00374 glVertex2f(int_x[1],mouse_xy[1]);
00375 glEnd();
00376 stringstream ss;
00377 ss << "(" << mouse_xy[0] << "," << mouse_xy[1] << ")";
00378 glColor3f(1.0,1.0,1.0);
00379 OpenGlRenderState::ApplyWindowCoords();
00380 glRasterPos2f( v.l+5,v.b+5 );
00381 glutBitmapString(font,(unsigned char*)ss.str().c_str());
00382 }
00383 else
00384 {
00385 int xu = (int)mouse_xy[0];
00386 glColor3fv(colour_ms);
00387 glBegin(GL_LINE_STRIP);
00388 glVertex2f(xu,int_y[0]);
00389 glVertex2f(xu,int_y[1]);
00390 glEnd();
00391 stringstream ss;
00392 glColor3f(1.0,1.0,1.0);
00393 ss << "x=" << xu << " ";
00394 OpenGlRenderState::ApplyWindowCoords();
00395 int tx = v.l+5;
00396 glRasterPos2f( tx,v.b+5 );
00397 glutBitmapString(font,(unsigned char*)ss.str().c_str());
00398 tx += glutBitmapLength(font,(unsigned char*)ss.str().c_str());
00399 for( unsigned int s=0; s<log->sequences.size(); ++s )
00400 {
00401 if( (s > show_n || show[s]) && log->sequences[s].HasData(xu) )
00402 {
00403 stringstream ss;
00404 ss << " " << log->sequences[s][xu];
00405 glColor3fv(plot_colours[s%num_plot_colours]);
00406 glRasterPos2f( tx,v.b+5 );
00407 glutBitmapString(font,(unsigned char*)ss.str().c_str());
00408 tx += glutBitmapLength(font,(unsigned char*)ss.str().c_str());
00409 }
00410 }
00411 }
00412 }
00413
00414 float ty = v.h-15;
00415 for (size_t i=0; i<log->labels.size(); ++i)
00416 {
00417 glColor3fv(plot_colours[i%num_plot_colours]);
00418
00419 OpenGlRenderState::ApplyWindowCoords();
00420 glRasterPos2f( v.l+5,ty);
00421 glutBitmapString(font,(unsigned char*)log->labels[i].c_str());
00422 ty -= 15;
00423 }
00424 }
00425
00426 void Plotter::ResetView()
00427 {
00428 track_front = true;
00429 int_x[0] = int_x_dflt[0];
00430 int_x[1] = int_x_dflt[1];
00431 int_y[0] = int_y_dflt[0];
00432 int_y[1] = int_y_dflt[1];
00433 for( unsigned int i=0; i<show_n; ++i )
00434 show[i] = true;
00435 }
00436
00437 void Plotter::Keyboard(View&, unsigned char key, int x, int y, bool pressed)
00438 {
00439 if( pressed )
00440 {
00441 if( key == 't' )
00442 {
00443 track_front = !track_front;
00444 }
00445 else if( key == 'c' )
00446 {
00447 log->Clear();
00448 cout << "Plotter: Clearing data" << endl;
00449 }
00450 else if( key == 's' )
00451 {
00452 log->Save("./log.csv");
00453 cout << "Plotter: Saving to log.csv" << endl;
00454 }
00455 else if( key == 'm' )
00456 {
00457 draw_mode = (draw_mode+1)%draw_modes_n;
00458 }
00459 else if( key == 'p' )
00460 {
00461 plot_mode = (plot_mode+1)%modes_n;
00462 ResetView();
00463 if( plot_mode==XY )
00464 {
00465 int_x[0] = int_y[0];
00466 int_x[1] = int_y[1];
00467 track_front = false;
00468 }
00469 }
00470 else if( key == 'r' )
00471 {
00472 cout << "Plotter: Reset viewing range" << endl;
00473 ResetView();
00474 }
00475 else if( key == 'a' || key == ' ' )
00476 {
00477 cout << "Plotter: Auto scale" << endl;
00478 if( plot_mode==XY && log->sequences.size() >= 2)
00479 {
00480 int_x[0] = log->sequences[0].min_y;
00481 int_x[1] = log->sequences[0].max_y;
00482 int_y[0] = log->sequences[1].min_y;
00483 int_y[1] = log->sequences[1].max_y;
00484 }
00485 else
00486 {
00487 float min_y = numeric_limits<float>::max();
00488 float max_y = numeric_limits<float>::min();
00489 for( unsigned int i=0; i<log->sequences.size(); ++i )
00490 {
00491 if( i>=show_n || show[i] )
00492 {
00493 min_y = std::min(min_y,log->sequences[i].min_y);
00494 max_y = std::max(max_y,log->sequences[i].max_y);
00495 }
00496 }
00497 if( min_y < max_y )
00498 {
00499 int_y[0] = min_y;
00500 int_y[1] = max_y;
00501 }
00502 }
00503 }
00504 else if( '1' <= key && key <= '9' )
00505 {
00506 show[key-'1'] = !show[key-'1'];
00507 }
00508 }
00509 }
00510
00511 void Plotter::ScreenToPlot(int x, int y)
00512 {
00513 mouse_xy[0] = int_x[0] + (int_x[1]-int_x[0]) * (x - v.l) / (float)v.w;
00514 mouse_xy[1] = int_y[0] + (int_y[1]-int_y[0]) * (y - v.b) / (float)v.h;
00515 }
00516
00517 void Plotter::Mouse(View&, MouseButton button, int x, int y, bool pressed, int button_state)
00518 {
00519 last_mouse_pos[0] = x;
00520 last_mouse_pos[1] = y;
00521 mouse_state = button_state;
00522
00523 if(button == MouseWheelUp || button == MouseWheelDown)
00524 {
00525
00526 const float scale = 1.0f + ((button == MouseWheelDown) ? 0.1 : -0.1);
00527
00528
00529 int_y[0] = scale*(int_y[0]) ;
00530 int_y[1] = scale*(int_y[1]) ;
00531 }
00532
00533 ScreenToPlot(x,y);
00534 }
00535
00536 void Plotter::MouseMotion(View&, int x, int y, int button_state)
00537 {
00538 mouse_state = button_state;
00539 const int d[2] = {x-last_mouse_pos[0],y-last_mouse_pos[1]};
00540 const float is[2] = {int_x[1]-int_x[0],int_y[1]-int_y[0]};
00541 const float df[2] = {is[0]*d[0]/(float)v.w, is[1]*d[1]/(float)v.h};
00542
00543 if( button_state == MouseButtonLeft )
00544 {
00545 track_front = false;
00546 int_x[0] -= df[0];
00547 int_x[1] -= df[0];
00548
00549
00550 }
00551 else if(button_state == MouseButtonMiddle )
00552 {
00553 int_y[0] -= df[1];
00554 int_y[1] -= df[1];
00555 }
00556 else if(button_state == MouseButtonRight )
00557 {
00558 const double c[2] =
00559 {
00560 track_front ? int_x[1] : (int_x[0] + int_x[1])/2.0,
00561 (int_y[0] + int_y[1])/2.0
00562 };
00563 const float scale[2] =
00564 {
00565 1.0f + (float)d[0] / (float)v.w,
00566 1.0f - (float)d[1] / (float)v.h,
00567 };
00568 int_x[0] = scale[0]*(int_x[0] - c[0]) + c[0];
00569 int_x[1] = scale[0]*(int_x[1] - c[0]) + c[0];
00570 int_y[0] = scale[1]*(int_y[0] - c[1]) + c[1];
00571 int_y[1] = scale[1]*(int_y[1] - c[1]) + c[1];
00572 }
00573
00574 last_mouse_pos[0] = x;
00575 last_mouse_pos[1] = y;
00576 }
00577
00578 Plotter& CreatePlotter(const string& name, DataLog* log)
00579 {
00580 Plotter* v = new Plotter(log);
00581
00582 bool inserted = context->named_managed_views.insert(name,v).second;
00583 if(!inserted) throw exception();
00584 context->base.views.push_back(v);
00585 return *v;
00586 }
00587
00588 }