Plot2D.cpp

Go to the documentation of this file.
00001 /**
00002 \file    Plot2D.cpp
00003 \brief   The plotting class.
00004 
00005 \author  Glenn D. MacGougan (GDM)
00006 \date    2007-12-19
00007 \since   2007-12-19
00008 
00009 \b "LICENSE INFORMATION" \n
00010 Copyright (c) 2007, refer to 'author' doxygen tags \n
00011 All rights reserved. \n
00012 
00013 Redistribution and use in source and binary forms, with or without
00014 modification, are permitted provided the following conditions are met: \n
00015 
00016 - Redistributions of source code must retain the above copyright
00017   notice, this list of conditions and the following disclaimer. \n
00018 - Redistributions in binary form must reproduce the above copyright
00019   notice, this list of conditions and the following disclaimer in the
00020   documentation and/or other materials provided with the distribution. \n
00021 - The name(s) of the contributor(s) may not be used to endorse or promote 
00022   products derived from this software without specific prior written 
00023   permission. \n
00024 
00025 THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS 
00026 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
00027 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00028 DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00029 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00030 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
00031 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
00032 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
00033 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
00034 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
00035 SUCH DAMAGE.
00036 */
00037 
00038 #include <math.h>
00039 #include <memory.h>
00040 #include <float.h>
00041 #include <string.h>
00042 #include <ctype.h>
00043 #include "Plot2D.h"
00044 
00045 #define PLOT2D_MAX_PLO2D_ReadFromFile_BUFFER (8192)
00046 
00047 #ifndef _CRT_SECURE_NO_DEPRECATE
00048 #define _CRT_SECURE_NO_DEPRECATE
00049 #endif
00050 
00051 
00052 namespace namespace_Plot2D
00053 {
00054   //___________________________________________________________________________
00055 
00056 
00057   /// struct specific for ReadFromFile and related functions (a simple linked list)
00058   typedef struct _listItem
00059   {
00060     bool    isReal;    //!< A boolean to indicate if real data or complex data is attached to this item.
00061     double  *rowptr;   //!< The pointer to real data.
00062     struct _listItem  *next;  //!< The pointer to the next item in the list.
00063   } _PLOT2D_structReadFromFileListElem;
00064 
00065 
00066   //___________________________________________________________________________
00067 
00068 
00069   /// This function gets the next valid line of data. Whitespace lines are skipped.
00070   static bool _PLOT2D_get_next_valid_data_line( 
00071     FILE *in,               //!< The input file pointer (input).
00072     char *linebuf,          //!< A exisiting buffer to store the input line (input/output).
00073     unsigned *line_length,  //!< The length of the line read (output).
00074     bool *atEOF             //!< A boolean to indicate if EOF has been reached.
00075     );
00076 
00077   /// Obtain the next row array from the string.
00078   static bool _PLOT2D_get_row_array_from_string( char *datastr, _PLOT2D_structReadFromFileListElem *L, const unsigned ncols );
00079 
00080   /// \brief  Determine the number of columns in the data string provided.
00081   /// \return true if successful, false otherwise.
00082   static bool _PLO2D_determine_nr_cols_in_data_str( const char *datastr, unsigned *ncols );
00083 
00084 
00085   /// \brief  Determine the matrix file delimiter and if a comment line is available.
00086   /// \return true if successful, false otherwise.
00087   static bool _PLOT2D_determine_file_delimiter( 
00088     const char *path,    //!< path to the input file
00089     char *delimiter,     //!< delimiter, 'b' is binary
00090     bool *hasComment     //!< bool to indicate if a comment line is present    
00091     );
00092 
00093 
00094   /// \brief  Read a real-only matrix from a file (ASCII formatted, any common delimiters).
00095   /// \return true if successful, false otherwise.
00096   static bool _PLOT2D_ReadFromFile( Plot2D::_PLOT2D_structMTX *M, const char *path );
00097 
00098 
00099   //___________________________________________________________________________
00100   
00101 
00102   bool _PLOT2D_get_next_valid_data_line( 
00103     FILE *in,               //!< The input file pointer (input).
00104     char *linebuf,          //!< A exisiting buffer to store the input line (input/output).
00105     unsigned *line_length,  //!< The length of the line read (output).
00106     bool *atEOF             //!< A boolean to indicate if EOF has been reached.
00107     )
00108   {
00109     unsigned i = 0;
00110     i = 0;
00111     *line_length = 0;
00112     while( i == *line_length )
00113     {
00114       if( fgets( linebuf, PLOT2D_MAX_PLO2D_ReadFromFile_BUFFER, in ) == NULL )
00115       {
00116         *atEOF = true;
00117         if( feof(in) )
00118         {
00119           // reached the end of the file properly
00120           fclose(in);        
00121           return true;
00122         }
00123         else if( ferror(in) != 0 )
00124         {
00125           // error in reading the datafile
00126           fclose(in);
00127           return false;        
00128         }
00129         else
00130         {
00131           // error in reading the datafile
00132           fclose(in);
00133           return false;
00134         }
00135       }  
00136 
00137       *line_length = (unsigned int)strlen(linebuf);
00138       for( i = 0; i < *line_length; i++ )
00139       {
00140         if( !isspace(linebuf[i]) )
00141           break;
00142       }
00143     }
00144 
00145     // provide some buffer room, in case of overreading the linebuffer.
00146     if( *line_length < PLOT2D_MAX_PLO2D_ReadFromFile_BUFFER-2 )
00147     {
00148       linebuf[*line_length] = '\0';
00149       linebuf[(*line_length)+1] = '\0';
00150     }
00151 
00152     return true;
00153   }
00154 
00155 
00156 
00157   bool _PLOT2D_determine_file_delimiter( 
00158     const char *path,    //!< path to the input file
00159     char *delimiter,     //!< delimiter, 'b' is binary
00160     bool *hasComment     //!< bool to indicate if a comment line is present    
00161     )
00162   { 
00163     unsigned i = 0;
00164     unsigned line_length = 0;   
00165     char line[PLOT2D_MAX_PLO2D_ReadFromFile_BUFFER];
00166     char *linebuf;  
00167     FILE *in;
00168     bool atEOF = false;
00169 
00170     *hasComment = false;
00171 
00172     // open the input file
00173     in = fopen( path, "r" );
00174     if( !in )
00175       return false;
00176 
00177     // get the first line of data (or the comment line)
00178     if( !_PLOT2D_get_next_valid_data_line( in, line, &line_length, &atEOF ) )
00179       return false;
00180     if( atEOF )
00181       return false;
00182 
00183     // check the first line with isalpha (the first line might be the comment line
00184     linebuf = (char *)line;
00185     for( i = 0; i < line_length; i++ )
00186     {
00187       if( isalpha( line[i] ) )
00188       {
00189         if( line[i] == 'e' || line[i] == 'E' || line[i] == '-' || line[i] == '+' || line[i] == '.' || line[i] == 'i' || line[i] == 'j' )
00190           continue;
00191 
00192         // first line is likely a comment line, store this for decoding later
00193         *hasComment = true;
00194 
00195         if( !_PLOT2D_get_next_valid_data_line( in, line, &line_length, &atEOF ) )
00196           return false;
00197         if( atEOF )
00198           return false;      
00199 
00200         linebuf = (char *)line;
00201         break;
00202       }
00203     }
00204     fclose( in ); 
00205 
00206     // default is whitespace
00207     *delimiter = 'w';
00208 
00209     if( strstr( linebuf, "," ) )  { *delimiter = ','; return true; }
00210     if( strstr( linebuf, ":" ) )  { *delimiter = ':'; return true; }
00211     if( strstr( linebuf, ";" ) )  { *delimiter = ';'; return true; }
00212     if( strstr( linebuf, "|" ) )  { *delimiter = '|'; return true; }
00213     if( strstr( linebuf, "`" ) )  { *delimiter = '`'; return true; }
00214     if( strstr( linebuf, "~" ) )  { *delimiter = '~'; return true; }
00215     if( strstr( linebuf, "!" ) )  { *delimiter = '!'; return true; }
00216     if( strstr( linebuf, "@" ) )  { *delimiter = '@'; return true; }
00217     if( strstr( linebuf, "#" ) )  { *delimiter = '#'; return true; }
00218     if( strstr( linebuf, "$" ) )  { *delimiter = '$'; return true; }
00219     if( strstr( linebuf, "%%" ) ) { *delimiter = '%'; return true; }
00220     if( strstr( linebuf, "^" ) )  { *delimiter = '^'; return true; }
00221     if( strstr( linebuf, "&" ) )  { *delimiter = '&'; return true; }
00222     if( strstr( linebuf, "*" ) )  { *delimiter = '*'; return true; }
00223     if( strstr( linebuf, "(" ) )  { *delimiter = '('; return true; }
00224     if( strstr( linebuf, ")" ) )  { *delimiter = ')'; return true; }
00225     if( strstr( linebuf, "_" ) )  { *delimiter = '_'; return true; }
00226     if( strstr( linebuf, "=" ) )  { *delimiter = '='; return true; }
00227     if( strstr( linebuf, "{" ) )  { *delimiter = '{'; return true; }
00228     if( strstr( linebuf, "}" ) )  { *delimiter = '}'; return true; }
00229     if( strstr( linebuf, "[" ) )  { *delimiter = '['; return true; }
00230     if( strstr( linebuf, "]" ) )  { *delimiter = ']'; return true; }
00231     if( strstr( linebuf, "\\" ) ) { *delimiter = '\\'; return true; }
00232     if( strstr( linebuf, "\'" ) ) { *delimiter = '\''; return true; }
00233     if( strstr( linebuf, "<" ) )  { *delimiter = '<'; return true; }
00234     if( strstr( linebuf, "<" ) )  { *delimiter = '<'; return true; }
00235     if( strstr( linebuf, ">" ) )  { *delimiter = '>'; return true; }
00236     if( strstr( linebuf, "?" ) )  { *delimiter = '?'; return true; }
00237     if( strstr( linebuf, "/" ) )  { *delimiter = '/'; return true; }
00238 
00239     return true;
00240   }
00241 
00242 
00243 
00244   bool _PLO2D_determine_nr_cols_in_data_str( const char *datastr, unsigned *ncols )
00245   { 
00246     unsigned i = 0;
00247     unsigned line_length;
00248     char c;
00249     bool wasLastCharData = false;
00250     double dtmp;
00251     int rv;
00252 
00253     if( !datastr )
00254       return false;
00255 
00256     line_length = (unsigned int)strlen(datastr);
00257     if( line_length == 0 )
00258       return false;
00259 
00260     // intialize ncols
00261     *ncols = 0;
00262 
00263     // advance over whitespace to the first data element  
00264     for( i = 0; i < line_length; i++ )
00265     {
00266       if( isspace(datastr[i]) )         
00267         continue;
00268       else 
00269         break;
00270     }  
00271     if( i == line_length )
00272     {
00273       // no data in the string
00274       return false;
00275     }
00276 
00277     // determine the number of columns in the matrix
00278     for( /* i */; i < line_length; i++ )
00279     {
00280       c = datastr[i];
00281       if( isdigit(c) || c == '.' || c == '-' || c == '+' )
00282       {
00283         if( !wasLastCharData )
00284         {
00285           // try to read in the data to be sure the element is valid
00286 #ifndef _CRT_SECURE_NO_DEPRECATE
00287           rv = sscanf_s( &(datastr[i]), "%lf", &dtmp );
00288 #else
00289           rv = sscanf( &(datastr[i]), "%lf", &dtmp );
00290 #endif
00291           if( rv == 0 || rv == EOF )
00292           {
00293             // invalid element, file is corrupt
00294             (*ncols) = 0;
00295             return false;          
00296           }
00297           (*ncols)++;
00298         }
00299         wasLastCharData = true;
00300       }
00301       else if( c == 'e' || c == 'E' )
00302       {
00303         if( !wasLastCharData )
00304         {
00305           // the exponent should not be the leading character in 
00306           // a data element
00307           (*ncols) = 0;
00308           return false;
00309         }
00310         continue;
00311       }
00312       else
00313       {
00314         wasLastCharData = false;
00315       }
00316     }
00317 
00318     return true;
00319   }
00320 
00321 
00322   bool _PLOT2D_get_row_array_from_string( char *datastr, _PLOT2D_structReadFromFileListElem *L, const unsigned ncols )
00323   {
00324     unsigned i = 0;
00325     unsigned n; // number of columns read successfully
00326     unsigned line_length;
00327     char c;
00328     BOOL wasLastCharData = false;
00329     int rv;
00330 
00331     if( !datastr )
00332       return false;
00333 
00334     if( !L )
00335       return false;
00336 
00337     if( !L->rowptr )
00338       return false;
00339 
00340     line_length = (unsigned int)strlen(datastr);
00341     if( line_length == 0 )
00342       return false;
00343 
00344     // advance over whitespace to the first data element  
00345     for( i = 0; i < line_length; i++ )
00346     {
00347       if( isspace(datastr[i]) )         
00348         continue;
00349       else 
00350         break;
00351     }  
00352     if( i == line_length )
00353     {
00354       // no data in the string
00355       return false;
00356     }
00357 
00358     // read in one row of data
00359     n = 0;  
00360     for( /* i */; i < line_length; i++ )
00361     {
00362       c = datastr[i];
00363       if( isdigit(c) || c == '.' || c == '-' || c == '+' )
00364       {
00365         if( !wasLastCharData )
00366         {
00367           // try to read in the data to be sure the element is valid
00368 #ifndef _CRT_SECURE_NO_DEPRECATE
00369           rv = sscanf_s( &(datastr[i]), "%lf", &(L->rowptr[n]) );
00370 #else
00371           rv = sscanf( &(datastr[i]), "%lf", &(L->rowptr[n]) );
00372 #endif
00373           if( rv == 0 || rv == EOF )
00374           {
00375             // invalid element, file is corrupt                    
00376             return false;          
00377           }
00378           n++;
00379           if( n == ncols )
00380             break;
00381         }
00382         wasLastCharData = true;
00383       }
00384       else if( c == 'e' || c == 'E' )
00385       {
00386         if( !wasLastCharData )
00387         {
00388           // the exponent should not be the leading character in 
00389           // a data element        
00390           return false;
00391         }
00392         continue;
00393       }
00394       else
00395       {
00396         wasLastCharData = false;
00397       }
00398     }
00399 
00400     if( n != ncols )
00401     {
00402       // the number of columns does not match
00403       return false;
00404     }
00405 
00406     return true;
00407   }
00408 
00409 
00410 
00411 
00412   // Reads in the matrix from the specified file using the indicated *delimiter
00413   // ReadFromFile is 'read smart' (it determines the size of the input matrix on its own)
00414   // The number of columns are first determined then all data is read into linked lists
00415   // until end of file is reached. Data is then stored in the matrix.
00416   bool _PLOT2D_ReadFromFile( Plot2D::_PLOT2D_structMTX *M, const char *path )
00417   {
00418     unsigned i = 0;
00419     unsigned j = 0;
00420     FILE *in = NULL;
00421     char delimiter = 0;
00422     char linebuf[PLOT2D_MAX_PLO2D_ReadFromFile_BUFFER];
00423     unsigned ncols = 0;
00424     unsigned nrows = 0;
00425     unsigned line_length = 0;
00426     bool hasCommentLine = false;  
00427     bool errorInReadingDataFile = false;
00428     bool atEOF = false;
00429 
00430     // a linked list of row arrays
00431     _PLOT2D_structReadFromFileListElem *L = NULL;
00432     _PLOT2D_structReadFromFileListElem *nL = NULL;
00433     _PLOT2D_structReadFromFileListElem head;
00434     head.next = NULL;
00435     head.rowptr = NULL;
00436 
00437     // check input
00438     if( !M )
00439       return false;
00440 
00441     M->data = NULL;
00442     M->nrows = 0;
00443     M->ncols = 0;
00444 
00445     // check path 
00446     if( !path  )
00447       return false;
00448 
00449     // check path exists
00450     in = fopen( path, "r" );
00451     if( !in )
00452       return false;
00453     fclose(in);
00454 
00455     // determine the file delimiter
00456     if( _PLOT2D_determine_file_delimiter( path, &delimiter, &hasCommentLine ) == false )  
00457       return false;
00458 
00459     // open the input file for full input operations
00460     in = fopen( path, "r" );
00461     if( !in )
00462       return false;
00463 
00464     // advance over whitespace lines at the beginning of the file
00465     if( !_PLOT2D_get_next_valid_data_line( in, linebuf, &line_length, &atEOF ) )
00466       return false;
00467     if( atEOF )
00468       return false;
00469 
00470     if( hasCommentLine )
00471     {
00472       if( !_PLOT2D_get_next_valid_data_line( in, linebuf, &line_length, &atEOF ) )
00473         return false;
00474       if( atEOF )
00475         return false;
00476     }
00477 
00478     // determine the number of columns in the first line of data
00479     if( _PLO2D_determine_nr_cols_in_data_str( linebuf, &ncols ) == false )
00480       return false;
00481 
00482 
00483     // super fast rowwise input routine
00484     // a rowwise matrix is constructed using a linked list approach
00485     // line by line input 
00486     nrows = 0;
00487     head.rowptr = (double*)malloc( ncols*sizeof(double) );
00488     // get the row data from the string and store it in the list item row array
00489     if( _PLOT2D_get_row_array_from_string( linebuf, &head, ncols ) == false )
00490     { 
00491       // must free head's rowarray   
00492       free( head.rowptr );
00493       return false;
00494     }
00495     nrows++;
00496     nL = &head;
00497 
00498     while(1)
00499     { 
00500       // get the next string of data
00501       if( !_PLOT2D_get_next_valid_data_line( in, linebuf, &line_length, &atEOF ) )
00502       {
00503         errorInReadingDataFile = true;
00504         break;
00505       }
00506       if( atEOF )
00507       {
00508         break;
00509       }
00510 
00511       // the 'current' list itme
00512       L = nL;
00513 
00514       // allocate the next list item
00515       nL = (_PLOT2D_structReadFromFileListElem*)malloc( sizeof( _PLOT2D_structReadFromFileListElem ) );
00516       if( !nL )
00517       {
00518         // memory allocate failure
00519         // must free the linked list
00520         errorInReadingDataFile = true;
00521         // must free head's rowarray   
00522         free( head.rowptr );
00523         return false;
00524       }
00525       // intialize the row 
00526       nL->rowptr = NULL;
00527       nL->next = NULL;
00528       // allocate the row
00529       nL->rowptr = (double*)malloc( ncols*sizeof(double) );
00530       if( !nL->rowptr )
00531       {
00532         // memory allocate failure
00533         // must free the linked list    
00534         errorInReadingDataFile = true;
00535         break;      
00536       }
00537 
00538       // store the pointer to the next list item
00539       // in the 'current'
00540       L->next = nL;
00541 
00542       // get the row data from the string and store it in the list item row array
00543       if( _PLOT2D_get_row_array_from_string( linebuf, nL, ncols ) == false )
00544       { 
00545         // must free the linked list          
00546         errorInReadingDataFile = true;
00547         break;      
00548       }
00549       nrows++;
00550     }
00551 
00552     if( errorInReadingDataFile )
00553     {
00554       // free the list
00555       L = head.next;
00556       while( L!=NULL )
00557       {
00558         nL = L->next;
00559         free( L->rowptr );
00560         free(L);
00561         L = nL;    
00562       }
00563       free( head.rowptr );
00564 
00565       return false;
00566     }
00567 
00568     // copy the list into a MTX object
00569 
00570     // allocate the row array of pointers
00571     M->data = (double*)malloc( nrows*ncols*sizeof(double) );
00572     if( !M->data )
00573       return false;
00574 
00575     L = &head;
00576     for( i = 0; i < nrows; i++ )
00577     {
00578       if( L == NULL )
00579       {
00580         // this should never happen
00581         free( M->data );
00582         return false;
00583       }
00584       memcpy( &(M->data[i*ncols]), L->rowptr, sizeof(double)*ncols );
00585       free( L->rowptr );
00586       L->rowptr = NULL;
00587       L = L->next;
00588     }
00589 
00590     // free the list items
00591     L = head.next;
00592     while( L!=NULL )
00593     {
00594       nL = L->next;
00595       if( L->rowptr )
00596       {
00597         // should never happen but just in case
00598         free( L->rowptr );
00599       }        
00600       free(L);
00601       L = nL;    
00602     }
00603     // free the head data
00604     if( head.rowptr )
00605     {
00606       // should never happen but just in case
00607       free( head.rowptr );
00608     }
00609 
00610     M->nrows = nrows;
00611     M->ncols = ncols;
00612 
00613     return true;
00614   }  
00615 
00616 
00617   Plot2D::~Plot2D()
00618   {
00619     int i = 0;
00620 
00621     if( m_mtx.data != NULL )
00622     {
00623       free( m_mtx.data );
00624       m_mtx.data = NULL;
00625     }
00626     for( i = 0; i < 12; i++ )
00627     {
00628       if( m_Series[i].X != NULL )
00629       {
00630         free(m_Series[i].X);
00631         m_Series[i].X = NULL;
00632       }
00633       if( m_Series[i].Y != NULL )
00634       {
00635         free(m_Series[i].Y);
00636         m_Series[i].Y = NULL;
00637       }
00638     }
00639   }
00640 
00641 
00642   Plot2D::Plot2D()
00643     : m_ArePlotOptionsSet(false),
00644     m_xmin(DBL_MAX),
00645     m_xmax(-DBL_MAX),
00646     m_ymin(DBL_MAX),
00647     m_ymax(-DBL_MAX)
00648   {
00649     int i = 0;    
00650     CPLOT_PlotOptionsInit( &m_opt );
00651     CPLOT_Init( &m_plot );
00652 
00653     for( i = 0; i < 12; i++ )
00654     {
00655       memset( &m_Series[i], 0, sizeof(CPLOT_structSeries) );
00656     }
00657   }
00658 
00659   void Plot2D::SetTitle( const std::string title )
00660   {
00661     m_title = title;
00662     m_opt.title = (char*)m_title.c_str();
00663   }
00664 
00665   bool Plot2D::SetNrSeries( const int nrSeries )
00666   {
00667     if( nrSeries >= 12 )
00668       return false;
00669 
00670     m_opt.numberOfSeries = nrSeries;
00671 
00672     return true;
00673   }
00674 
00675   bool Plot2D::SetPlotSize( const int width_cm, const int height_cm )
00676   {
00677     if( width_cm <= 0 || height_cm <= 0 )
00678       return false;
00679 
00680     m_opt.PlotSize_Width_cm = width_cm;
00681     m_opt.PlotSize_Height_cm = height_cm;
00682 
00683     return true;
00684   }
00685 
00686   bool Plot2D::Set_X_Axis_LowerLimit( const double value )
00687   {
00688     m_opt.x.lowerlimit.doNotUseDefault = true;
00689     m_opt.x.lowerlimit.val = value;    
00690 
00691     if( m_opt.x.upperlimit.doNotUseDefault )
00692     {
00693       if( m_opt.x.lowerlimit.val >= m_opt.x.upperlimit.val )
00694         return false;
00695 
00696     }
00697     return true;
00698   }
00699 
00700   bool Plot2D::Set_X_Axis_UpperLimit( const double value )
00701   {
00702     m_opt.x.upperlimit.doNotUseDefault = true;
00703     m_opt.x.upperlimit.val = value;    
00704 
00705     if( m_opt.x.lowerlimit.doNotUseDefault )
00706     {
00707       if( m_opt.x.lowerlimit.val >= m_opt.x.upperlimit.val )
00708         return false;
00709 
00710     }
00711     return true;
00712   }
00713 
00714   bool Plot2D::Set_X_Axis_TickStart( const double value )
00715   {
00716     m_opt.x.tickstart.doNotUseDefault = true;
00717     m_opt.x.tickstart.val = value;    
00718 
00719     if( m_opt.x.tickend.doNotUseDefault )
00720     {
00721       if( m_opt.x.tickstart.val >= m_opt.x.tickend.val )
00722         return false;
00723     }
00724     return true;
00725   }
00726 
00727   bool Plot2D::Set_X_Axis_TickSize( const double value )
00728   {
00729     m_opt.x.ticksize.doNotUseDefault = true;
00730     m_opt.x.ticksize.val = value;    
00731     return true;
00732   }
00733 
00734   bool Plot2D::Set_X_Axis_TickEnd( const double value )
00735   {
00736     m_opt.x.tickend.doNotUseDefault = true;
00737     m_opt.x.tickend.val = value;    
00738 
00739     if( m_opt.x.tickstart.doNotUseDefault )
00740     {
00741       if( m_opt.x.tickstart.val >= m_opt.x.tickend.val )
00742         return false;
00743     }
00744     return true;
00745   }
00746 
00747   bool Plot2D::Set_X_Label( const std::string label )
00748   {
00749     m_x_label = label;
00750     m_opt.x.label = (char*)m_x_label.c_str();
00751     return true;
00752   }
00753 
00754   bool Plot2D::Set_Y_Label( const std::string label )
00755   {
00756     m_y_label = label;
00757     m_opt.y.label = (char*)m_y_label.c_str();
00758     return true;
00759   }
00760 
00761   bool Plot2D::Set_X_Axis_isGridOn( const bool isGridOn )
00762   {
00763     m_opt.x.isGridOn = isGridOn;
00764     return true;
00765   }
00766 
00767   bool Plot2D::Set_Y_Axis_isGridOn( const bool isGridOn )
00768   {
00769     m_opt.y.isGridOn = isGridOn;
00770     return true;
00771   }
00772 
00773 
00774 
00775   bool Plot2D::Set_Y_Axis_LowerLimit( const double value )
00776   {
00777     m_opt.y.lowerlimit.doNotUseDefault = true;
00778     m_opt.y.lowerlimit.val = value;    
00779 
00780     if( m_opt.y.upperlimit.doNotUseDefault )
00781     {
00782       if( m_opt.y.lowerlimit.val >= m_opt.y.upperlimit.val )
00783         return false;
00784 
00785     }
00786     return true;
00787   }
00788 
00789   bool Plot2D::Set_Y_Axis_UpperLimit( const double value )
00790   {
00791     m_opt.y.upperlimit.doNotUseDefault = true;
00792     m_opt.y.upperlimit.val = value;    
00793 
00794     if( m_opt.y.lowerlimit.doNotUseDefault )
00795     {
00796       if( m_opt.y.lowerlimit.val >= m_opt.y.upperlimit.val )
00797         return false;
00798 
00799     }
00800     return true;
00801   }
00802 
00803   bool Plot2D::Set_Y_Axis_TickStart( const double value )
00804   {
00805     m_opt.y.tickstart.doNotUseDefault = true;
00806     m_opt.y.tickstart.val = value;    
00807 
00808     if( m_opt.y.tickend.doNotUseDefault )
00809     {
00810       if( m_opt.y.tickstart.val >= m_opt.y.tickend.val )
00811         return false;
00812     }
00813     return true;
00814   }
00815 
00816   bool Plot2D::Set_Y_Axis_TickSize( const double value )
00817   {
00818     m_opt.y.ticksize.doNotUseDefault = true;
00819     m_opt.y.ticksize.val = value;    
00820     return true;
00821   }
00822 
00823   bool Plot2D::Set_Y_Axis_TickEnd( const double value )
00824   {
00825     m_opt.y.tickend.doNotUseDefault = true;
00826     m_opt.y.tickend.val = value;    
00827 
00828     if( m_opt.y.tickstart.doNotUseDefault )
00829     {
00830       if( m_opt.y.tickstart.val >= m_opt.y.tickend.val )
00831         return false;
00832     }
00833     return true;
00834   }
00835 
00836   bool Plot2D::SetRightYLabel( 
00837     const std::string label,            //!< The description string.
00838     double y_label_right_scale_factor,  //!< The scale factor.
00839     double y_label_right_bias,          //!< The bias.
00840     CPLOT_enumColor color               //!< The color.
00841     )
00842   {
00843     if( label.empty() )
00844       return false;
00845     m_y_label_right = label;
00846     
00847     m_opt.y_label_right = (char *)m_y_label_right.c_str();
00848     m_opt.y_label_right_scale_factor = y_label_right_scale_factor;
00849     m_opt.y_label_right_bias = y_label_right_bias;
00850     m_opt.RightYLabelColor = color;
00851     return true;
00852   }
00853 
00854   bool Plot2D::SetGPSLabel( const int UTCOffset )
00855   {
00856     if( UTCOffset < 0 )
00857       return false;
00858     m_opt.useGPSLabel = true;
00859     m_opt.UTCOffset = UTCOffset;
00860     return true;
00861   }
00862 
00863   bool Plot2D::SetBackgroundColor( const CPLOT_enumColor color )
00864   {
00865     m_opt.figureBackgroundColor = color;
00866     return true;
00867   }
00868     
00869 
00870   void Plot2D::EnablePlotStatistics( const bool enable )
00871   {
00872     m_opt.plotStatistics = enable;    
00873   }
00874 
00875   void Plot2D::SetTheSeriesLabelsBelowThePlot( const bool setBelow )
00876   {
00877     m_opt.plotLabelOnRight = !setBelow;
00878   }
00879 
00880   bool Plot2D::SavePlotToBitmapFile( std::string filename )
00881   {
00882     if( !CPLOT_SaveToFile( &m_plot, filename.c_str() ) )
00883       return false;
00884 
00885     return true;
00886   }
00887   
00888   bool Plot2D::SetSeriesInfo( const unsigned index, PLOT2D_structSeriesInfo &series )
00889   {
00890     unsigned i = 0;
00891     unsigned n = 0;         // The number of rows in the file.
00892     double *X = NULL;       // The X array.
00893     double *Y = NULL;       // The Y array.
00894 
00895     double xmin = 0;
00896     double xmax = 0;
00897     double ymin = 0;
00898     double ymax = 0;
00899     
00900     if( index >= 12 )
00901       return false;
00902 
00903     if( index >= (unsigned)m_opt.numberOfSeries )
00904       return false;
00905 
00906     // Check if there is a need to load the data file or was it the last one used.
00907     if( m_prev_datapath.compare( series.DataPath ) != 0 )
00908     {
00909       if( m_mtx.data != NULL )
00910       {
00911         free( m_mtx.data );
00912         m_mtx.data = NULL;
00913         m_mtx.ncols = 0;
00914         m_mtx.nrows = 0;
00915       }      
00916       if( series.DataPath.compare( "stdin" ) == 0 )
00917       {
00918         // Data is input from stdin.
00919         // Read all the data line by line and save to a temporary file.
00920         FILE* fid = NULL;
00921         char buffer[8192];
00922         size_t buffer_length = 0;
00923         fid = fopen("_plot2d_stdin_.tmp", "w" );
00924         if( fid == NULL )        
00925           return false;
00926         do
00927         {
00928           if( fgets( buffer, 8192, stdin ) == NULL )
00929             break;
00930           buffer_length = strlen( buffer );
00931           if( buffer_length > 0 )
00932             fprintf( fid, buffer ); // newline is included in fgets          
00933         }
00934         while( buffer_length > 1 );
00935         fclose(fid);
00936 
00937         if( !_PLOT2D_ReadFromFile( &m_mtx, "_plot2d_stdin_.tmp" ) )
00938           return false;
00939 
00940         // Get rid of the temporary file.
00941         remove( "_plot2d_stdin_.tmp" );
00942       }
00943       else
00944       {
00945         if( !_PLOT2D_ReadFromFile( &m_mtx, series.DataPath.c_str() ) )
00946           return false;
00947       }
00948       m_prev_datapath = series.DataPath;
00949     }
00950 
00951     // Check special case of a single column matrix input.
00952     if( m_mtx.ncols == 1 && series.X_Column == 0 && series.Y_Column == 1 )
00953     {
00954       // Change the series column indexes to allow for plotting of the single column
00955       // The x column will be a 1 based index into the column vector.
00956       series.Y_Column = 0;
00957     }
00958 
00959     // Check the indices provided.
00960     if( series.X_Column >= m_mtx.ncols )
00961     {
00962       return false;
00963     }
00964     if( series.Y_Column >= m_mtx.ncols )
00965     {
00966       return false;
00967     }
00968 
00969     n = m_mtx.nrows;
00970 
00971     // Allocate memory for the vectors required.
00972     X = (double*)malloc( n*sizeof(double) );
00973     if( !X )
00974     {      
00975       return false;
00976     }
00977     Y = (double*)malloc( n*sizeof(double) );
00978     if( !Y )
00979     {
00980       free( X );      
00981       return false;
00982     }
00983 
00984     // Copy the columns used.
00985     if( series.X_Column == series.Y_Column && series.Y_Column == 0 && m_mtx.ncols == 1 )
00986     {
00987       for( i = 0; i < n; i++ )
00988       { 
00989         X[i] = i+1.0;
00990         Y[i] = m_mtx.data[i*m_mtx.ncols + series.Y_Column];
00991       }
00992     }
00993     else
00994     {
00995       for( i = 0; i < n; i++ )
00996       {      
00997         X[i] = m_mtx.data[i*m_mtx.ncols + series.X_Column];
00998         Y[i] = m_mtx.data[i*m_mtx.ncols + series.Y_Column];
00999       }
01000     }
01001     if( series.negate_X )
01002     {
01003       for( i = 0; i < n; i++ )
01004       {      
01005         X[i] *= -1;
01006       }
01007     }
01008     if( series.negate_Y )
01009     {
01010       for( i = 0; i < n; i++ )
01011       {      
01012         Y[i] *= -1;
01013       }
01014     }
01015     
01016     if( m_Series[index].X != NULL )
01017     {
01018       // The data is already set for this index. That's not good.
01019       free( m_Series[index].X );
01020       m_Series[index].X = NULL;
01021     }
01022     if( m_Series[index].Y != NULL )
01023     {
01024       // The data is already set for this index. That's not good.
01025       free( m_Series[index].Y );
01026       m_Series[index].Y = NULL;
01027     }
01028     m_Series[index].X = X;
01029     m_Series[index].Y = Y;
01030     m_Series[index].label = (char*)series.Label.c_str();
01031     m_Series[index].units = (char*)series.Units.c_str();
01032     m_Series[index].n = n;
01033     m_Series[index].connected = series.IsConnected;
01034     m_Series[index].markOutlierData = series.MarkOutlierData;
01035     m_Series[index].precision = series.Precision;
01036     m_Series[index].color = series.Color;
01037 
01038 
01039     // Determine the maximum and minimum values for this series.    
01040     // Why? because if we are plotting multiple series, we first have to
01041     // determine the windows maximum, minimums.
01042     xmin = DBL_MAX;
01043     xmax = -DBL_MAX;
01044     for( i = 0; i < n; i++ )
01045     {
01046       // ignore NaN and INF.
01047       if( CPLOT_IsNAN( X[i] ) || CPLOT_IsPostiveINF( X[i] ) || CPLOT_IsNegativeINF( X[i] ) )
01048         continue;
01049 
01050       if( X[i] < xmin )
01051       {
01052         xmin = X[i];
01053       }
01054       if( X[i] > xmax )
01055       {
01056         xmax = X[i];
01057       }            
01058     }
01059 
01060     ymin = DBL_MAX;
01061     ymax = -DBL_MAX;
01062     for( i = 0; i < n; i++ )
01063     {
01064       // ignore NaN and INF.
01065       if( CPLOT_IsNAN( Y[i] ) || CPLOT_IsPostiveINF( Y[i] ) || CPLOT_IsNegativeINF( Y[i] ) )
01066         continue;
01067 
01068       if( Y[i] < ymin )
01069       {
01070         ymin = Y[i];
01071       }
01072       if( Y[i] > ymax )
01073       {
01074         ymax = Y[i];
01075       }
01076     }
01077 
01078     if( xmin < m_xmin )
01079       m_xmin = xmin;
01080     if( xmax > m_xmax )
01081       m_xmax = xmax;
01082     if( ymin < m_ymin )
01083       m_ymin = ymin;
01084     if( ymax > m_ymax )
01085       m_ymax = ymax;
01086     
01087     return true;
01088   }
01089 
01090   bool Plot2D::PlotAll()
01091   {
01092     int i = 0;
01093     double val = 0;
01094     typedef struct 
01095     {
01096       double lowerlimit;
01097       double upperlimit;
01098       double tickstart;
01099       double ticksize;
01100       double tickend;
01101     } _structAxis;
01102     _structAxis x;
01103     _structAxis y;
01104     
01105     // First deal with determining the full window size 
01106     // use the max min values already determined for all series if needed.
01107     // We do this because otherwise the plot window dimensions are determined
01108     // by the first series to be plotted.
01109 
01110     // Special case - all defaults indicated
01111     if( !m_opt.x.lowerlimit.doNotUseDefault &&
01112       !m_opt.x.upperlimit.doNotUseDefault &&
01113       !m_opt.x.tickstart.doNotUseDefault &&
01114       !m_opt.x.tickend.doNotUseDefault )
01115     {
01116       val = m_xmin;
01117       if( val < 0 )
01118       {
01119         val = -ceil( -val * 10.0 ) / 10.0;
01120       }
01121       else
01122       {
01123         val = floor( val * 10.0 ) / 10.0;        
01124       }
01125       x.lowerlimit = val;
01126 
01127       val = m_xmax;
01128       if( val < 0 )
01129       {
01130         val = -floor( -val * 10.0 ) / 10.0;      
01131       }
01132       else
01133       {
01134         val = ceil( val * 10.0 ) / 10.0;        
01135       }
01136       x.upperlimit = val;
01137 
01138 
01139       if( x.lowerlimit == x.upperlimit )
01140       {
01141         x.lowerlimit -= x.lowerlimit/100.0;
01142         x.upperlimit += x.upperlimit/100.0;
01143       }
01144 
01145       x.tickstart = x.lowerlimit;
01146       x.ticksize = (x.upperlimit-x.lowerlimit)/5.0; 
01147       x.tickend = x.upperlimit;
01148     }
01149     else 
01150     {  
01151       // deal with mixed or all user specified case
01152 
01153       if( m_opt.x.lowerlimit.doNotUseDefault )
01154         x.lowerlimit = m_opt.x.lowerlimit.val;
01155       else
01156         x.lowerlimit = m_xmin;
01157 
01158       if( m_opt.x.upperlimit.doNotUseDefault )
01159         x.upperlimit = m_opt.x.upperlimit.val;
01160       else
01161         x.upperlimit = m_xmax;
01162 
01163       if( m_opt.x.tickstart.doNotUseDefault )
01164         x.tickstart = m_opt.x.tickstart.val;
01165       else
01166         x.tickstart = x.lowerlimit;
01167 
01168       if( m_opt.x.tickend.doNotUseDefault )
01169         x.tickend = m_opt.x.tickend.val;
01170       else
01171         x.tickend = x.upperlimit;
01172 
01173       if( m_opt.x.ticksize.doNotUseDefault )
01174         x.ticksize = m_opt.x.ticksize.val;
01175       else
01176         x.ticksize = (x.tickend - x.tickstart)/5.0;
01177     }
01178     if( x.lowerlimit == x.upperlimit )
01179       return false;
01180     if( x.tickstart == x.tickend )
01181       return false;
01182     if( x.ticksize <= 0.0 )
01183       return false;
01184 
01185 
01186 
01187     // Special case - all defaults indicated
01188     if( !m_opt.y.lowerlimit.doNotUseDefault &&
01189       !m_opt.y.upperlimit.doNotUseDefault &&
01190       !m_opt.y.tickstart.doNotUseDefault &&
01191       !m_opt.y.tickend.doNotUseDefault )
01192     {
01193       val = m_ymin;
01194       if( val < 0 )
01195       {
01196         val = -ceil( -val * 10.0 ) / 10.0;
01197       }
01198       else
01199       {
01200         val = floor( val * 10.0 ) / 10.0;        
01201       }
01202       y.lowerlimit = val;
01203 
01204       val = m_ymax;
01205       if( val < 0 )
01206       {
01207         val = -floor( -val * 10.0 ) / 10.0;      
01208       }
01209       else
01210       {
01211         val = ceil( val * 10.0 ) / 10.0;        
01212       }
01213       y.upperlimit = val;
01214 
01215 
01216       if( y.lowerlimit == y.upperlimit )
01217       {
01218         y.lowerlimit -= y.lowerlimit/100.0;
01219         y.upperlimit += y.upperlimit/100.0;
01220       }
01221 
01222       y.tickstart = y.lowerlimit;
01223       y.ticksize = (y.upperlimit-y.lowerlimit)/10.0; 
01224       y.tickend = y.upperlimit;
01225     }
01226     else 
01227     {  
01228       // deal with mixed or all user specified case
01229 
01230       if( m_opt.y.lowerlimit.doNotUseDefault )
01231         y.lowerlimit = m_opt.y.lowerlimit.val;
01232       else
01233         y.lowerlimit = m_ymin;
01234 
01235       if( m_opt.y.upperlimit.doNotUseDefault )
01236         y.upperlimit = m_opt.y.upperlimit.val;
01237       else
01238         y.upperlimit = m_ymax;
01239 
01240       if( m_opt.y.tickstart.doNotUseDefault )
01241         y.tickstart = m_opt.y.tickstart.val;
01242       else
01243         y.tickstart = y.lowerlimit;
01244 
01245       if( m_opt.y.tickend.doNotUseDefault )
01246         y.tickend = m_opt.y.tickend.val;
01247       else
01248         y.tickend = y.upperlimit;
01249 
01250       if( m_opt.y.ticksize.doNotUseDefault )
01251         y.ticksize = m_opt.y.ticksize.val;
01252       else
01253         y.ticksize = (y.tickend - y.tickstart)/10.0;
01254     }
01255     if( y.lowerlimit == y.upperlimit )
01256       return false;
01257     if( y.tickstart == y.tickend )
01258       return false;  
01259     if( y.ticksize <= 0.0 )
01260       return false;
01261 
01262     // All the values for lower, upper, and ticks are now set.
01263 
01264     m_opt.x.lowerlimit.val = x.lowerlimit;
01265     m_opt.x.upperlimit.val = x.upperlimit;
01266     m_opt.x.tickstart.val = x.tickstart;
01267     m_opt.x.ticksize.val = x.ticksize;
01268     m_opt.x.tickend.val = x.tickend;
01269 
01270     m_opt.x.lowerlimit.doNotUseDefault = TRUE;
01271     m_opt.x.upperlimit.doNotUseDefault = TRUE;
01272     m_opt.x.tickstart.doNotUseDefault = TRUE;
01273     m_opt.x.ticksize.doNotUseDefault = TRUE;
01274     m_opt.x.tickend.doNotUseDefault = TRUE;
01275 
01276     m_opt.y.lowerlimit.val = y.lowerlimit;
01277     m_opt.y.upperlimit.val = y.upperlimit;
01278     m_opt.y.tickstart.val = y.tickstart;
01279     m_opt.y.ticksize.val = y.ticksize;
01280     m_opt.y.tickend.val = y.tickend;
01281 
01282     m_opt.y.lowerlimit.doNotUseDefault = TRUE;
01283     m_opt.y.upperlimit.doNotUseDefault = TRUE;
01284     m_opt.y.tickstart.doNotUseDefault = TRUE;
01285     m_opt.y.ticksize.doNotUseDefault = TRUE;
01286     m_opt.y.tickend.doNotUseDefault = TRUE;
01287 
01288     if( !CPLOT_SetPlotOptions( &m_plot, &m_opt ) )
01289       return false;
01290     m_ArePlotOptionsSet = true;
01291 
01292     for( i = 0; i < m_opt.numberOfSeries; i++ )
01293     {
01294       if( m_Series[i].X == NULL ||  m_Series[i].Y == NULL )
01295         return false;
01296 
01297       if( !CPLOT_Plot( &m_plot, &m_Series[i] ) )
01298       {
01299         free( m_Series[i].X );
01300         free( m_Series[i].Y );
01301         m_Series[i].X = NULL;
01302         m_Series[i].Y = NULL;        
01303         return false;
01304       }
01305 
01306       free( m_Series[i].X );
01307       free( m_Series[i].Y );
01308       m_Series[i].X = NULL;
01309       m_Series[i].Y = NULL;
01310     }
01311 
01312     return true;
01313   }
01314 
01315   bool Plot2D::Plot( PLOT2D_structSeriesInfo &series )
01316   {
01317     unsigned i = 0;
01318     unsigned n = 0;         // The number of rows in the file.
01319     double *X = NULL;       // The X array.
01320     double *Y = NULL;       // The Y array.
01321     CPLOT_structSeries s;   // The deep level series struct.
01322 
01323     memset( &s, 0, sizeof(CPLOT_structSeries) );
01324     
01325     if( !m_ArePlotOptionsSet )
01326     {
01327       if( !CPLOT_SetPlotOptions( &m_plot, &m_opt ) )
01328         return false;
01329       m_ArePlotOptionsSet = true;
01330     }
01331 
01332     if( m_prev_datapath.compare( series.DataPath ) != 0 )
01333     {
01334       if( m_mtx.data != NULL )
01335       {
01336         free( m_mtx.data );
01337         m_mtx.data = NULL;
01338         m_mtx.ncols = 0;
01339         m_mtx.nrows = 0;
01340       }
01341       if( !_PLOT2D_ReadFromFile( &m_mtx, series.DataPath.c_str() ) )
01342         return false;
01343       m_prev_datapath = series.DataPath;
01344     }
01345 
01346     // Check the indices provided.
01347     if( series.X_Column >= m_mtx.ncols )
01348     {
01349       return false;
01350     }
01351     if( series.Y_Column >= m_mtx.ncols )
01352     {
01353       return false;
01354     }
01355 
01356     n = m_mtx.nrows;
01357 
01358     // Allocate memory for the vectors required.
01359     X = (double*)malloc( n*sizeof(double) );
01360     if( !X )
01361     {
01362       return false;
01363     }
01364     Y = (double*)malloc( n*sizeof(double) );
01365     if( !Y )
01366     {
01367       free( X );
01368       return false;
01369     }
01370 
01371     // Copy the columns used.
01372     for( i = 0; i < n; i++ )
01373     {
01374       X[i] = m_mtx.data[i*m_mtx.ncols + series.X_Column];
01375       Y[i] = m_mtx.data[i*m_mtx.ncols + series.Y_Column];
01376     }
01377 
01378     s.X = X;
01379     s.Y = Y;
01380     s.label = (char*)series.Label.c_str();
01381     s.units = (char*)series.Units.c_str();
01382     s.n = n;
01383     s.connected = series.IsConnected;
01384     s.markOutlierData = series.MarkOutlierData;
01385     s.precision = series.Precision;
01386     s.color = series.Color;
01387     
01388     if( !CPLOT_Plot( &m_plot, &s ) )
01389     {
01390       free( X );
01391       free( Y );
01392       X = NULL;
01393       Y = NULL;
01394       s.X = NULL;
01395       s.Y = NULL;        
01396       return false;
01397     }
01398 
01399     free( X );
01400     free( Y );
01401     X = NULL;
01402     Y = NULL;
01403     s.X = NULL;
01404     s.Y = NULL;    
01405     return true;
01406   }
01407 
01408 
01409 
01410   
01411 } // end of namespace Plot2D;
01412