00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
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     
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 
00182 
00183 
00184 
00185 
00186 
00187 void BasicGraph::drawGrid(QPainter *painter)
00188 {
00189     painter->save();
00190     painter->setPen(QPen(_gridColor,1,Qt::DotLine));
00191     
00192 
00193 
00194         
00195         for (int i = 0; i < -xMin / xScale2; i++)
00196         {
00197             
00198             painter->drawLine((int) (origin.x() - i * xScale * xScale2), 0, (int) (origin.x() - i * xScale * xScale2), height());
00199         }
00200         
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         
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         
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 
00217 
00218 
00219 
00220 
00221 
00222 
00223 
00224 
00225 
00226 
00227 
00228 
00229 
00230 
00231 
00232 
00233 
00234 
00235 
00236 
00237 
00238 
00239 
00240 
00241 
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()); 
00253     painter->drawLine(0, origin.y(), width(), origin.y());  
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         
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         
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         
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         
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     
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); 
00378         else
00379         {
00380             double deltax = toGraphXCoord(e->x())-toGraphXCoord(lastClick.x());
00381             double deltay = toGraphYCoord(e->y())-toGraphYCoord(lastClick.y());
00382 
00383             
00384             yMin -= deltay;
00385             yMax -= deltay;
00386             xMin -= deltax;
00387             xMax -= deltax;
00388             emitDimensionsChanged(); 
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 
00667     )
00668 {
00669     if ( printer->setup(this) )
00670     {              
00671         qDebug( "Printing...");
00672         QPainter p;
00673         if ( !p.begin( printer ) )
00674             return;
00675         paint(&p); 
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; 
00693             else
00694                 mouseX = x - (interval + remainder); 
00695         }
00696         else 
00697         {
00698             if ( fabs(remainder) < (interval/2) )
00699                 mouseX = x - remainder; 
00700             else
00701                 mouseX = x + (interval - remainder); 
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 }