Home

Download

Features

Screenshots

Handbook

Browse Source

Authors

SourceForge.net Logo
Hosted by SourceForge.net

OSI Certified


Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   Related Pages   Search  

basicgraph.cpp

00001 /***************************************************************************
00002                           basicgraph.cpp  - description
00003                              -------------------
00004     begin                : Thu May 30 2002
00005     copyright            : (C) 2002-03 by Fungmeista
00006     email                : mizunoami44@users.sourceforge.net
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 #include "basicgraph.h"
00018 
00019 #include <cmath>
00020 #include <sstream>
00021 
00022 #include <qcursor.h>
00023 #include <qpixmap.h>
00024 #include <qpainter.h>
00025 
00026 #ifdef KDE_APP
00027 #include <kprinter.h>
00028 #else
00029 #include <qprinter.h>
00030 #endif //KDE_APP
00031 
00032 #include "polarcoord.h"
00033 #include "rectcoord.h"
00034 #include "graphevent.h"
00035 #include "distancefunction.h"
00036 
00037 BasicGraph::BasicGraph( QWidget *parent, const char *name ) : 
00038     QWidget ( parent, name ),
00039     xMin(-15), xMax(15), xScale2(2),
00040     yMin(-15), yMax(15), yScale2(2),
00041     xScale(1), yScale(1),
00042     xZoom(1), yZoom(1),
00043     doingZoomBox(false), snapToGrid(false),
00044     showAxis(true), showGrid(true), showGridNum(true), showScale(true)
00045 {
00046     _axisColor = QColor(255,0,0);
00047     _gridColor = QColor(0,5,150);
00048     _backgroundColor = QColor(0,0,0);
00049     _scaleColor = QColor(60,120,0);
00050     
00051     axisWidth = 1;
00052     graphFont = QFont("helvetica",10,QFont::Black);
00053     scaleFont = QFont("helvetica",8);
00054     mouseDown = false;
00055 
00056     //rectangular or polar grid mode -- polar not yet implemented
00057     mode = RECTANGULAR;
00058 
00059     setPaletteBackgroundColor(_backgroundColor);
00060     setMouseTracking(true);
00061 
00062     buffer = new QPixmap(size());
00063     buffer->fill(_backgroundColor);
00064     p = new QPainter();
00065 
00066     updateGraph();
00067 }
00068 
00069 BasicGraph::~BasicGraph()
00070 {
00071     delete buffer;
00072     delete p;
00073 }
00074 
00075 void BasicGraph::load_key( const char *key, const char *value )
00076 {
00077     if (strcmp(key,"angle=") == 0)
00078     {
00079         setAngle(QString(value).toInt());
00080     }
00081     else if (strcmp(key,"BackgroundColor=") == 0)
00082     {
00083         QColor c(value);
00084         setBackgroundColor(c);
00085     }
00086     else if (strcmp(key,"AxisColor=") == 0)
00087     {
00088         QColor c(value);
00089         setAxisColor(c);
00090     }
00091     else if (strcmp(key,"GridColor=") == 0)
00092     {
00093         QColor c(value);
00094         setGridColor(c);
00095     }
00096     else if (strcmp(key,"ScaleColor=") == 0)
00097     {
00098         QColor c(value);
00099         setScaleColor(c);
00100     }
00101     else if (strcmp(key,"showGrid=") == 0)
00102     {
00103         bool show;
00104         if ( strcmp( value,"0" ) == 0 )
00105             show = false;
00106         else
00107             show = true;
00108         setGrid(show);
00109     }
00110     else if (strcmp(key,"showAxis=") == 0)
00111     {
00112         bool show;
00113         if ( strcmp( value,"0" ) == 0 )
00114             show = false;
00115         else
00116             show = true;
00117         setAxis(show);
00118     }
00119     else if (strcmp(key,"showScale=") == 0)
00120     {
00121         bool show;
00122         if ( strcmp( value,"0" ) == 0 )
00123             show = false;
00124         else
00125             show = true;
00126         setScale(show);
00127     }
00128     else if (strcmp(key,"snapToGrid=") == 0)
00129     {
00130         bool snap;
00131         if ( strcmp( value,"0" ) == 0 )
00132             snap = false;
00133         else
00134             snap = true;
00135         setSnapToGrid(snap);
00136     }
00137     else if (strcmp(key,"dimensions=") == 0)
00138     {
00139         char xMin[256], xMax[256], yMin[256], yMax[256];
00140         
00141         std::stringstream valueStream(value);
00142         valueStream>>xMin;
00143         valueStream>>xMax;
00144         valueStream>>yMin;
00145         valueStream>>yMax;
00146         setRange(QString(xMin).toDouble(),QString(xMax).toDouble(),QString(yMin).toDouble(),QString(yMax).toDouble());
00147     }
00148     #ifndef NO_SUPPORT_OLD //loading the dimensions this way is deprecated
00149     else if (strcmp(key,"xMin=") == 0)
00150     {
00151         setXMin(QString(value).toDouble());
00152     }
00153     else if (strcmp(key,"xMax=") == 0)
00154     {
00155         setXMax(QString(value).toDouble());
00156     }
00157     else if (strcmp(key,"yMin=") == 0)
00158     {
00159         setYMin(QString(value).toDouble());
00160     }
00161     else if (strcmp(key,"yMax=") == 0)
00162     {
00163         setYMax(QString(value).toDouble());
00164     }
00165     #endif
00166 }
00167 
00168 void BasicGraph::updateGraph()
00169 {
00170     origin.setX( int(-xMin * width() / (xMax - xMin)) );
00171     origin.setY( int(-yMax * height() / (yMin - yMax)) );
00172     xScale = width() / (xMax - xMin);
00173     yScale = height() / (yMin - yMax);
00174 
00175     cancelMathFunctions();
00176 
00177     repaint(false);
00178 }
00179 
00180 /*
00181 static double max(double a, double b)
00182 {
00183     return (a > b) ? a : b;
00184 }
00185 */
00186 
00187 void BasicGraph::drawGrid(QPainter *painter)
00188 {
00189     painter->save();
00190     painter->setPen(QPen(_gridColor,1,Qt::DotLine));
00191     //rectangular, stat, parametric mode
00192 //  if (mode == RECTANGULAR)
00193 //  {
00194         //x grid left of axis
00195         for (int i = 0; i < -xMin / xScale2; i++)
00196         {
00197             //painter->drawText((int) (origin.x() - i * xScale * xScale2),8,QString("%1").arg(toGraphXCoord((origin.x() - i * xScale * xScale2))));
00198             painter->drawLine((int) (origin.x() - i * xScale * xScale2), 0, (int) (origin.x() - i * xScale * xScale2), height());
00199         }
00200         //x grid right of axis
00201         for (int i = 1; i < xMax / xScale2; i++)
00202         {
00203             painter->drawLine((int) (origin.x() + i * xScale * xScale2), 0, (int) (origin.x() + i * xScale * xScale2), height());
00204         }
00205         //y grid below axis
00206         for (int i = 0; i < -yMin / yScale2; i++)
00207         {
00208             painter->drawLine(0, (int) (origin.y() + i * -yScale * yScale2), width(), (int) (origin.y() + i * -yScale * yScale2));
00209         }
00210         //y grid above axis
00211         for (int i = 1; i < yMax / yScale2; i++)
00212         {
00213             painter->drawLine(0, (int) (origin.y() - i * -yScale * yScale2), width(), (int) (origin.y() - i * -yScale * yScale2));
00214         }
00215 //  }
00216 //FIXME
00217 /*  else if (mode == POLAR)
00218     {
00219         //radius grid
00220         for (double r = xScale2; r < max(max(xMin,xMax),max(yMin,yMax)); r += xScale2) //<---work on this
00221         {
00222             for (int theta = 0; theta < 360; theta++)
00223             {
00224                 PolarCoord p(r, theta, PolarCoord::DEGREES);
00225                 PolarCoord nextP(r, theta + 1, PolarCoord::DEGREES);
00226                 painter->drawLine(  toPixelXCoord(p.toRectangular().getX()),
00227                             toPixelYCoord(p.toRectangular().getY()),
00228                             toPixelXCoord(nextP.toRectangular().getX()),
00229                             toPixelYCoord(nextP.toRectangular().getY())
00230                 );
00231             }
00232         }
00233 
00234         //angle grid
00235         for (double angle = 0; angle < 360; angle += yScale2) //and this
00236         {
00237             PolarCoord p(max(max(xMin,xMax),max(yMin,yMax)), angle, PolarCoord::DEGREES);
00238             painter->drawLine(  origin.x(),
00239                         origin.y(),
00240                         toPixelXCoord(p.toRectangular().getX()),
00241                         toPixelYCoord(p.toRectangular().getY())
00242             );
00243         }
00244     }*/
00245     painter->restore();
00246 }
00247 
00248 void BasicGraph::drawAxis(QPainter *painter)
00249 {
00250     painter->save();
00251     painter->setPen(QPen(_axisColor,axisWidth,Qt::SolidLine));
00252     painter->drawLine(origin.x(), 0, origin.x(), height()); //x-axis
00253     painter->drawLine(0, origin.y(), width(), origin.y());  //y-axis
00254     painter->restore();
00255 }
00256 
00257 void BasicGraph::drawScale(QPainter *painter)
00258 {
00259     painter->save();
00260     painter->setFont(scaleFont);
00261     painter->setPen(QColor(_scaleColor));
00262     QFontMetrics metrics(painter->font());
00263         //x scale left of axis
00264         for (int i = 0; i < -xMin / xScale2; i++)
00265         {
00266                 QString scale;
00267                 scale = QString::number(toGraphXCoord(origin.x() - i * xScale * xScale2));
00268                 painter->drawText((int) (origin.x() - i * xScale * xScale2) - metrics.width(scale)/2, metrics.height(), scale);
00269         }
00270         //x scale right of axis
00271         for (int i = 1; i < xMax / xScale2; i++)
00272         {
00273                 QString scale;
00274                 scale = QString::number(toGraphXCoord(origin.x() + i * xScale * xScale2));
00275                 painter->drawText((int) (origin.x() + i * xScale * xScale2) - metrics.width(scale)/2, metrics.height(), scale);
00276         }
00277         //y scale below axis
00278         for (int i = 0; i < -yMin / yScale2; i++)
00279         {
00280                 QString scale;
00281                 scale = QString::number(toGraphYCoord(origin.y() + i * -yScale * yScale2));
00282                 painter->drawText(0, (int) (origin.y() + i * -yScale * yScale2) + metrics.height()/2,  scale);
00283         }
00284         //y scale above axis
00285         for (int i = 1; i < yMax / yScale2; i++)
00286         {
00287                 QString scale;
00288                 scale = QString::number(toGraphYCoord(origin.y() - i * -yScale * yScale2));
00289                 painter->drawText(0, (int) (origin.y() - i * -yScale * yScale2) + metrics.height()/2, scale);
00290         }
00291     painter->restore();
00292 }
00293 
00294 void BasicGraph::paintEvent(QPaintEvent *)
00295 {
00296     bitBlt(this,0,0,buffer);
00297 }
00298 
00299 void BasicGraph::paint(QPainter* painter)
00300 {
00301     painter->setPen( Qt::blue );
00302     painter->setFont(graphFont);
00303         
00304     if (showGrid)
00305         drawGrid(painter);
00306     drawAfterAxis(painter);
00307     if (showAxis)
00308         drawAxis(painter);
00309     if (showScale)
00310         drawScale(painter);
00311     drawMathFunctions(p);
00312     if (doingZoomBox)
00313     {
00314         painter->setPen( QColor(255,130,20) );
00315         painter->drawText(30,height()-30,QString(tr("Click and drag to create box")));
00316         if (mouseDown)
00317             painter->drawRect( QRect( QPoint(lastClick.x(),lastClick.y()), QPoint(toPixelXCoord(getMouseX()),toPixelYCoord(getMouseY())) ) );
00318     }
00319 }
00320 
00321 void BasicGraph::offscreenResize(int width, int height)
00322 {
00323     resize(width,height);
00324     resizeEvent(0);
00325 }
00326 
00327 void BasicGraph::resizeEvent(QResizeEvent *)
00328 {
00329     buffer->resize(size());
00330     updateGraph();
00331 }
00332 
00333 double BasicGraph::toGraphXCoord(double x) const
00334 {
00335     return ((x - origin.x()) / xScale);
00336 }
00337 
00338 double BasicGraph::toGraphYCoord(double y) const
00339 {
00340     return ((y - origin.y()) / yScale);
00341 }
00342 
00343 int BasicGraph::toPixelXCoord(double x) const
00344 {
00345     return static_cast<int>(origin.x() + x * (xScale));
00346 }
00347 
00348 int BasicGraph::toPixelYCoord(double y) const
00349 {
00350     return static_cast<int>(origin.y() + y * (yScale));
00351 }
00352 
00353 void BasicGraph::mouseDoubleClickEvent(QMouseEvent *e)
00354 {
00355     int x = e->x();
00356     int y = e->y();
00357 
00358     //bring graph to the mouse position
00359     yMin += (y - height() / 2) / yScale;
00360     yMax += (y - height() / 2) / yScale;
00361     xMin += (x - width() / 2) / xScale;
00362     xMax += (x - width() / 2) / xScale;
00363 
00364     emitDimensionsChanged();
00365     updateGraph();
00366 }
00367 
00368 void BasicGraph::mouseMoveEvent(QMouseEvent *e)
00369 {
00370     setMouseX(toGraphXCoord(e->x()));
00371     setMouseY(toGraphYCoord(e->y()));
00372 
00373     updateCoords();
00374     if (mouseDown)
00375     {
00376         if (doingZoomBox)
00377             repaint(false); //repaint to show selected box
00378         else
00379         {
00380             double deltax = toGraphXCoord(e->x())-toGraphXCoord(lastClick.x());
00381             double deltay = toGraphYCoord(e->y())-toGraphYCoord(lastClick.y());
00382 
00383             //bring graph to the mouse position
00384             yMin -= deltay;
00385             yMax -= deltay;
00386             xMin -= deltax;
00387             xMax -= deltax;
00388             emitDimensionsChanged(); // should I do this here or wait until the mouse has been released?
00389             updateGraph();
00390 
00391             lastClick.setX(e->x());
00392             lastClick.setY(e->y());
00393         }
00394     }
00395 }
00396 
00397 void BasicGraph::mousePressEvent(QMouseEvent *e)
00398 {
00399     mouseDown = true;
00400 
00401     mathFunctionClick(e,getMouseX(),getMouseY());
00402 
00403     lastClick.setX(toPixelXCoord(getMouseX()));
00404     lastClick.setY(toPixelYCoord(getMouseY()));
00405     lastClickGraphCoordX = getMouseX();
00406     lastClickGraphCoordY = getMouseY();
00407 
00408     setCursor(QCursor(Qt::PointingHandCursor));
00409 }
00410 
00411 void BasicGraph::mouseReleaseEvent(QMouseEvent *)
00412 {
00413     if (doingZoomBox && mouseDown)
00414     {
00415         double _xMin = (lastClickGraphCoordX < getMouseX()) ? lastClickGraphCoordX : getMouseX();
00416         double _xMax = (lastClickGraphCoordX > getMouseX()) ? lastClickGraphCoordX : getMouseX();
00417         double _yMin = (lastClickGraphCoordY < getMouseY()) ? lastClickGraphCoordY : getMouseY();
00418         double _yMax = (lastClickGraphCoordY > getMouseY()) ? lastClickGraphCoordY : getMouseY();
00419 
00420         setRange(_xMin, _xMax, _yMin, _yMax);
00421         emitDimensionsChanged();
00422         doingZoomBox = false;
00423         repaint(false);
00424     }
00425 
00426     mouseDown = false;
00427     setCursor(QCursor(Qt::ArrowCursor));
00428 }
00429 
00430 void BasicGraph::updateCoords()
00431 {
00432     emit activeCoordinateChanged( QString("x = %1").arg(mouseX), QString("y = %1").arg(mouseY) );
00433 }
00434 
00435 void BasicGraph::emitDimensionsChanged()
00436 {
00437     GraphEvent event(xMin,xMax,xScale2,yMin,yMax,yScale2);
00438     emit dimensionsChanged(event);
00439 }
00440 
00441 int BasicGraph::setRange(double _xMin, double _xMax, double _yMin, double _yMax)
00442 {
00443     if (_xMin < _xMax && _yMin < _yMax)
00444     {
00445         xMin = _xMin;
00446         xMax = _xMax;
00447         yMin = _yMin;
00448         yMax = _yMax;
00449         updateGraph();
00450         return 0;
00451     }
00452     else
00453         return 1;
00454 }
00455 
00456 int BasicGraph::setXMin(double _xMin)
00457 {
00458     if (_xMin < xMax)
00459     {
00460         xMin = _xMin;
00461         updateGraph();
00462         return 0;
00463     }
00464     else
00465         return 1;
00466 }
00467 
00468 int BasicGraph::setXMax(double _xMax)
00469 {
00470     if (_xMax > xMin)
00471     {
00472         xMax = _xMax;
00473         updateGraph();
00474         return 0;
00475     }
00476     else
00477         return 1;
00478 }
00479 
00480 int BasicGraph::setXScale(double _xScale)
00481 {
00482     if (_xScale > 0)
00483     {
00484         xScale2 = _xScale;
00485         repaint(false);
00486         return 0;
00487     }
00488     else
00489         return 1;
00490 }
00491 
00492 int BasicGraph::setYMin(double _yMin)
00493 {
00494     if (_yMin < yMax)
00495     {
00496         yMin = _yMin;
00497         updateGraph();
00498         return 0;
00499     }
00500     else
00501         return 1;
00502 }
00503 
00504 int BasicGraph::setYMax(double _yMax)
00505 {
00506     if (_yMax > yMin)
00507     {
00508         yMax = _yMax;
00509         updateGraph();
00510         return 0;
00511     }
00512     else
00513         return 1;
00514 }
00515 
00516 int BasicGraph::setYScale(double _yScale)
00517 {
00518     if (_yScale > 0)
00519     {
00520         yScale2 = _yScale;
00521         repaint(false);
00522         return 0;
00523     }
00524     else
00525         return 1;
00526 }
00527 
00528 void BasicGraph::setAxis(bool state)
00529 {
00530     showAxis = state;
00531     repaint(false);
00532 }
00533 
00534 void BasicGraph::setGrid(bool state)
00535 {
00536     showGrid = state;
00537     repaint(false);
00538 }
00539 
00540 void BasicGraph::setGridNum(bool state)
00541 {
00542     showGridNum = state;
00543     repaint(false);
00544 }
00545 
00546 void BasicGraph::setScale(bool state)
00547 {
00548     showScale = state;
00549     repaint(false);
00550 }
00551 
00552 void BasicGraph::setAxisColor(const QColor &c)
00553 {
00554     _axisColor = c;
00555     repaint(false);
00556 }
00557 
00558 void BasicGraph::setGridColor(const QColor &c)
00559 {
00560     _gridColor = c;
00561     repaint(false);
00562 }
00563 
00564 void BasicGraph::setBackgroundColor(const QColor &c)
00565 {
00566     _backgroundColor = c;
00567     setPaletteBackgroundColor(_backgroundColor);
00568     repaint(false);
00569 }
00570 
00571 void BasicGraph::setScaleColor(const QColor &c)
00572 {
00573     _scaleColor = c;
00574     repaint(false);
00575 }
00576 
00577 void BasicGraph::setXZoomFactor(int zoom)
00578 {
00579     xZoom = zoom;
00580 }
00581 
00582 void BasicGraph::setYZoomFactor(int zoom)
00583 {
00584     yZoom = zoom;
00585 }
00586 
00587 QColor BasicGraph::axisColor() const
00588 {
00589     return _axisColor;
00590 }
00591 
00592 QColor BasicGraph::gridColor() const
00593 {
00594     return _gridColor;
00595 }
00596 
00597 QColor BasicGraph::backgroundColor() const
00598 {
00599     return _backgroundColor;
00600 }
00601 
00602 QColor BasicGraph::scaleColor() const
00603 {
00604     return _scaleColor;
00605 }
00606 
00607 int BasicGraph::getXZoomFactor() const
00608 {
00609     return xZoom;
00610 }
00611 
00612 int BasicGraph::getYZoomFactor() const
00613 {
00614     return yZoom;
00615 }
00616 
00617 void BasicGraph::zoomIn()
00618 {
00619     double half_x = (xMax-xMin)/2;
00620     double half_y = (yMax-yMin)/2;
00621     (void)setRange( xMin + half_x - (xMax-xMin) / ( pow(2,xZoom+1) ),
00622                 xMin + half_x + (xMax-xMin)/(pow(2,xZoom+1)),
00623                 yMin + half_y - (yMax-yMin)/(pow(2,yZoom+1)),
00624                 yMin + half_y + (yMax-yMin)/(pow(2,yZoom+1))
00625                 );
00626     emitDimensionsChanged();
00627 }
00628 
00629 void BasicGraph::zoomOut()
00630 {
00631     double half_x = (xMax-xMin)/2;
00632     double half_y = (yMax-yMin)/2;
00633     (void)setRange( xMin + half_x - (xMax-xMin)*( pow(2,xZoom-1) ),
00634                 xMin + half_x + (xMax-xMin)*(pow(2,xZoom-1)),
00635                 yMin + half_y - (yMax-yMin)*(pow(2,yZoom-1)),
00636                 yMin + half_y + (yMax-yMin)*(pow(2,yZoom-1))
00637                 );
00638     emitDimensionsChanged();
00639 }
00640 
00641 void BasicGraph::zoomStd()
00642 {
00643     yScale2 = 2;
00644     xScale2 = 2;
00645     setRange(-15,15,-15,15);
00646     emitDimensionsChanged();
00647 }
00648 
00649 void BasicGraph::zoomBox()
00650 {
00651     doingZoomBox = true;
00652     repaint(false);
00653 }
00654 
00655 void BasicGraph::setAxisWidth(int i)
00656 {
00657     axisWidth = i;
00658     repaint(false);   
00659 }
00660 
00661 void BasicGraph::print(
00662     #ifdef KDE_APP
00663     KPrinter *printer
00664     #else
00665     QPrinter *printer
00666     #endif //KDE_APP
00667     )
00668 {
00669     if ( printer->setup(this) )
00670     {              
00671         qDebug( "Printing...");
00672         QPainter p;
00673         if ( !p.begin( printer ) )
00674             return;
00675         paint(&p); //paint grid and axis
00676         p.end();
00677         qDebug( "Printing completed");
00678     }
00679     else
00680         qDebug( "Printing aborted" );
00681 }
00682 
00683 void BasicGraph::setMouseX(double x, bool snap)
00684 {
00685     if (snapToGrid && snap)
00686     {
00687         double interval = snapIntervalX();
00688         double remainder = fmod(x - startSnap(), interval);
00689         if (remainder < 0)
00690         {
00691             if ( fabs(remainder) < (interval/2) )
00692                 mouseX = x - remainder; //round up
00693             else
00694                 mouseX = x - (interval + remainder); //round down
00695         }
00696         else 
00697         {
00698             if ( fabs(remainder) < (interval/2) )
00699                 mouseX = x - remainder; //round down
00700             else
00701                 mouseX = x + (interval - remainder); //round up
00702         }
00703     }
00704     else
00705         mouseX = x;
00706 }
00707 
00708 void BasicGraph::setMouseY(double y, bool snap)
00709 {
00710     if (snapToGrid && snap)
00711     {
00712         double interval = snapIntervalY();
00713         double remainder = fmod(y, interval);
00714         if (remainder < 0)
00715         {
00716             if ( fabs(remainder) < (interval/2) )
00717                 mouseY = y - remainder;
00718             else
00719                 mouseY = y - (interval + remainder);
00720         }
00721         else
00722         {
00723             if ( fabs(remainder) < (interval/2) )
00724                 mouseY = y - remainder;
00725             else
00726                 mouseY = y + (interval - remainder);
00727         }
00728     }
00729     else
00730         mouseY = y;
00731 }
00732 
00733 void BasicGraph::installMathFunctions()
00734 {
00735     addMathFunction( new DistanceFunction(this,0), "Distance" );
00736 }
00737 
00738 int BasicGraph::execMathFunction( const char * id )
00739 {
00740     getMathFunction(id)->exec();
00741     return 0;
00742 }