Main Page | Modules | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

omggraph.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           omggraph.cpp  -  description
00003                              -------------------
00004     begin                : April 2007
00005     copyright            : (C) 2007 by Tim Sutton
00006     email                : tim@linfiniti.com
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "omggraph.h"
00019 #include "omgui.h"
00020 #include <QListIterator>
00021 #include <QPainter>
00022 #include <QPainterPath>
00023 #include <QPen>
00024 #include <QFontMetrics>
00025 #include <QTime>
00026 #include <cmath> //needed for fabs
00027 
00028 OmgGraph::OmgGraph(QPainter * thepPainter)
00029 {
00030   mpPainter = thepPainter;
00031   initialise();
00032 }
00033 //private - one of the above should be used
00034 OmgGraph::OmgGraph()
00035 {
00036 }
00037 OmgGraph::~OmgGraph()
00038 {
00039 }
00040 
00041 void OmgGraph::initialise()
00042 {
00043   mYAxisMax=0.0;
00044   mYAxisMin=0.0;
00045   mXAxisMin=0.0;
00046   mXAxisMax=0.0;
00047   mImageWidth = mpPainter->device()->width();
00048   mImageHeight = mpPainter->device()->height();
00049   mXGutterWidth=0;
00050   mYGutterWidth=0;
00051   mGradientWidth=mImageWidth;
00052   mGradientHeight=mImageHeight;
00053   mGridLinesFlag=true;
00054   mDrawDiagonal=false;
00055   mShowVerticesFlag=false;
00056   mShowVertexLabelsFlag=false;
00057   mSplinePointsFlag=false;
00058   mAreaFillFlag=false;
00059   mLegendHeight=0;
00060   QFont myFont("arial", 10, QFont::Normal);
00061   mAxisFont = myFont;
00062   mLegendFont = myFont;
00063   mVertexLabelFont = myFont;
00064 }
00065  
00066 void OmgGraph::setAxisFont(QFont theFont)
00067 {
00068   mAxisFont=theFont;
00069 }
00070 void OmgGraph::setLegendFont(QFont theFont)
00071 {
00072   mLegendFont=theFont;
00073 }
00074 void OmgGraph::setVertexLabelFont(QFont theFont)
00075 {
00076   mVertexLabelFont=theFont;
00077 }
00078 void OmgGraph::setGridLinesEnabled(bool theFlag)
00079 {
00080   mGridLinesFlag=theFlag;
00081 }
00082 bool OmgGraph::hasGridLinesEnabled()
00083 {
00084   return mGridLinesFlag;
00085 }
00086 
00087 void OmgGraph::setDiagonalEnabled(bool theFlag)
00088 {
00089   mDrawDiagonal=theFlag;
00090 }
00091 bool OmgGraph::hasDiagonalEnabled()
00092 {
00093   return mDrawDiagonal;
00094 }
00095 
00096 void OmgGraph::setVerticesEnabled(bool theFlag)
00097 {
00098   mShowVerticesFlag=theFlag;
00099 }
00100 bool OmgGraph::hasVerticesEnabled()
00101 {
00102   return mShowVerticesFlag;
00103 }
00104 void OmgGraph::setVertexLabelsEnabled(bool theFlag)
00105 {
00106   mShowVertexLabelsFlag=theFlag;
00107 }
00108 bool OmgGraph::hasVertexLabelsEnabled()
00109 {
00110   return mShowVertexLabelsFlag;
00111 }
00112 void OmgGraph::setSpliningEnabled(bool theFlag)
00113 {
00114   mSplinePointsFlag=theFlag;
00115 }
00116 bool OmgGraph::hasSpliningEnabled()
00117 {
00118   return mSplinePointsFlag;
00119 }
00120 void OmgGraph::setAreaFillEnabled(bool theFlag)
00121 {
00122   mAreaFillFlag=theFlag;
00123 }
00124 bool OmgGraph::hasAreaFillEnabled()
00125 {
00126   return mAreaFillFlag;
00127 }
00128 
00129 void OmgGraph::clear(QColor theColour)
00130 {
00131   mpPainter->fillRect( QRect(0,0,mImageWidth,mImageHeight), theColour );
00132 }
00133 
00134 void OmgGraph::addSeries(OmgDataSeries theSeries)
00135 {
00136   mSeriesList.append(theSeries);
00137 }
00138 bool OmgGraph::removeSeriesAt(int theSeriesNo)
00139 {
00140   if ((theSeriesNo < mSeriesList.count()) && (theSeriesNo >= 0))
00141   {
00142     mSeriesList.removeAt(theSeriesNo);
00143     return true;
00144   }
00145   else
00146   {
00147     return false;
00148   }
00149 }
00150 int OmgGraph::seriesCount() const
00151 {
00152   return mSeriesList.count();
00153 }
00154 
00155 void OmgGraph::render( )
00156 {
00157   if (mSeriesList.size() < 1)
00158   {
00159     qDebug("No graph drawn as no data series added");
00160     return;
00161   }
00162   //
00163   // First scan through to get max and min cell counts
00164   //
00165   bool myFirstItemFlag=true;
00166   QListIterator< OmgDataSeries > mySeriesIterator( mSeriesList );
00167   while (mySeriesIterator.hasNext()) 
00168   {
00169     OmgDataSeries mySeries = mySeriesIterator.next();
00170     //qDebug("Series xMin : " + QString::number(mySeries.xMin()).toLocal8Bit());
00171     //qDebug("Series xMax : " + QString::number(mySeries.xMax()).toLocal8Bit());
00172     //qDebug("Series yMin : " + QString::number(mySeries.yMin()).toLocal8Bit());
00173     //qDebug("Series yMax : " + QString::number(mySeries.yMax()).toLocal8Bit());
00174     if (mXAxisMax < mySeries.xMax() || myFirstItemFlag)
00175     {
00176       mXAxisMax = mySeries.xMax();
00177     }
00178     if (mXAxisMin > mySeries.xMin() || myFirstItemFlag)
00179     {
00180       mXAxisMin = mySeries.xMin();
00181     }
00182     if (mYAxisMax < mySeries.yMax() || myFirstItemFlag)
00183     {
00184       mYAxisMax = mySeries.yMax();
00185     }
00186     if (mYAxisMin > mySeries.yMin() || myFirstItemFlag)
00187     {
00188       mYAxisMin = mySeries.yMin();
00189     }
00190     myFirstItemFlag=false;
00191   }
00192   //qDebug("Graph xMin : " + QString::number(mXAxisMin).toLocal8Bit());
00193   //qDebug("Graph xMax : " + QString::number(mXAxisMax).toLocal8Bit());
00194   //qDebug("Graph yMin : " + QString::number(mYAxisMin).toLocal8Bit());
00195   //qDebug("Graph yMax : " + QString::number(mYAxisMax).toLocal8Bit());
00196   // rather than make the chart run right to the borders
00197   // of the graph area, we will add 1/100th space
00198   // at the top ...
00199   //mYAxisMax += (mYAxisMax - mYAxisMin)/100;
00200 
00201   //
00202   // Now calculate the graph drawable area after the axis labels have been taken
00203   // into account
00204   //
00205   calculateLegendHeight();
00206   calculateGutters();
00207   mGraphImageWidth = mImageWidth-mYGutterWidth; 
00208   mGraphImageHeight = mImageHeight-(mXGutterHeight+mLegendHeight); 
00209   QImage myImage(mGraphImageWidth,mGraphImageHeight,
00210       QImage::Format_ARGB32);
00211   QPainter myPainter(&myImage);
00212   myPainter.fillRect(QRectF(0,0,mGraphImageWidth,mGraphImageHeight),Qt::white);
00213   myPainter.setRenderHint(QPainter::Antialiasing);
00214   //
00215   // determine the scaling factor for x and y axis
00216   // to do this we use:
00217   //  ( image width / x max ) * x
00218   //  ( image height / y max ) * y
00219   //  ( 500 / 40 ) * 20 = 250
00220   //  ( 500 / 1000 ) * 800 = 400 
00221 
00222   float myXScaleFactor =  mGraphImageWidth / (mXAxisMax-mXAxisMin) ;
00223   float myYScaleFactor =  mGraphImageHeight / (mYAxisMax-mYAxisMin) ;
00224   
00225   QString myString = "\nX Scale Factor graph width / (xmax-xmin): " 
00226                    + QString::number(mGraphImageWidth) + "/"
00227                    + QString::number(mXAxisMax-mXAxisMin) + " = "
00228                    + QString::number(myXScaleFactor);
00229   myString        += "\nY Scale Factor graph height / (ymax - ymin): " 
00230                    + QString::number(mGraphImageHeight) + "/"
00231                    + QString::number(mYAxisMax-mYAxisMin) + " = "
00232                    + QString::number(myYScaleFactor);
00233   //qDebug(myString);
00234 
00235 
00236   
00237   //
00238   // now draw actual line graph
00239   //
00240   float myX=0.0;
00241   float myY=0.0;
00242   mySeriesIterator.toFront();
00243   while (mySeriesIterator.hasNext()) 
00244   {
00245     OmgDataSeries mySeries = mySeriesIterator.next();
00246     QPainterPath myPath;
00247     //make sure the line starts in the bottom left corner
00248     myX = 0;
00249     myY = 0;
00250     myY = mGraphImageHeight - myY;
00251     QPointF myLastPoint ( myX, myY );
00252     //this will be ignored / overridden in non fill mode
00253     myPath.moveTo( myLastPoint );
00254     //set the pen for the path outline
00255     QPen myPen;
00256     myPen.setWidth(3);
00257     myPen.setStyle(Qt::SolidLine);
00258     myPainter.setPen( myPen );
00259     //start iterating through the provided points
00260     //draw a line on the graph along the bar peaks; 
00261     int myXCounter = 0;
00262     for (int myCounter = 0; myCounter < mySeries.size(); ++ myCounter)
00263     {
00264       //qDebug("Getting x,y no " + QString::number(myCounter).toLocal8Bit() + " from series");
00265       myX = mySeries.xAt(myCounter);
00266       myY = mySeries.yAt(myCounter);
00267       // scale the values into the graph frame
00268       if (myX==mXAxisMin)
00269       {
00270         myX=0;
00271       }
00272       else
00273       {
00274         myX = ( myXScaleFactor * (myX - mXAxisMin) ) ;
00275       }
00276       if (myY==mYAxisMin)
00277       {
00278         myY=mGraphImageHeight;
00279       }
00280       else
00281       {
00282         myY = ( myYScaleFactor * (myY - mYAxisMin) ) ;
00283         myY = mGraphImageHeight - myY;
00284       }
00285       // store this point in our line 
00286       QPointF myPoint ( myX, myY );
00287       if (mSplinePointsFlag && myXCounter != 0) //dont bother splining first point
00288       {
00289         //control points for splining points into a curve
00290         float myControl1X=0.0;
00291         float myControl1Y=0.0;
00292         float myControl2X=0.0;
00293         float myControl2Y=0.0;
00294         const int XSTEEPNESS=2;
00295         const int YSTEEPNESS=2;
00296         if (myPoint.x() > myLastPoint.x())
00297         {
00298           myControl1X = myLastPoint.x();
00299           myControl2X = myLastPoint.x()+(fabs(myPoint.x()-myLastPoint.x())/XSTEEPNESS);
00300         }
00301         else
00302         {
00303           myControl1X = myLastPoint.x()-(fabs(myLastPoint.x()-myPoint.x())/XSTEEPNESS);
00304           myControl2X = myLastPoint.x();
00305         }
00306         if (myPoint.y() > myLastPoint.y())
00307         {
00308           myControl1Y = myPoint.y();
00309           myControl2Y = myLastPoint.y()+(fabs(myPoint.y()-myLastPoint.y())/YSTEEPNESS);
00310         }
00311         else
00312         {
00313           myControl1Y = myLastPoint.y()-(fabs(myLastPoint.y()-myPoint.y())/YSTEEPNESS);
00314           myControl2Y = myPoint.y();
00315         }
00316         if (myControl1Y < 0 ) myControl1Y=0;
00317         if (myControl2Y < 0 ) myControl2Y=0;
00318         if (myControl1Y > mGraphImageHeight) myControl1Y=mGraphImageHeight;
00319         if (myControl2Y > mGraphImageHeight) myControl2Y=mGraphImageHeight;
00320 
00321         if (myControl1X < 0) myControl1X=0;
00322         if (myControl2X < 0) myControl2X=0;
00323         if (myControl1X > mGraphImageWidth) myControl1X=mGraphImageWidth;
00324         if (myControl2X > mGraphImageWidth) myControl2X=mGraphImageWidth;
00325 
00326         QPointF myControlPoint1(myControl1X,myControl1Y);
00327         QPointF myControlPoint2(myControl2X,myControl2Y);
00328         myPainter.drawEllipse(static_cast<int>(myControl1X),static_cast<int>(myControl1Y),5,5);
00329         myPainter.drawText(static_cast<int>(myControl1X),static_cast<int>(myControl1Y),QString::number(myXCounter));
00330         myPainter.drawEllipse(static_cast<int>(myControl2X),static_cast<int>(myControl2Y),5,5);
00331         myPainter.drawText(static_cast<int>(myControl2X),static_cast<int>(myControl2Y),QString::number(myXCounter));
00332         //connect last point to this using spline curve
00333         myPath.cubicTo(myControlPoint1, myControlPoint2, myPoint);
00334         //myPath.cubicTo(myLastPoint, myPoint, myPoint);
00335       }
00336       else //just draw straight lines between vertices
00337       {
00338         if (myXCounter==0)
00339         {
00340           //just move to the point dont draw it in
00341           myPath.moveTo(myPoint);
00342         }
00343         else
00344         {
00345           myPath.lineTo(myPoint);
00346         }
00347       }
00348       myLastPoint = myPoint;
00349       ++myXCounter;
00350     }
00351     if (mAreaFillFlag)
00352     {
00353       //close of the point array so it makes a nice polygon
00354       //bottom right point
00355       myPath.lineTo( QPointF( 
00356             myLastPoint.x()  ,
00357             mImageHeight));
00358       //bottom left point
00359       myPath.lineTo( QPointF( 
00360             0, 
00361             mImageHeight));
00362       //set a gradient fill for the path
00363       QLinearGradient myGradient = customGradient(mySeries.fillColor());
00364       myPainter.setBrush(myGradient);
00365     }
00366     myPen.setColor(mySeries.lineColor());
00367     myPainter.setPen( myPen );
00368     //create the path and draw it
00369     myPainter.drawPath(myPath);
00370 
00371   }
00372   
00373   //
00374   // now paint in the vertices and vertex labels
00375   //
00376   mySeriesIterator.toFront();
00377   while (mySeriesIterator.hasNext()) 
00378   {
00379     OmgDataSeries mySeries = mySeriesIterator.next();
00380     QPen myPen;
00381     myPen.setWidth(5);
00382     myPen.setColor(mySeries.lineColor());
00383     myPainter.setPen( myPen );
00384     QFont myQFont("arial", 10, QFont::Normal);
00385     myPainter.setFont(myQFont);
00386     for (int myCounter = 0; myCounter < mySeries.size(); ++ myCounter)
00387     {
00388       myX = mySeries.xAt(myCounter);
00389       myY = mySeries.yAt(myCounter);
00390       QString myString = QString::number( myX  ) +
00391         " , " + QString::number( myY );
00392       //QString myString = "  "  + QString::number( myY );
00393 
00394       // scale the values into the graph frame
00395       if (myX!=mXAxisMin)
00396       {
00397         myX = ( myXScaleFactor * (myX - mXAxisMin) );
00398       }
00399       if (myY!=mYAxisMin)
00400       {
00401         myY = ( myYScaleFactor * (myY - mYAxisMin) ) ;
00402         myY = mGraphImageHeight - myY;
00403       }
00404       //
00405       // Uncomment this when debugging if you want
00406       // to see the scaled values printed on the chart
00407       //
00408       //myString += "\n   "  + QString::number( myX  ) +
00409       //  " , " + QString::number( myY );
00410       //qDebug(myString);
00411       if (mShowVerticesFlag)
00412       {
00413         myPen.setWidth(1);
00414         myPainter.drawEllipse(static_cast<int>(myX-2),static_cast<int>(myY-2),4,4);
00415       }
00416       if (mShowVertexLabelsFlag)
00417       {
00418         myPainter.setFont(mVertexLabelFont);
00419         QFontMetrics myFontMetrics( mVertexLabelFont );
00420         int myWidth = myFontMetrics.width(myString); //width of text
00421         int myHeight = myFontMetrics.height();
00422         myPen.setWidth(1);
00423         myPen.setColor(mySeries.lineColor());
00424         myPainter.setPen(myPen);
00425         myPainter.setBrush(mySeries.fillColor());
00426         //draw a rounded rect around the label the magic numbers below
00427         //just add a little padding so the label does not ride up
00428         //against the edge of the rect
00429         QPointF myTopLeftPoint =QPointF( myX + 3 , myY-(myHeight/2)-2);
00430         QPointF myBottomRightPoint = QPointF( myX+myWidth+10 , myY+(myHeight/2)+2 );
00431         myPainter.drawRoundRect ( QRectF(myTopLeftPoint,myBottomRightPoint),80,60 );
00432         //now paint the label itself
00433         myPen.setColor(Qt::black);
00434         myPainter.setPen(myPen);
00435         myPainter.drawText(QRectF(myTopLeftPoint,myBottomRightPoint),
00436                Qt::AlignCenter || Qt::AlignVCenter,myString);
00437       }
00438     }
00439   }
00440   //
00441   // Finish up
00442   //
00443   //
00444   // Draw the axes first otherwise if grid lines are
00445   // enabled they will overdraw labels which looks bad
00446   //
00447   drawAxes();
00448   if (mDrawDiagonal) 
00449   {
00450     drawDiagonal();
00451   }
00452   //draw the chart part onto the main image
00453   myPainter.end();
00454   mpPainter->drawImage(
00455       QPointF(mYGutterWidth,
00456               0
00457         ),myImage);
00458   makeLegend();
00459 }
00460 
00461 QLinearGradient OmgGraph::redGradient()
00462 {
00463   //define a gradient
00464   QLinearGradient myGradient = QLinearGradient(mGradientWidth,0,mGradientWidth,mGradientHeight);
00465   myGradient.setColorAt(0.0,QColor(242, 14, 25, 190));
00466   myGradient.setColorAt(0.7,QColor(175, 29, 37, 190));
00467   myGradient.setColorAt(1.0,QColor(114, 17, 22, 190));
00468   return myGradient;
00469 }
00470 QLinearGradient OmgGraph::greenGradient()
00471 {
00472   //define a gradient 
00473   QLinearGradient myGradient = QLinearGradient(mGradientWidth,0,mGradientWidth,mGradientHeight);
00474   myGradient.setColorAt(0.0,QColor(48, 168, 5, 190));
00475   myGradient.setColorAt(0.7,QColor(36, 122, 4, 190));
00476   myGradient.setColorAt(1.0,QColor(21, 71, 2, 190));
00477   return myGradient;
00478 }
00479 QLinearGradient OmgGraph::blueGradient()
00480 {
00481   //define a gradient 
00482   QLinearGradient myGradient = QLinearGradient(mGradientWidth,0,mGradientWidth,mGradientHeight);
00483   myGradient.setColorAt(0.0,QColor(30, 0, 106, 190));
00484   myGradient.setColorAt(0.7,QColor(30, 72, 128, 190));
00485   myGradient.setColorAt(1.0,QColor(30, 223, 196, 190));
00486   return myGradient;
00487 }
00488 QLinearGradient OmgGraph::grayGradient()
00489 {
00490   //define a gradient 
00491   QLinearGradient myGradient = QLinearGradient(mGradientWidth,0,mGradientWidth,mGradientHeight);
00492   myGradient.setColorAt(0.0,QColor(5, 5, 5, 190));
00493   myGradient.setColorAt(0.7,QColor(122, 122, 122, 190));
00494   myGradient.setColorAt(1.0,QColor(220, 220, 220, 190));
00495   return myGradient;
00496 }
00497 QLinearGradient OmgGraph::randomGradient()
00498 {
00499   //define a gradient 
00500   QLinearGradient myGradient = QLinearGradient(mGradientWidth,0,mGradientWidth,mGradientHeight);
00501   QColor myColour = Omgui::randomColour();
00502   myGradient.setColorAt(0.0,myColour.lighter()); //lighter introduced in qt4.3
00503   myGradient.setColorAt(0.7,myColour);
00504   myGradient.setColorAt(1.0,myColour.darker()); //darker circa qt4.3
00505   return myGradient;
00506 }
00507 QLinearGradient OmgGraph::customGradient(QColor theColour)
00508 {
00509   //if the user didnt set alpha transparency in the colour lets force some ok?
00510   if (theColour.alphaF()==1)
00511   {
00512     theColour.setAlphaF(0.6);
00513   }
00514   //define a gradient 
00515   QLinearGradient myGradient = QLinearGradient(mGradientWidth,0,mGradientWidth,mGradientHeight);
00516   myGradient.setColorAt(0.0,theColour.lighter()); //lighter introduced in qt4.3
00517   myGradient.setColorAt(0.7,theColour);
00518   myGradient.setColorAt(1.0,theColour.darker()); //darker circa qt4.3
00519   return myGradient;
00520 }
00521 QLinearGradient OmgGraph::highlightGradient()
00522 {
00523   //define another gradient for the highlight
00524   QLinearGradient myGradient = QLinearGradient(mGradientWidth,0,mGradientWidth,mGradientHeight);
00525   myGradient.setColorAt(1.0,QColor(255, 255, 255, 50));
00526   myGradient.setColorAt(0.5,QColor(255, 255, 255, 100));
00527   myGradient.setColorAt(0.0,QColor(255, 255, 255, 150));
00528   return myGradient;
00529 }
00530 
00531 void OmgGraph::drawAxes( )
00532 {
00533   //anti alias lines in the graph
00534   mpPainter->setRenderHint(QPainter::Antialiasing);
00535   //determine labels sizes and draw them
00536   mpPainter->setFont(mAxisFont);
00537   QString myYMaxLabel = QString::number(static_cast < unsigned int >(mYAxisMax));
00538   QString myXMinLabel = QString::number(mXAxisMin);
00539   QString myXMaxLabel = QString::number(mXAxisMax);
00540 
00541   //
00542   // Now draw interval markers on the x axis
00543   //
00544   int myXDivisions = mGraphImageWidth/10;
00545   mpPainter->setPen( Qt::gray );
00546   for (int i=0;i<myXDivisions;++i)
00547   {
00548     QPolygon myPolygon;
00549     QPoint myPosition((i*myXDivisions)+mYGutterWidth , mImageHeight-(mXGutterHeight+mLegendHeight));
00550     myPolygon << myPosition;
00551     myPolygon << QPoint((i*myXDivisions)+mYGutterWidth , mImageHeight-((mXGutterHeight+mLegendHeight)-5));
00552     myPolygon << myPosition;
00553     myPolygon << QPoint(((i+1)*myXDivisions)+mYGutterWidth , mImageHeight-(mXGutterHeight+mLegendHeight));
00554     mpPainter->drawPolyline(myPolygon);
00555     if (mGridLinesFlag)
00556     {
00557       QPoint myTopPosition((i*myXDivisions)+mYGutterWidth , 0 );
00558       mpPainter->drawLine(myPosition,myTopPosition);
00559     }
00560   }
00561   //
00562   // Now draw interval markers on the y axis
00563   //
00564   int myYDivisions = mGraphImageHeight/10;
00565   mpPainter->setPen( Qt::gray );
00566   int myYOrigin = mImageHeight-(mXGutterHeight+mLegendHeight);
00567   for (int i=myYDivisions;i>0;--i)
00568   {
00569     QPolygon myPolygon;
00570     QPoint myPosition(mYGutterWidth,myYOrigin-(i*myYDivisions ));
00571     myPolygon << myPosition;
00572     myPolygon << QPoint(mYGutterWidth-5,myYOrigin-(i*myYDivisions ));
00573     myPolygon << myPosition;
00574     myPolygon << QPoint(mYGutterWidth,myYOrigin-((i-1)*myYDivisions ));
00575     mpPainter->drawPolyline(myPolygon);
00576     if (mGridLinesFlag)
00577     {
00578       QPoint myRightPosition(mImageWidth,myYOrigin-(i*myYDivisions ));
00579       mpPainter->drawLine(myPosition,myRightPosition);
00580     }
00581   }
00582 
00583 
00584   //now draw the axis labels onto the graph
00585   QFontMetrics myMetrics(mAxisFont);
00586   mpPainter->setPen(Qt::black);
00587   mpPainter->drawText(1, myMetrics.height(), myYMaxLabel);
00588   mpPainter->drawText(1, mImageHeight-(mXGutterHeight+mLegendHeight), QString::number(static_cast < unsigned int >(mYAxisMin)));
00589   mpPainter->drawText(mYGutterWidth,mImageHeight-(mXGutterHeight+mLegendHeight-myMetrics.height()) , myXMinLabel);
00590   mpPainter->drawText(mImageWidth-mXGutterWidth,mImageHeight-(mXGutterHeight+mLegendHeight-myMetrics.height()), myXMaxLabel );
00591 }
00592 
00593 void OmgGraph::drawDiagonal()
00594 {
00595   mpPainter->setPen( Qt::gray );
00596   QPoint myPoint1 (mYGutterWidth , mImageHeight-(mXGutterHeight+mLegendHeight));
00597   QPoint myPoint2 (mImageWidth , 0);
00598   mpPainter->drawLine (myPoint1 , myPoint2);
00599 }
00600 
00601 void OmgGraph::calculateGutters()
00602 {
00603   //
00604   // Calculate the graph drawable area after the axis labels have been taken
00605   // into account
00606   //
00607   QFont myFont("arial", 10, QFont::Normal);
00608   QFontMetrics myFontMetrics( myFont );
00609   QString myYMaxLabel = QString::number(static_cast < unsigned int >(mYAxisMax));
00610   QString myXMinLabel = QString::number(mXAxisMin);
00611   QString myXMaxLabel = QString::number(mXAxisMax);
00612   //calculate the gutters
00613   if (myFontMetrics.width(myXMinLabel) < myFontMetrics.width(myYMaxLabel))
00614   {
00615     //add 2 so we can have 1 pix whitespace either side of label
00616     mYGutterWidth = myFontMetrics.width(myYMaxLabel )+2; 
00617   }
00618   else
00619   {
00620     //add 2 so we can have 1 pix whitespace either side of label
00621     mYGutterWidth = myFontMetrics.width(myXMinLabel )+2; 
00622   }
00623   mXGutterHeight = myFontMetrics.height()+2;
00624   //1 pix whtispace from right edge of image
00625   mXGutterWidth = myFontMetrics.width(myXMaxLabel)+1;
00626 }
00627 
00628 void OmgGraph::makeLegend()
00629 {
00630   QPen myPen;
00631   myPen.setWidth(1);
00632   myPen.setStyle(Qt::SolidLine);
00633   mpPainter->setPen( myPen );
00634   //put the legend in a box
00635   //QPointF myTopLeftPoint ( 0.0 , mImageHeight-mLegendHeight);
00636   //QPointF myBottomRightPoint ( mImageWidth , mImageHeight );
00637   //last two params are amount of roundness of corners in x and y direction
00638   //mpPainter->drawRoundRect ( QRectF(myTopLeftPoint,myBottomRightPoint),80,60 );
00639   
00640   myPen.setWidth(1);
00641   int myLastXPos=20; // leave some space on the left gutter
00642   mpPainter->setFont(mLegendFont);
00643   const int myLeftSpace=20;
00644   const int myBoxWidth=20;
00645   const int myBoxToTextSpace=10;
00646   const int mySpaceAfterText=10;
00647   const int myYSpace=10;
00648   int myLastYPos=mImageHeight-(mLegendHeight-myYSpace);
00649   QFontMetrics myFontMetrics( mLegendFont );
00650   QListIterator< OmgDataSeries > mySeriesIterator( mSeriesList );
00651   while (mySeriesIterator.hasNext()) 
00652   {
00653     OmgDataSeries mySeries = mySeriesIterator.next();
00654     QString myLabel = mySeries.label();
00655     int myLabelAndMarkerWidth=0;
00656     myLabelAndMarkerWidth += myBoxWidth; 
00657     myLabelAndMarkerWidth += myBoxToTextSpace; //a little space between box and text
00658     myLabelAndMarkerWidth += myFontMetrics.width(myLabel); //width of text
00659     myLabelAndMarkerWidth += mySpaceAfterText; //a little space after the text and before the next box
00660     //we need to know if the label when added to the current
00661     //line will over run the right edge
00662     //if  it does we need to rather leave blank space to the end of the line and
00663     //add the label and its marker to the start of the next line.
00664     int myDistanceToEnd = mImageWidth - (myLastXPos % mImageWidth);
00665     if (myDistanceToEnd < myLabelAndMarkerWidth)
00666     {
00667       myLastXPos = myLeftSpace; // leave some whitespace on the left
00668       myLastYPos += myFontMetrics.height() + myYSpace;;
00669     }
00670     //draw the little legend box first
00671     QPointF myTopLeftPoint ( myLastXPos , myLastYPos);
00672     myLastXPos += myBoxWidth; 
00673     QPointF myBottomRightPoint ( myLastXPos , myLastYPos-myFontMetrics.height() );
00674     //last two params are amount of roundness of corners in x and y direction
00675     myPen.setColor(mySeries.lineColor());
00676     mpPainter->setBrush(mySeries.fillColor());
00677     mpPainter->drawRoundRect ( QRectF(myTopLeftPoint,myBottomRightPoint),80,60 );
00678     myLastXPos += myBoxToTextSpace; //a little space between box and text
00679     myTopLeftPoint =QPointF( myLastXPos , myLastYPos);
00680     myLastXPos += myFontMetrics.width(myLabel); //width of text
00681     myBottomRightPoint = QPointF( myLastXPos , myLastYPos-myFontMetrics.height() );
00682     myPen.setColor(Qt::black);
00683     mpPainter->drawText(QRectF(myTopLeftPoint,myBottomRightPoint),Qt::AlignVCenter,myLabel);
00684     myLastXPos += mySpaceAfterText; //a little space after the text and before the next box
00685     //first check we arent overrunning the space avaiable
00686     if (myLastXPos > mImageWidth)
00687     {
00688       myLastXPos = myLeftSpace; // leave some whitespace on the left
00689       myLastYPos += myFontMetrics.height() + myYSpace;;
00690     }
00691   }
00692   //QFont myQFont("arial", 15, QFont::Normal);
00693   //mpPainter->setFont(myQFont);
00694   //mpPainter->drawEllipse(myTopLeftPoint.x(),myTopLeftPoint.y(),12,12);
00695   //mpPainter->drawText(myTopLeftPoint,"TL");
00696   //mpPainter->drawEllipse(myBottomRightPoint.x()-12,myBottomRightPoint.y(),12,12);
00697   //mpPainter->drawText(myBottomRightPoint,"BR");
00698   //drawHighlight ( myTopLeftPoint, myBottomRightPoint );
00699 }
00700 
00701 void OmgGraph::calculateLegendHeight()
00702 {
00703   const int myLeftSpace=20;
00704   const int myBoxWidth=20;
00705   const int myBoxToTextSpace=10;
00706   const int mySpaceAfterText=10;
00707   const int myYSpace=10;
00708   int myTotalWidth = myLeftSpace;
00709   QFontMetrics myFontMetrics( mLegendFont );
00710   QListIterator< OmgDataSeries > mySeriesIterator( mSeriesList );
00711   while (mySeriesIterator.hasNext()) 
00712   {
00713     OmgDataSeries mySeries = mySeriesIterator.next();
00714     QString myLabel = mySeries.label();
00715     int myLabelAndMarkerWidth=0;
00716     myLabelAndMarkerWidth += myBoxWidth; 
00717     myLabelAndMarkerWidth += myBoxToTextSpace; //a little space between box and text
00718     myLabelAndMarkerWidth += myFontMetrics.width(myLabel); //width of text
00719     myLabelAndMarkerWidth += mySpaceAfterText; //a little space after the text and before the next box
00720     //we need to know if the label when added to the current
00721     //line will over run the right edge
00722     //if  it does we need to rather leave blank space to the end of the line and
00723     //add the label and its marker to the start of the next line.
00724     int myDistanceToEnd = mImageWidth - (myTotalWidth % mImageWidth);
00725     if (myDistanceToEnd < myLabelAndMarkerWidth)
00726     {
00727       myTotalWidth += myDistanceToEnd;
00728     }
00729     myTotalWidth += myLabelAndMarkerWidth;
00730   }
00731   //qDebug("Total label width:" + QString::number(myTotalWidth).toLocal8Bit());
00732   //qDebug("Total image width:" + QString::number(mImageWidth).toLocal8Bit());
00733   int myTotalHeight = 0;
00734   if ( myTotalWidth > mImageWidth )
00735   {
00736     if (myTotalWidth % mImageWidth) //not exaclty divisible
00737     {
00738       myTotalHeight = (( myTotalWidth / mImageWidth )+1) * ( myFontMetrics.xHeight() + myYSpace );
00739     }
00740     else //exactly divisible
00741     {
00742       myTotalHeight = ( myTotalWidth / mImageWidth ) * ( myFontMetrics.xHeight() + myYSpace );
00743     }
00744   }
00745   else
00746   {
00747     myTotalHeight = myFontMetrics.xHeight() + myYSpace ;
00748   }
00749   myTotalHeight += ( myYSpace * 2 ); // one more pad out for the top and bottom
00750   mLegendHeight = myTotalHeight;
00751   //qDebug("Legend Height: " + QString::number(mLegendHeight).toLocal8Bit());
00752 }
00753 
00754 void OmgGraph::drawHighlight( QPointF theTopLeftPoint, QPointF theBottomRightPoint )
00755 {
00756   // in the beginning do it all like a rounded rect
00757   const float SWEEPANGLE=90.0;
00758   float myAngle=0.0;
00759   const float WIDTH=5.0;
00760   const float HEIGHT=5.0;
00761   QPainterPath myHighlightPath;
00762   myHighlightPath.moveTo(theBottomRightPoint.x(),theTopLeftPoint.y());
00763   //top right corner
00764   myHighlightPath.arcTo(theBottomRightPoint.x()-WIDTH,theTopLeftPoint.y()-HEIGHT, WIDTH, HEIGHT, myAngle , SWEEPANGLE);
00765   myAngle+=90;
00766   //line to top left 
00767   myHighlightPath.lineTo(theTopLeftPoint.x()+WIDTH, theTopLeftPoint.y()-HEIGHT);
00768   //top left corner
00769   myHighlightPath.arcTo(theTopLeftPoint.x(), theTopLeftPoint.y()-HEIGHT, WIDTH, HEIGHT, myAngle , SWEEPANGLE);
00770   myAngle+=90;
00771   //line to two thirds way down on left
00772   myHighlightPath.lineTo(theTopLeftPoint.x(),theTopLeftPoint.y() + 
00773       ((theBottomRightPoint.y()-theTopLeftPoint.y())/3));
00774   // calculate the lower midpoint for the curve
00775   qreal myLowerMidPointX = theTopLeftPoint.x()+((theBottomRightPoint.x()-theTopLeftPoint.x())/2);
00776   qreal myLowerMidPointY = theTopLeftPoint.y()+((theBottomRightPoint.y()-theTopLeftPoint.y())/2);
00777   QPointF myLowerMidPoint(myLowerMidPointX,myLowerMidPointY);
00778   // calculate the upper midpoint for the curve
00779   qreal myUpperMidPointX = theTopLeftPoint.x()+((theBottomRightPoint.x()-theTopLeftPoint.x())/2);
00780   qreal myUpperMidPointY = theTopLeftPoint.y();
00781   QPointF myUpperMidPoint(myUpperMidPointX,myUpperMidPointY);
00782   // calculate the endpoint one third down on the right
00783   QPointF myEndPoint(theBottomRightPoint.x(),theTopLeftPoint.y() + 
00784       ((theBottomRightPoint.y()-theTopLeftPoint.y())/3));
00785   //draw a bezier curve throught the center point to the end point
00786   myHighlightPath.cubicTo(myUpperMidPoint,myLowerMidPoint,myEndPoint);
00787   //close path back to original start pos
00788   myHighlightPath.closeSubpath();
00789   mpPainter->drawPath(myHighlightPath);
00790 }

Generated on Mon Apr 28 15:08:36 2008 for openModellerDesktop by  doxygen 1.4.1-20050210