File indexing completed on 2024-12-22 04:17:52

0001 /***************************************************************************
0002                           vcurve.cpp: holds info for a curve
0003                              -------------------
0004     begin                : Fri Nov 3 2000
0005     copyright            : (C) 2000 by cbn
0006     email                : netterfield@astro.utoronto.ca
0007  ***************************************************************************/
0008 
0009 /***************************************************************************
0010  *                                                                         *
0011  *   This program is free software; you can redistribute it and/or modify  *
0012  *   it under the terms of the GNU General Public License as published by  *
0013  *   the Free Software Foundation; either version 2 of the License, or     *
0014  *   (at your option) any later version.                                   *
0015  *                                                                         *
0016  ***************************************************************************/
0017 #include "curve.h"
0018 
0019 // includes for KDE
0020 
0021 #include <qdebug.h>
0022 #include <QPolygonF>
0023 #include <QXmlStreamWriter>
0024 
0025 // application specific includes
0026 #include "dialoglauncher.h"
0027 #include "colorsequence.h"
0028 #include "datacollection.h"
0029 #include "debug.h"
0030 #include "linestyle.h"
0031 #include "math_kst.h"
0032 #include "datavector.h"
0033 #include "ksttimers.h"
0034 #include "objectstore.h"
0035 #include "relationscriptinterface.h"
0036 
0037 #include <time.h>
0038 #include <iostream>
0039 
0040 // #define DEBUG_VECTOR_CURVE
0041 //#define BENCHMARK
0042 
0043 // NOTE: on a modern (eg, sandybridge or later) cpu
0044 // the cpu's branch prediction is so good, this does
0045 // more or less nothing
0046 #ifndef KDE_IS_LIKELY
0047 #if __GNUC__ - 0 >= 3
0048 # define KDE_ISLIKELY( x )    __builtin_expect(!!(x),1)
0049 # define KDE_ISUNLIKELY( x )  __builtin_expect(!!(x),0)
0050 #else
0051 # define KDE_ISLIKELY( x )   ( x )
0052 # define KDE_ISUNLIKELY( x )  ( x )
0053 #endif
0054 #endif
0055 
0056 // for painting
0057 #define MAX_NUM_POLYLINES       3
0058 
0059 using namespace std;
0060 
0061 namespace Kst {
0062 
0063 const QString Curve::staticTypeString = "Curve";
0064 const QString Curve::staticTypeTag = "curve";
0065 
0066 static const QLatin1String& XVECTOR = QLatin1String("X");
0067 static const QLatin1String& YVECTOR = QLatin1String("Y");
0068 static const QLatin1String& EXVECTOR = QLatin1String("EX");
0069 static const QLatin1String& EYVECTOR = QLatin1String("EY");
0070 static const QLatin1String& EXMINUSVECTOR = QLatin1String("EXMinus");
0071 static const QLatin1String& EYMINUSVECTOR = QLatin1String("EYMinus");
0072 
0073 Curve::Curve(ObjectStore *store)
0074 : Relation(store) {
0075   setHasPoints(false);
0076   setHasHead(false);
0077   setHasBars(false);
0078   setHasLines(true);
0079   setLineWidth(1);
0080   setLineStyle(0);
0081   setPointDensity(0);
0082   setPointSize(CURVE_DEFAULT_POINT_SIZE);
0083 
0084   MaxX = MinX = MeanX = MaxY = MinY = MeanY = MinPosX = MinPosY = 0;
0085   NS = 0;
0086   _typeString = tr("Curve");
0087   _type = "Curve";
0088   _initializeShortName();
0089   Color = QColor();
0090   HeadColor = QColor();
0091   BarFillColor = QColor();
0092 }
0093 
0094 void Curve::_initializeShortName() {
0095   _shortName = 'C'+QString::number(_curvecnum);
0096   if (_curvecnum>max_curvenum)
0097     max_curvenum = _curvecnum;
0098   _curvecnum++;
0099 }
0100 
0101 
0102 Curve::~Curve() {
0103 }
0104 
0105 ScriptInterface* Curve::createScriptInterface() {
0106   return new CurveSI(this);
0107 }
0108 
0109 
0110 void Curve::internalUpdate() {
0111   Q_ASSERT(myLockStatus() == KstRWLock::WRITELOCKED);
0112 
0113   VectorPtr cxV = *_inputVectors.find(XVECTOR);
0114   VectorPtr cyV = *_inputVectors.find(YVECTOR);
0115   if (!cxV || !cyV) {
0116     return;
0117   }
0118 
0119   writeLockInputsAndOutputs();
0120 
0121   MaxX = cxV->max();
0122   MinX = cxV->min();
0123   MeanX = cxV->mean();
0124   MinPosX = cxV->minPos();
0125 
0126   if (MinPosX > MaxX) {
0127     MinPosX = 0;
0128   }
0129   MaxY = cyV->max();
0130   MinY = cyV->min();
0131   MeanY = cyV->mean();
0132   MinPosY = cyV->minPos();
0133 
0134   if (MinPosY > MaxY) {
0135     MinPosY = 0;
0136   }
0137 
0138   NS = qMax(cxV->length(), cyV->length());
0139 
0140   unlockInputsAndOutputs();
0141 
0142   _redrawRequired = true;
0143 
0144   return;
0145 }
0146 
0147 
0148 void Curve::point(int i, double &x, double &y) const {
0149   VectorPtr xv = xVector();
0150   if (xv) {
0151     x = xv->interpolate(i, NS);
0152   }
0153   VectorPtr yv = yVector();
0154   if (yv) {
0155     y = yv->interpolate(i, NS);
0156   }
0157 }
0158 
0159 
0160 void Curve::getEXPoint(int i, double &x, double &y, double &ex) {
0161   VectorPtr xv = xVector();
0162   if (xv) {
0163     x = xv->interpolate(i, NS);
0164   }
0165   VectorPtr yv = yVector();
0166   if (yv) {
0167     y = yv->interpolate(i, NS);
0168   }
0169   VectorPtr exv = xErrorVector();
0170   if (exv) {
0171     ex = exv->interpolate(i, NS);
0172   }
0173 }
0174 
0175 
0176 void Curve::getEXMinusPoint(int i, double &x, double &y, double &ex) {
0177   VectorPtr xv = xVector();
0178   if (xv) {
0179     x = xv->interpolate(i, NS);
0180   }
0181   VectorPtr yv = yVector();
0182   if (yv) {
0183     y = yv->interpolate(i, NS);
0184   }
0185   VectorPtr exmv = xMinusErrorVector();
0186   if (exmv) {
0187     ex = exmv->interpolate(i, NS);
0188   }
0189 }
0190 
0191 
0192 void Curve::getEXPoints(int i, double &x, double &y, double &exminus, double &explus) {
0193   VectorPtr xv = xVector();
0194   if (xv) {
0195     x = xv->interpolate(i, NS);
0196   }
0197   VectorPtr yv = yVector();
0198   if (yv) {
0199     y = yv->interpolate(i, NS);
0200   }
0201   VectorPtr exv = xErrorVector();
0202   if (exv) {
0203     explus = exv->interpolate(i, NS);
0204   }
0205   VectorPtr exmv = xMinusErrorVector();
0206   if (exmv) {
0207     exminus = exmv->interpolate(i, NS);
0208   }
0209 }
0210 
0211 
0212 void Curve::getEYPoint(int i, double &x, double &y, double &ey) {
0213   VectorPtr xv = xVector();
0214   if (xv) {
0215     x = xv->interpolate(i, NS);
0216   }
0217   VectorPtr yv = yVector();
0218   if (yv) {
0219     y = yv->interpolate(i, NS);
0220   }
0221   VectorPtr eyv = yErrorVector();
0222   if (eyv) {
0223     ey = eyv->interpolate(i, NS);
0224   }
0225 }
0226 
0227 
0228 void Curve::getEYMinusPoint(int i, double &x, double &y, double &ey) {
0229   VectorPtr xv = xVector();
0230   if (xv) {
0231     x = xv->interpolate(i, NS);
0232   }
0233   VectorPtr yv = yVector();
0234   if (yv) {
0235     y = yv->interpolate(i, NS);
0236   }
0237   VectorPtr eyv = yMinusErrorVector();
0238   if (eyv) {
0239     ey = eyv->interpolate(i, NS);
0240   }
0241 }
0242 
0243 
0244 void Curve::getEYPoints(int i, double &x, double &y, double &eyminus, double &eyplus) {
0245   VectorPtr xv = xVector();
0246   if (xv) {
0247     x = xv->interpolate(i, NS);
0248   }
0249   VectorPtr yv = yVector();
0250   if (yv) {
0251     y = yv->interpolate(i, NS);
0252   }
0253   VectorPtr eyv = yErrorVector();
0254   if (eyv) {
0255     eyplus = eyv->interpolate(i, NS);
0256   }
0257   VectorPtr eymv = yMinusErrorVector();
0258   if (eymv) {
0259     eyminus = eymv->interpolate(i, NS);
0260   }
0261 }
0262 
0263 
0264 bool Curve::hasXError() const {
0265   return _inputVectors.contains(EXVECTOR);
0266 }
0267 
0268 
0269 bool Curve::hasYError() const {
0270   return _inputVectors.contains(EYVECTOR);
0271 }
0272 
0273 
0274 bool Curve::hasXMinusError() const {
0275   return _inputVectors.contains(EXMINUSVECTOR);
0276 }
0277 
0278 
0279 bool Curve::hasYMinusError() const {
0280   return _inputVectors.contains(EYMINUSVECTOR);
0281 }
0282 
0283 
0284 void Curve::save(QXmlStreamWriter &s) {
0285   s.writeStartElement(staticTypeTag);
0286   s.writeAttribute("xvector", _inputVectors[XVECTOR]->Name());
0287   s.writeAttribute("yvector", _inputVectors[YVECTOR]->Name());
0288   if (_inputVectors.contains(EXVECTOR)) {
0289     s.writeAttribute("errorxvector", _inputVectors[EXVECTOR]->Name());
0290   }
0291   if (_inputVectors.contains(EYVECTOR)) {
0292     s.writeAttribute("erroryvector", _inputVectors[EYVECTOR]->Name());
0293   }
0294   if (_inputVectors.contains(EXMINUSVECTOR)) {
0295     s.writeAttribute("errorxminusvector", _inputVectors[EXMINUSVECTOR]->Name());
0296   }
0297   if (_inputVectors.contains(EYMINUSVECTOR)) {
0298     s.writeAttribute("erroryminusvector", _inputVectors[EYMINUSVECTOR]->Name());
0299   }
0300   s.writeAttribute("color", Color.name());
0301   s.writeAttribute("alpha", QString::number(Color.alpha()));
0302   s.writeAttribute("headcolor", HeadColor.name());
0303   s.writeAttribute("headalpha", QString::number(HeadColor.alpha()));
0304   s.writeAttribute("barfillcolor", BarFillColor.name());
0305   s.writeAttribute("barfillalpha", QString::number(BarFillColor.alpha()));
0306 
0307   s.writeAttribute("haslines", QVariant(HasLines).toString());
0308   s.writeAttribute("linewidth", QString::number(LineWidth));
0309   s.writeAttribute("linestyle", QString::number(LineStyle));
0310 
0311   s.writeAttribute("haspoints", QVariant(HasPoints).toString());
0312   s.writeAttribute("pointtype", QString::number(PointType));
0313   s.writeAttribute("pointdensity", QString::number(PointDensity));
0314   s.writeAttribute("pointsize", QString::number(PointSize));
0315 
0316   s.writeAttribute("hasbars", QVariant(HasBars).toString());
0317   s.writeAttribute("ignoreautoscale", QVariant(_ignoreAutoScale).toString());
0318 
0319   s.writeAttribute("hashead", QVariant(HasHead).toString());
0320   s.writeAttribute("headtype", QString::number(HeadType));
0321 
0322   saveNameInfo(s, CURVENUM);
0323 
0324   s.writeEndElement();
0325 }
0326 
0327 
0328 void Curve::setXVector(VectorPtr new_vx) {
0329   if (new_vx) {
0330     _inputVectors[XVECTOR] = new_vx;
0331   } else {
0332     _inputVectors.remove(XVECTOR);
0333   }
0334 }
0335 
0336 
0337 void Curve::setYVector(VectorPtr new_vy) {
0338   if (new_vy) {
0339     _inputVectors[YVECTOR] = new_vy;
0340   } else {
0341     _inputVectors.remove(YVECTOR);
0342   }
0343 }
0344 
0345 
0346 void Curve::setXError(VectorPtr new_ex) {
0347   if (new_ex) {
0348     _inputVectors[EXVECTOR] = new_ex;
0349   } else {
0350     _inputVectors.remove(EXVECTOR);
0351   }
0352 }
0353 
0354 
0355 void Curve::setYError(VectorPtr new_ey) {
0356   if (new_ey) {
0357     _inputVectors[EYVECTOR] = new_ey;
0358   } else {
0359     _inputVectors.remove(EYVECTOR);
0360   }
0361 }
0362 
0363 
0364 void Curve::setXMinusError(VectorPtr new_ex) {
0365   if (new_ex) {
0366     _inputVectors[EXMINUSVECTOR] = new_ex;
0367   } else {
0368     _inputVectors.remove(EXMINUSVECTOR);
0369   }
0370 }
0371 
0372 
0373 void Curve::setYMinusError(VectorPtr new_ey) {
0374   if (new_ey) {
0375     _inputVectors[EYMINUSVECTOR] = new_ey;
0376   } else {
0377     _inputVectors.remove(EYMINUSVECTOR);
0378   }
0379 }
0380 
0381 
0382 // labels for plots
0383 LabelInfo Curve::xLabelInfo() const {
0384   return xVector()->labelInfo();
0385 }
0386 
0387 
0388 LabelInfo Curve::yLabelInfo() const {
0389   return yVector()->labelInfo();
0390 }
0391 
0392 
0393 LabelInfo Curve::titleInfo() const {
0394 
0395   LabelInfo label_info = yVector()->titleInfo();
0396 
0397   if (!_manualDescriptiveName.isEmpty()) {
0398     label_info.name = _manualDescriptiveName;
0399   }
0400 
0401   return label_info;
0402 }
0403 
0404 
0405 QString Curve::propertyString() const {
0406   return tr("%1 vs %2", "a plot of arg1 vs arg2").arg(yVector()->Name()).arg(xVector()->Name());
0407 }
0408 
0409 
0410 void Curve::showNewDialog() {
0411   DialogLauncher::self()->showCurveDialog();
0412 }
0413 
0414 
0415 void Curve::showEditDialog() {
0416   DialogLauncher::self()->showCurveDialog(this);
0417 }
0418 
0419 
0420 int Curve::samplesPerFrame() const {
0421   const DataVector *rvp = qobject_cast<const DataVector*>(_inputVectors[YVECTOR].data());
0422   return rvp ? rvp->samplesPerFrame() : 1;
0423 }
0424 
0425 
0426 VectorPtr Curve::xVector() const {
0427   return *_inputVectors.find(XVECTOR);
0428 }
0429 
0430 
0431 VectorPtr Curve::yVector() const {
0432   return *_inputVectors.find(YVECTOR);
0433 }
0434 
0435 
0436 VectorPtr Curve::xErrorVector() const {
0437   return *_inputVectors.find(EXVECTOR);
0438 }
0439 
0440 
0441 VectorPtr Curve::yErrorVector() const {
0442   return *_inputVectors.find(EYVECTOR);
0443 }
0444 
0445 
0446 VectorPtr Curve::xMinusErrorVector() const {
0447   return *_inputVectors.find(EXMINUSVECTOR);
0448 }
0449 
0450 
0451 VectorPtr Curve::yMinusErrorVector() const {
0452   return *_inputVectors.find(EYMINUSVECTOR);
0453 }
0454 
0455 
0456 bool Curve::xIsRising() const {
0457   return _inputVectors[XVECTOR]->isRising();
0458 }
0459 
0460 
0461 inline int indexNearX(double x, VectorPtr& xv, int NS) {
0462   // monotonically rising: we can do a binary search
0463   // should be reasonably fast
0464   if (xv->isRising()) {
0465     int i_top = NS - 1;
0466     int i_bot = 0;
0467 
0468     // don't pre-check for x outside of the curve since this is not
0469     // the common case.  It will be correct - just slightly slower...
0470     while (i_bot + 1 < i_top) {
0471       int i0 = (i_top + i_bot)/2;
0472       double rX = xv->interpolate(i0, NS);
0473       if (x < rX) {
0474         i_top = i0;
0475       } else {
0476         i_bot = i0;
0477       }
0478     }
0479     double xt = xv->interpolate(i_top, NS);
0480     double xb = xv->interpolate(i_bot, NS);
0481     if (xt - x < x - xb) {
0482       return i_top;
0483     } else {
0484       return i_bot;
0485     }
0486   } else {
0487     // Oh Oh... not monotonically rising - we have to search the entire curve!
0488     // May be unbearably slow for large vectors
0489     double rX = xv->interpolate(0, NS);
0490     double dx0 = fabs(x - rX);
0491     int i0 = 0;
0492 
0493     for (int i = 1; i < NS; ++i) {
0494       rX = xv->interpolate(i, NS);
0495       double dx = fabs(x - rX);
0496       if (dx < dx0) {
0497         dx0 = dx;
0498         i0 = i;
0499       }
0500     }
0501     return i0;
0502   }
0503 }
0504 
0505 
0506 /** getIndexNearXY: return index of point within (or closest too)
0507     x +- dx which is closest to y **/
0508 int Curve::getIndexNearXY(double x, double dx_per_pix, double y) const {
0509   VectorPtr xv = *_inputVectors.find(XVECTOR);
0510   VectorPtr yv = *_inputVectors.find(YVECTOR);
0511   if (!xv || !yv) {
0512     return 0; // anything better we can do?
0513   }
0514 
0515   double xi, yi, dx, dxi, dy, dyi;
0516   bool first = true;
0517   int i,i0, iN, index;
0518   int sc = sampleCount();
0519 
0520   if (xv->isRising()) {
0521     iN = i0 = indexNearX(x, xv, NS);
0522 
0523     xi = xv->interpolate(i0, NS);
0524     while (i0 > 0 && x-dx_per_pix < xi) {
0525       xi = xv->interpolate(--i0, NS);
0526     }
0527 
0528     xi = xv->interpolate(iN, NS);
0529     while (iN < sc-1 && x+dx_per_pix > xi) {
0530       xi = xv->interpolate(++iN, NS);
0531     }
0532   } else {
0533     i0 = 0;
0534     iN = sampleCount()-1;
0535   }
0536 
0537   index = i0;
0538   xi = xv->interpolate(index, NS);
0539   yi = yv->interpolate(index, NS);
0540   dx = fabs(x - xi);
0541   dy = fabs(y - yi);
0542 
0543   for (i = i0 + 1; i <= iN; ++i) {
0544     xi = xv->interpolate(i, NS);
0545     dxi = fabs(x - xi);
0546     if (dxi < dx_per_pix) {
0547       dx = dxi;
0548       yi = yv->interpolate(i, NS);
0549       dyi = fabs(y - yi);
0550       if (first || dyi < dy) {
0551         first = false;
0552         index = i;
0553         dy = dyi;
0554       }
0555     } else if (dxi < dx) {
0556       dx = dxi;
0557       index = i;
0558     }
0559   }
0560   return index;
0561 }
0562 
0563 
0564 void Curve::setHasPoints(bool in_HasPoints) {
0565   HasPoints = in_HasPoints;
0566 }
0567 
0568 
0569 void Curve::setHasHead(bool in_HasHead) {
0570   HasHead = in_HasHead;
0571 }
0572 
0573 
0574 void Curve::setHasLines(bool in_HasLines) {
0575   HasLines = in_HasLines;
0576 }
0577 
0578 
0579 void Curve::setHasBars(bool in_HasBars) {
0580   HasBars = in_HasBars;
0581 }
0582 
0583 
0584 void Curve::setLineWidth(int in_LineWidth) {
0585 #ifdef Q_OS_WIN32
0586   if (in_LineWidth == 0) {
0587     in_LineWidth = 1;
0588   }
0589 #endif
0590   LineWidth = in_LineWidth;
0591 }
0592 
0593 
0594 void Curve::setLineStyle(int in_LineStyle) {
0595   LineStyle = in_LineStyle;
0596 }
0597 
0598 
0599 void Curve::setPointDensity(int in_PointDensity) {
0600   PointDensity = in_PointDensity;
0601 }
0602 
0603 
0604 void Curve::setPointType(int in_PointType) {
0605   PointType = in_PointType;
0606 }
0607 
0608 
0609 void Curve::setPointSize(double in_PointSize) {
0610   PointSize = in_PointSize;
0611 }
0612 
0613 
0614 void Curve::setHeadType(int in_HeadType) {
0615   HeadType = in_HeadType;
0616 }
0617 
0618 
0619 void Curve::setColor(const QColor& new_c) {
0620   Color = new_c;
0621 }
0622 
0623 void Curve::setHeadColor(const QColor& new_c) {
0624   HeadColor = new_c;
0625 }
0626 
0627 void Curve::setBarFillColor(const QColor &new_c) {
0628   BarFillColor = new_c;
0629 }
0630 
0631 double Curve::maxX() const {
0632   if (hasBars() && sampleCount() > 0) {
0633     return MaxX + (MaxX - MinX)/(2*(sampleCount()-1));
0634   }
0635   return MaxX;
0636 }
0637 
0638 
0639 double Curve::minX() const {
0640   if (hasBars() && sampleCount() > 0) {
0641     return MinX - (MaxX - MinX)/(2*(sampleCount()-1));
0642   }
0643   return MinX;
0644 }
0645 
0646 
0647 RelationPtr Curve::makeDuplicate() const {
0648   CurvePtr curve = store()->createObject<Curve>();
0649 
0650   if (descriptiveNameIsManual()) {
0651     curve->setDescriptiveName(descriptiveName());
0652   }
0653   curve->setXVector(xVector());
0654   curve->setYVector(yVector());
0655   if (hasXError()) {
0656     curve->setXError(xErrorVector());
0657   }
0658   if (hasYError()) {
0659     curve->setYError(yErrorVector());
0660   }
0661   if (hasXMinusError()) {
0662     curve->setXMinusError(xMinusErrorVector());
0663   }
0664   if (hasYMinusError()) {
0665     curve->setYMinusError(yMinusErrorVector());
0666   }
0667 
0668   curve->setColor(Color);
0669   curve->setHeadColor(HeadColor);
0670   curve->setBarFillColor(BarFillColor);
0671   curve->setHasPoints(HasPoints);
0672   curve->setHasLines(HasLines);
0673   curve->setHasBars(HasBars);
0674   curve->setHasHead(HasHead);
0675   curve->setLineWidth(LineWidth);
0676   curve->setLineStyle(LineStyle);
0677   curve->setPointType(PointType);
0678   curve->setPointSize(PointSize);
0679   curve->setPointDensity(PointDensity);
0680 
0681   curve->writeLock();
0682   curve->registerChange();
0683   curve->unlock();
0684 
0685   return RelationPtr(curve);
0686 }
0687 
0688 // FIXME: magic numbers (1/4000 and 1/2000) should be based on the reference plot size,
0689 // and not just pulled out of the air by what looks ~good.
0690 // They are currently "about right" for printing to US Letter.
0691 double Curve::pointDim(QRectF w) const {
0692   return qMax(double(1.01), PointSize * ((w.width() + w.height()) * (1.0 / 4000.0)));
0693 }
0694 
0695 
0696 double Curve::lineDim(const QRectF &R, double linewidth) {
0697   qreal lw = qMax(qreal(1.01), qreal(linewidth)*(R.width()+R.height())*qreal(1.0/2000.0));
0698   // if you ask for a thicker line, make sure it is at least 2 px wide
0699   if (linewidth>1.9) {
0700     lw = qMax(qreal(2.0),lw);
0701   }
0702 
0703   return (int(lw+0.5));
0704 }
0705 
0706 void tmpPolyLine(const QPolygonF& poly, QPainter *p) {
0707   int n = poly.count();
0708   for (int i=0; i< n-1; i++) {
0709     p->drawLine(poly[i], poly[i+1]);
0710   }
0711 }
0712 
0713 void Curve::paintObjects(const CurveRenderContext& context) {
0714 #ifdef BENCHMARK
0715   QTime bench_time;
0716   bench_time.start();
0717 #endif
0718 
0719   QPainter *p = context.painter;
0720   double point_dim = pointDim(p->window());
0721   p->save();
0722 
0723   p->setRenderHint(QPainter::Antialiasing, context.antialias);
0724   Qt::PenStyle style = Kst::LineStyle[lineStyle()];
0725 
0726   if (hasBars()) {
0727 
0728     p->setPen(QPen(barFillColor(), _width, style));
0729 
0730     foreach(const QRectF& rect, _filledRects) {
0731         p->fillRect(rect, barFillColor());
0732     }
0733   }
0734   p->setPen(QPen(color(), _width, style)); //, Qt::RoundCap, Qt::RoundJoin));
0735 
0736   foreach(const QPolygonF& poly, _polygons) {
0737     tmpPolyLine(poly, p);
0738     //p->drawPolyline(poly);
0739   }
0740   //qDebug() << "E:" << bench_time.elapsed() << "ms w: " << p->viewport().width() << "h: " << p->viewport().height() << " a: " << p->viewport().width() * p->viewport().height();
0741   foreach(const QLineF& line, _lines) {
0742     p->drawLine(line);
0743   }
0744   foreach(const QRectF& rect, _rects) {
0745     p->drawRect(rect);
0746   }
0747 
0748   foreach(const QPointF& point, _points) {
0749     CurvePointSymbol::draw(PointType, p, point.x(), point.y(), point_dim);
0750   }
0751 
0752   if (hasHead() && _head_valid) {
0753     p->setPen(QPen(headColor(), _width, style));
0754     CurvePointSymbol::draw(HeadType, p, _head.x(), _head.y(), point_dim);
0755   }
0756   p->restore();
0757 #ifdef BENCHMARK
0758   std::cout << "curve drawing:" << bench_time.elapsed() << "ms\n";
0759 #endif
0760 }
0761 
0762 
0763 void Curve::updatePaintObjects(const CurveRenderContext& context) {
0764   _polygons.clear();
0765   _lines.clear();
0766   _points.clear();
0767   _filledRects.clear();
0768   _rects.clear();
0769 
0770   VectorPtr xv = *_inputVectors.find(XVECTOR);
0771   VectorPtr yv = *_inputVectors.find(YVECTOR);
0772   if (!xv || !yv) {
0773     return;
0774   }
0775 
0776   double Lx = context.Lx, Hx = context.Hx, Ly = context.Ly, Hy = context.Hy;
0777   double m_X = context.m_X, m_Y = context.m_Y;
0778   double b_X = context.b_X, b_Y = context.b_Y;
0779   double XMin = context.XMin, XMax = context.XMax;
0780   bool xLog = context.xLog, yLog = context.yLog;
0781   double xLogBase = context.xLogBase;
0782   double yLogBase = context.yLogBase;
0783   double maxY = 0.0, minY = 0.0;
0784   double rX = 0.0, rY, rEX, rEY;
0785   double X1 = 0.0, Y1 = 0.0;
0786   double X2 = 0.0, Y2 = 0.0;
0787   double last_x1, last_y1;
0788   bool overlap = false;
0789   int i_pt;
0790 
0791 #ifdef BENCHMARK
0792   QTime bench_time, benchtmp;
0793   int b_1 = 0, b_2 = 0, b_3 = 0, b_4 = 0;
0794   bench_time.start();
0795   benchtmp.start();
0796   int numberOfLinesDrawn = 0;
0797   int numberOfPointsDrawn = 0;
0798   int numberOfBarsDrawn = 0;
0799 #endif
0800 
0801   _width = lineDim(context.painter->window(), lineWidth());
0802 
0803   //qDebug() << context.painter->device()->width() << context.painter->device()->logicalDpiX() <<
0804   //            context.painter->device()->width()/context.painter->device()->logicalDpiX();
0805 
0806   double errorFlagDim = pointDim(context.painter->window());
0807   if (sampleCount() > 0) {
0808     int i0, iN;
0809 
0810     if (xv->isRising()) {
0811       i0 = indexNearX(XMin, xv, NS);
0812       if (i0 > 0) {
0813         --i0;
0814       }
0815       iN = indexNearX(XMax, xv, NS);
0816       if (iN < sampleCount() - 1) {
0817         ++iN;
0818       }
0819     } else {
0820       i0 = 0;
0821       iN = sampleCount() - 1;
0822     }
0823 
0824 #ifdef BENCHMARK
0825     clock_t linesStart = clock();
0826 #endif
0827     if (hasLines()) {
0828       QPolygonF points;
0829       points.reserve(MAX_NUM_POLYLINES);
0830 
0831       double lastPlottedX = 0;
0832       double lastPlottedY = 0;
0833       int index = 0;
0834       int i0Start = i0;
0835 
0836 // optimize - isnan seems expensive, at least in gcc debug mode
0837 //            cachegrind backs this up.
0838 #undef isnan
0839 #define isnan(x) (x != x)
0840       rX = xv->interpolate(i0, NS);
0841       rY = yv->interpolate(i0, NS);
0842       // if invalid point then look backward for the last valid point.
0843       while (i0 > 0 && (isnan(rX) || isnan(rY))) {
0844         --i0;
0845         rX = xv->interpolate(i0, NS);
0846         rY = yv->interpolate(i0, NS);
0847       }
0848 
0849       // if invalid point then look forward for the next valid point...
0850       if (isnan(rX) || isnan(rY)) {
0851         i0 = i0Start;
0852         while (i0 < iN && (isnan(rX) || isnan(rY))) {
0853           ++i0;
0854           rX = xv->interpolate(i0, NS);
0855           rY = yv->interpolate(i0, NS);
0856         }
0857       }
0858  
0859       if (xLog) {
0860         rX = logXLo(rX, xLogBase);
0861       }
0862       if (yLog) {
0863         rY = logYLo(rY, yLogBase);
0864       }
0865       last_x1 = m_X*rX + b_X;
0866       last_y1 = m_Y*rY + b_Y;
0867 
0868       i_pt = i0;
0869 
0870       while (i_pt < iN) {
0871         X2 = last_x1;
0872         Y2 = last_y1;
0873 
0874         ++i_pt;
0875         rX = xv->interpolate(i_pt, NS);
0876         rY = yv->interpolate(i_pt, NS);
0877         bool foundNan = false;
0878 
0879         // if necessary continue looking for the first valid point...
0880         while (i_pt < iN && (isnan(rX) || isnan(rY))) {
0881 #undef isnan
0882           foundNan = true;
0883           ++i_pt;
0884           rX = xv->interpolate(i_pt, NS);
0885           rY = yv->interpolate(i_pt, NS);
0886         }
0887 
0888         if (KDE_ISUNLIKELY(foundNan)) {
0889           if (points.size()>0) {
0890             _polygons.append(points);
0891 #ifdef BENCHMARK
0892             ++numberOfLinesDrawn;
0893 #endif
0894           }
0895           points.resize(0);
0896           if (overlap) {
0897             if (X2 >= Lx && X2 <= Hx) {
0898               if (maxY > Hy && minY <= Hy)
0899                 maxY = Hy;
0900               if (minY < Ly && maxY >= Ly)
0901                 minY = Ly;
0902               if (minY >= Ly && minY <= Hy && maxY >= Ly && maxY <= Hy) {
0903 #ifdef BENCHMARK
0904                 ++numberOfLinesDrawn;
0905 #endif
0906                 double fX2 = floor(X2)+0.5;
0907                 _lines.append(QLineF(fX2, minY, fX2, maxY));
0908               }
0909             }
0910             overlap = false;
0911           }
0912         }
0913 
0914         if (xLog) {
0915           rX = logXLo(rX, xLogBase);
0916         }
0917         if (yLog) {
0918           rY = logYLo(rY, yLogBase);
0919         }
0920         X1 = m_X*rX + b_X;
0921         Y1 = m_Y*rY + b_Y;
0922         last_x1 = X1;
0923         last_y1 = Y1;
0924 
0925         if (KDE_ISLIKELY(!foundNan)) {
0926           if (KDE_ISLIKELY(samePixel(X1, X2))) {
0927             if (KDE_ISLIKELY(overlap)) {
0928               if (KDE_ISUNLIKELY(Y1 > maxY)) {
0929                 maxY = Y1;
0930               }
0931               if (KDE_ISUNLIKELY(Y1 < minY)) {
0932                 minY = Y1;
0933               }
0934             } else {
0935               if (Y1 < Y2) {
0936                 minY = Y1;
0937                 maxY = Y2;
0938               } else {
0939                 maxY = Y1;
0940                 minY = Y2;
0941               }
0942               overlap = true;
0943             }
0944           } else {
0945             if (KDE_ISLIKELY(overlap)) {
0946               if (KDE_ISLIKELY(X2 >= Lx && X2 <= Hx)) {
0947                 if (KDE_ISUNLIKELY(maxY <= Hy && minY >= Ly)) {
0948 
0949                   if (points.size()>MAX_NUM_POLYLINES-2) {
0950                     _polygons.append(points);
0951                     points.resize(0);
0952 #ifdef BENCHMARK
0953                     ++numberOfLinesDrawn;
0954 #endif
0955                   }
0956                   double fX2 = floor(X2)+0.5;
0957 
0958                   if (KDE_ISUNLIKELY(minY == maxY)) {
0959                     points.append(QPointF(fX2, maxY));
0960                   } else if (KDE_ISUNLIKELY(Y2 == minY)) {
0961                     points.append(QPointF(fX2, maxY));
0962                     points.append(QPointF(fX2, minY));
0963                   } else if (KDE_ISUNLIKELY(Y2 == maxY)) {
0964                     points.append(QPointF(fX2, minY));
0965                     points.append(QPointF(fX2, maxY));
0966                   } else {
0967                     points.append(QPointF(fX2, minY));
0968                     points.append(QPointF(fX2, maxY));
0969                     if (KDE_ISLIKELY(Y2 >= Ly && Y2 <= Hy)) {
0970                       points.append(QPointF(fX2, Y2));
0971                     }
0972                   }
0973                   lastPlottedX = fX2;
0974                   lastPlottedY = Y2;
0975                 } else {
0976                   if (KDE_ISUNLIKELY(maxY > Hy && minY <= Hy)) {
0977                     maxY = Hy;
0978                   }
0979                   if (KDE_ISUNLIKELY(minY < Ly && maxY >= Ly)) {
0980                     minY = Ly;
0981                   }
0982                   if (KDE_ISUNLIKELY(minY >= Ly && minY <= Hy && maxY >= Ly && maxY <= Hy)) {
0983                     if (points.size()>0) {
0984                       _polygons.append(points);
0985                       points.resize(0);
0986 #ifdef BENCHMARK
0987                       ++numberOfLinesDrawn;
0988 #endif
0989                     }
0990 #ifdef BENCHMARK
0991   ++numberOfLinesDrawn;
0992 #endif
0993                     double fX2 = floor(X2)+0.5;
0994                     _lines.append(QLineF(fX2, minY, fX2, maxY));
0995                   }
0996                 }
0997               }
0998 
0999               overlap = false;
1000             }
1001 
1002             if (KDE_ISLIKELY(!((X1 < Lx && X2 < Lx) || (X1 > Hx && X2 > Hx)))) {
1003 
1004               // trim the line to be within the plot...
1005               if (KDE_ISUNLIKELY(isinf(X1))) {
1006                 Y1 = Y2;
1007                 if (X1 > 0.0) {
1008                   X1 = Hx;
1009                 } else {
1010                   X1 = Lx;
1011                 }
1012               }
1013 
1014               if (KDE_ISUNLIKELY(isinf(X2))) {
1015                 Y2 = Y1;
1016                 if (X2 > 0.0) {
1017                   X2 = Hx;
1018                 } else {
1019                   X2 = Lx;
1020                 }
1021               }
1022 
1023               if (KDE_ISUNLIKELY(isinf(Y1))) {
1024                 X1 = X2;
1025                 if (Y1 > 0.0) {
1026                   Y1 = Hy;
1027                 } else {
1028                   Y1 = Ly;
1029                 }
1030               }
1031 
1032               if (KDE_ISUNLIKELY(isinf(Y2))) {
1033                 X2 = X1;
1034                 if (Y2 > 0.0) {
1035                   Y2 = Hy;
1036                 } else {
1037                   Y2 = Ly;
1038                 }
1039               }
1040 
1041               if (KDE_ISUNLIKELY(X1 < Lx && X2 > Lx)) {
1042                 Y1 = (Y2 - Y1) / (X2 - X1) * (Lx - X1) + Y1;
1043                 X1 = Lx;
1044               } else if (KDE_ISUNLIKELY(X2 < Lx && X1 > Lx)) {
1045                 Y2 = (Y1 - Y2) / (X1 - X2) * (Lx - X2) + Y2;
1046                 X2 = Lx;
1047               }
1048 
1049               if (KDE_ISUNLIKELY(X1 < Hx && X2 > Hx)) {
1050                 Y2 = (Y2 - Y1) / (X2 - X1) * (Hx - X1) + Y1;
1051                 X2 = Hx;
1052               } else if (KDE_ISUNLIKELY(X2 < Hx && X1 > Hx)) {
1053                 Y1 = (Y1 - Y2) / (X1 - X2) * (Hx - X2) + Y2;
1054                 X1 = Hx;
1055               }
1056 
1057               if (KDE_ISUNLIKELY(Y1 < Ly && Y2 > Ly)) {
1058                 X1 = (X2 - X1) / (Y2 - Y1) * (Ly - Y1) + X1;
1059                 Y1 = Ly;
1060               } else if (KDE_ISUNLIKELY(Y2 < Ly && Y1 > Ly)) {
1061                 X2 = (X1 - X2) / (Y1 - Y2) * (Ly - Y2) + X2;
1062                 Y2 = Ly;
1063               }
1064 
1065               if (KDE_ISUNLIKELY(Y1 < Hy && Y2 > Hy)) {
1066                 X2 = (X2 - X1) / (Y2 - Y1) * (Hy - Y1) + X1;
1067                 Y2 = Hy;
1068               } else if (KDE_ISUNLIKELY(Y2 < Hy && Y1 > Hy)) {
1069                 X1 = (X1 - X2) / (Y1 - Y2) * (Hy - Y2) + X2;
1070                 Y1 = Hy;
1071               }
1072 
1073 
1074 #ifdef DEBUG_VECTOR_CURVE
1075 bool xInBounds = (X1 >= Lx && X1 <= Hx && X2 >= Lx && X2 <= Hx);
1076 bool yInBounds = (Y1 >= Ly && Y1 <= Hy && Y2 >= Ly && Y2 <= Hy);
1077 
1078 if (!xInBounds)
1079 qDebug() << "x not in bounds"
1080          << "X1:" << X1
1081          << "X2:" << X2
1082          << "Lx:" << Lx
1083          << "Hx:" << Hx
1084          << endl;
1085 if (!yInBounds)
1086 qDebug() << "y not in bounds"
1087          << "Y1:" << Y1
1088          << "Y2:" << Y2
1089          << "Ly:" << Ly
1090          << "Hy:" << Hy
1091          << endl;
1092 #endif
1093 
1094               if (X1 >= Lx && X1 <= Hx && X2 >= Lx && X2 <= Hx &&
1095                   Y1 >= Ly && Y1 <= Hy && Y2 >= Ly && Y2 <= Hy) {
1096 
1097 
1098                 if (KDE_ISUNLIKELY(points.size()==0)) {
1099                     points.append(QPointF(X2, Y2));
1100                     points.append(QPointF(X1, Y1));
1101                 } else if (samePixel(lastPlottedX, X2) &&
1102                     samePixel(lastPlottedY,Y2) &&
1103                     index < MAX_NUM_POLYLINES) {
1104                   points.append(QPointF(X1, Y1));
1105                 } else {
1106                   if (KDE_ISLIKELY(points.size()>1)) {
1107                     _polygons.append(points);
1108                     points.resize(0);
1109 #ifdef BENCHMARK
1110                     ++numberOfLinesDrawn;
1111 #endif
1112                   }
1113                   points.append(QPointF(X2, Y2));
1114                   points.append(QPointF(X1, Y1));
1115                 }
1116                 lastPlottedX = X1;
1117                 lastPlottedY = Y1;
1118               }
1119             }
1120           } // end if (X1 == X2)
1121         } // end if (!foundNan)
1122       } // end while
1123 
1124       // we might a have polyline left undrawn...
1125       if (points.size()>1) {
1126         _polygons.append(points);
1127         points.resize(0);
1128 #ifdef BENCHMARK
1129         ++numberOfLinesDrawn;
1130 #endif
1131       }
1132 
1133       // we might have some overlapping points still unplotted...
1134       if (overlap) {
1135         if (X2 >= Lx && X2 <= Hx) {
1136           if (maxY > Hy && minY <= Hy) {
1137             maxY = Hy;
1138           }
1139           if (minY < Ly && maxY >= Ly) {
1140             minY = Ly;
1141           }
1142           if (minY >= Ly && minY <= Hy && maxY >= Ly && maxY <= Hy) {
1143 #ifdef BENCHMARK
1144             ++numberOfLinesDrawn;
1145 #endif
1146            _lines.append(QLineF(X2, minY, X2, maxY));
1147           }
1148         }
1149         overlap = false;
1150       }
1151     } // end if hasLines()
1152 
1153 #ifdef BENCHMARK
1154     clock_t linesEnd = clock();
1155     std::cout << "\n        Lines clocks: " << (linesEnd - linesStart) << "\n";
1156     b_1 = benchtmp.elapsed();
1157 #endif
1158 
1159     VectorPtr exv = _inputVectors.contains(EXVECTOR) ? *_inputVectors.find(EXVECTOR) : 0;
1160     VectorPtr eyv = _inputVectors.contains(EYVECTOR) ? *_inputVectors.find(EYVECTOR) : 0;
1161     VectorPtr exmv = _inputVectors.contains(EXMINUSVECTOR) ? *_inputVectors.find(EXMINUSVECTOR) : 0;
1162     VectorPtr eymv = _inputVectors.contains(EYMINUSVECTOR) ? *_inputVectors.find(EYMINUSVECTOR) : 0;
1163     // draw the bargraph bars, if any...
1164     if (hasBars()) {
1165       bool visible = true;
1166       double rX2 = 0.0;
1167       double drX = 0.0;
1168       QRectF lastRect;
1169 
1170       if (!exv) {
1171         // determine the bar position width. NOTE: This is done
1172         //  only if xv->isRising() as in this case the calculation
1173         //  is simple...
1174         drX = (maxX() - minX())/double(sampleCount());
1175         if (xv->isRising()) {
1176           double oldX = 0.0;
1177 
1178           for (i_pt = i0; i_pt <= iN; ++i_pt) {
1179             rX = xv->interpolate(i_pt, NS);
1180             if (i_pt > i0) {
1181               if (rX - oldX < drX) {
1182                 drX = rX - oldX;
1183               }
1184             }
1185             oldX = rX; 
1186           }
1187         }
1188       }
1189 
1190       for (i_pt = i0; i_pt <= iN; ++i_pt) {
1191         visible = true;
1192 
1193         if (exv) {
1194           drX = exv->interpolate(i_pt, NS);
1195         }
1196         rX = xv->interpolate(i_pt, NS);
1197         rY = yv->interpolate(i_pt, NS);
1198         rX -= drX/2.0;
1199         rX2 = rX + drX;
1200         if (xLog) {
1201           rX = logXLo(rX, xLogBase);
1202           rX2 = logXLo(rX2, xLogBase);
1203         }
1204         if (yLog) {
1205           rY = logYLo(rY, yLogBase);
1206           Y2 = Hy;
1207         } else {
1208           Y2 = b_Y;
1209         }
1210 
1211         X1 = m_X * rX + b_X;
1212         X2 = m_X * rX2 + b_X;
1213         Y1 = m_Y * rY + b_Y;
1214 
1215         if ((X1 > Hx) || (X2 < Lx) || (Y1 == Ly && Y2 == Ly) || (Y1 == Hy && Y2 == Hy)) {
1216           visible = false;
1217         }
1218 
1219         if (visible) {
1220           QRectF rect(X1, Y1, X2 - X1, Y2 - Y1);
1221           if (!lastRect.contains(rect)) {
1222             //if (barStyle() == 1) { // filled
1223               _filledRects.append(rect);
1224             //} else {
1225               _rects.append(rect);
1226             //}
1227             lastRect = rect;
1228 #ifdef BENCHMARK
1229             ++numberOfBarsDrawn;
1230 #endif
1231           }
1232         }
1233       }
1234     }
1235 
1236 #ifdef BENCHMARK
1237     b_2 = benchtmp.elapsed();
1238 #endif
1239 
1240     // draw the points, if any...
1241     if (hasPoints()) {
1242       const double w = Hx - Lx;
1243       const double h = Hy - Ly;
1244       int size = 0;
1245       if (hasLines() && pointDensity() != 0) {
1246         // high density: 91 points
1247         // med density 27
1248         // low density 9 points
1249         size = w / int(pow(3.0, POINTDENSITY_MAXTYPE - pointDensity()+1));
1250       }
1251 
1252       QRectF rect(Lx, Ly, w, h);
1253       QPointF pt, lastPt;
1254 
1255       for (i_pt = i0; i_pt <= iN; ++i_pt) {
1256         rX = xv->interpolate(i_pt, NS);
1257         rY = yv->interpolate(i_pt, NS);
1258         if (xLog) {
1259           rX = logXLo(rX, xLogBase);
1260         }
1261         if (yLog) {
1262           rY = logYLo(rY, yLogBase);
1263         }
1264 
1265         pt.setX(m_X * rX + b_X);
1266         pt.setY(m_Y * rY + b_Y);
1267         if (rect.contains(pt) && pt != lastPt &&
1268             (lastPt.isNull() || (fabs(pt.x() - lastPt.x()) > size) || ((size==0) && (fabs(pt.y() - lastPt.y()) > 0)))) {
1269 #ifdef BENCHMARK
1270           ++numberOfPointsDrawn;
1271 #endif
1272             lastPt = pt;
1273             _points.append(pt);
1274         }
1275       }
1276     }
1277 
1278     _head_valid = false;
1279     if (hasHead()) {
1280       const double w = Hx - Lx;
1281       const double h = Hy - Ly;
1282 
1283       QRectF rect(Lx, Ly, w, h);
1284 
1285       rX = xv->interpolate(NS-1, NS);
1286       rY = yv->interpolate(NS-1, NS);
1287       if (xLog) {
1288         rX = logXLo(rX, xLogBase);
1289       }
1290       if (yLog) {
1291         rY = logYLo(rY, yLogBase);
1292       }
1293 
1294       _head.setX(m_X * rX + b_X);
1295       _head.setY(m_Y * rY + b_Y);
1296       if (rect.contains(_head)) {
1297         _head_valid = true;
1298       }
1299     }
1300 
1301 
1302 #ifdef BENCHMARK
1303     b_3 = benchtmp.elapsed();
1304 #endif
1305 
1306     // draw the x-errors, if any...
1307     if ((exv || exmv) && !hasBars()) {
1308       double rX1;
1309       double rX2;
1310       bool do_low_flag = true;
1311       bool do_high_flag = true;
1312       bool errorSame = false;
1313 
1314       if (exv && exmv) {
1315         if (exv->shortName() == exmv->shortName()) {
1316           errorSame = true;
1317         }
1318       }
1319 
1320       for (i_pt = i0; i_pt <= iN; ++i_pt) {
1321         do_low_flag = true;
1322         do_high_flag = true;
1323 
1324         rX = xv->interpolate(i_pt, NS);
1325         rY = yv->interpolate(i_pt, NS);
1326         if (errorSame) {
1327           rEX = fabs(exv->interpolate(i_pt, NS));
1328           if (xLog) {
1329             rX1 = logXLo(rX - rEX, xLogBase);
1330             rX2 = logXLo(rX + rEX, xLogBase);
1331           } else {
1332             rX1 = rX - rEX;
1333             rX2 = rX + rEX;
1334           }
1335         } else if (exv && exmv) {
1336           double rEXHi = fabs(exv->interpolate(i_pt, NS));
1337           double rEXLo = fabs(exmv->interpolate(i_pt, NS));
1338           if (xLog) {
1339             rX1 = logXLo(rX - rEXLo, xLogBase);
1340             rX2 = logXLo(rX + rEXHi, xLogBase);
1341           } else {
1342             rX1 = rX - rEXLo;
1343             rX2 = rX + rEXHi;
1344           }
1345         } else if (exv) {
1346           rEX = exv->interpolate(i_pt, NS);
1347           if (xLog) {
1348             rX1 = logXLo(rX, xLogBase);
1349             rX2 = logXLo(rX + fabs(rEX), xLogBase);
1350           } else {
1351             rX1 = rX;
1352             rX2 = rX + fabs(rEX);
1353           }
1354           do_low_flag = false;
1355         } else {
1356           rEX = fabs(exmv->interpolate(i_pt, NS));
1357           if (xLog) {
1358             rX1 = logXLo(rX - rEX, xLogBase);
1359             rX2 = logXLo(rX, xLogBase);
1360           } else {
1361             rX1 = rX - rEX;
1362             rX2 = rX;
1363           }
1364           do_high_flag = false;
1365         }
1366 
1367         if (yLog) {
1368           rY = logYLo(rY, yLogBase);
1369         }
1370 
1371         X1 = m_X * rX1 + b_X;
1372         X2 = m_X * rX2 + b_X;
1373         Y1 = m_Y * rY + b_Y;
1374 
1375         if (X1 < Lx && X2 > Lx) {
1376           X1 = Lx;
1377           do_low_flag = false;
1378         }
1379         if (X1 < Hx && X2 > Hx) {
1380           X2 = Hx;
1381           do_high_flag = false;
1382         }
1383 
1384         if (X1 >= Lx && X2 <= Hx && Y1 >= Ly && Y1 <= Hy) {
1385           _lines.append(QLineF(X1, Y1, X2, Y1));
1386           if (do_low_flag) {
1387             _lines.append(QLineF(X1, Y1 + errorFlagDim, X1, Y1 - errorFlagDim));
1388           }
1389           if (do_high_flag) {
1390             _lines.append(QLineF(X2, Y1 + errorFlagDim, X2, Y1 - errorFlagDim));
1391           }
1392         }
1393       }
1394     }
1395 
1396     // draw the y-errors, if any...
1397     if (eyv || eymv) {
1398       double rY1;
1399       double rY2;
1400       bool do_low_flag = true;
1401       bool do_high_flag = true;
1402       bool errorSame = false;
1403 
1404       if (eyv && eymv) {
1405         if (eyv->shortName() == eymv->shortName()) {
1406           errorSame = true;
1407         }
1408       }
1409 
1410       for (i_pt = i0; i_pt <= iN; ++i_pt) {
1411         do_low_flag = true;
1412         do_high_flag = true;
1413 
1414         rX = xv->interpolate(i_pt, NS);
1415         rY = yv->interpolate(i_pt, NS);
1416         if (errorSame) {
1417           rEY = eyv->interpolate(i_pt, NS);
1418           if (yLog) {
1419             rY1 = logYLo(rY-fabs(rEY), yLogBase);
1420             rY2 = logYLo(rY+fabs(rEY), yLogBase);
1421           } else {
1422             rY1 = rY-fabs(rEY);
1423             rY2 = rY+fabs(rEY);
1424           }
1425         } else if (eyv && eymv) {
1426           double rEYHi = fabs(eyv->interpolate(i_pt, NS));
1427           double rEYLo = fabs(eymv->interpolate(i_pt, NS));
1428           if (yLog) {
1429             rY1 = logYLo(rY - rEYLo, yLogBase);
1430             rY2 = logYLo(rY + rEYHi, yLogBase);
1431           } else {
1432             rY1 = rY - rEYLo;
1433             rY2 = rY + rEYHi;
1434           }
1435         } else if (eyv) {
1436           rEY = fabs(eyv->interpolate(i_pt, NS));
1437           if (yLog) {
1438             rY1 = logYLo(rY, yLogBase);
1439             rY2 = logYLo(rY + rEY, yLogBase);
1440           } else {
1441             rY1 = rY;
1442             rY2 = rY + rEY;
1443           }
1444           do_low_flag = false;
1445         } else {
1446           rEY = fabs(eymv->interpolate(i_pt, NS));
1447           if (yLog) {
1448             rY1 = logYLo(rY - rEY, yLogBase);
1449             rY2 = logYLo(rY, yLogBase);
1450           } else {
1451             rY1 = rY - rEY;
1452             rY2 = rY;
1453           }
1454           do_high_flag = false;
1455         }
1456 
1457         if (xLog) {
1458           rX = logXLo(rX, xLogBase);
1459         }
1460 
1461         X1 = m_X * rX + b_X;
1462         Y1 = m_Y * rY1 + b_Y;
1463         Y2 = m_Y * rY2 + b_Y;
1464 
1465         if (Y1 < Ly && Y2 > Ly) {
1466           Y1 = Ly;
1467           do_low_flag = false;
1468         }
1469         if (Y1 < Hy && Y2 > Hy) {
1470           Y2 = Hy;
1471           do_high_flag = false;
1472         }
1473 
1474         if (X1 >= Lx && X1 <= Hx && Y1 >= Ly && Y2 <= Hy) {
1475           _lines.append(QLineF(X1, Y1, X1, Y2));
1476           if (do_low_flag) {
1477           _lines.append(QLineF(X1 + errorFlagDim, Y1, X1 - errorFlagDim, Y1));
1478           }
1479           if (do_high_flag) {
1480           _lines.append(QLineF(X1 + errorFlagDim, Y2, X1 - errorFlagDim, Y2));
1481           }
1482         }
1483       }
1484     } // end if (hasYError())
1485   } // end if (sampleCount() > 0)
1486 
1487 #ifdef BENCHMARK
1488   b_4 = benchtmp.elapsed();
1489 #endif
1490 
1491 #ifdef BENCHMARK
1492   int i = bench_time.elapsed();
1493   //qDebug() << endl << "Plotting curve " << (void *)this << ": " << i << "ms";
1494   //qDebug() << "    Without locks: " << b_4 << "ms";
1495   //qDebug() << "    Number of lines drawn:" << numberOfLinesDrawn;
1496   //qDebug() << "    Number of points drawn:" << numberOfPointsDrawn;
1497   //qDebug() << "    Number of bars drawn:" << numberOfBarsDrawn;
1498   std::cout << "\nPlotting curve " << (void *)this << ": " << i << " ms";
1499   std::cout << "\n    Without locks: " << b_4 << "ms";
1500   std::cout << "\n    Number of lines drawn:" << numberOfLinesDrawn;
1501   std::cout << "\n    Number of points drawn:" << numberOfPointsDrawn;
1502   std::cout << "\n    Number of bars drawn:" << numberOfBarsDrawn;
1503   if (b_1 > 0)       std::cout << "\n            Lines: " << b_1 << " ms\n";
1504   if (b_2 - b_1 > 0) std::cout << "\n             Bars: " << (b_2 - b_1) << " ms\n";
1505   if (b_3 - b_2 > 0) std::cout << "\n           Points: " << (b_3 - b_2) << " ms\n";
1506   if (b_4 - b_3 > 0) std::cout << "\n           Errors: " << (b_4 - b_3) << " ms\n";
1507 #endif
1508 }
1509 
1510 
1511 void Curve::yRange(double xFrom, double xTo, double* yMin, double* yMax) {
1512   if (!yMin || !yMax) {
1513     return;
1514   }
1515 
1516   VectorPtr xv = *_inputVectors.find(XVECTOR);
1517   VectorPtr yv = *_inputVectors.find(YVECTOR);
1518   if (!xv || !yv) {
1519     *yMin = *yMax = 0;
1520     return;
1521   }
1522 
1523   // get range of the curve to search for min/max
1524   int i0, iN;
1525   if (xv->isRising()) {
1526     i0 = indexNearX(xFrom, xv, NS);
1527     iN = indexNearX(xTo, xv, NS);
1528   } else {
1529     i0 = 0;
1530     iN = sampleCount() - 1;
1531   }
1532   // search for min/max
1533   bool first = true;
1534   double newYMax = 0, newYMin = 0;
1535   for (int i_pt = i0; i_pt <= iN; ++i_pt) {
1536     double rX = xv->interpolate(i_pt, NS);
1537     double rY = yv->interpolate(i_pt, NS);
1538     // make sure this point is visible
1539     if (rX >= xFrom && rX <= xTo) {
1540       // update min/max
1541       if (first || rY > newYMax) {
1542         newYMax = rY;
1543       }
1544       if (first || rY < newYMin) {
1545         newYMin = rY;
1546       }
1547       first = false;
1548     }
1549   }
1550   *yMin = newYMin;
1551   *yMax = newYMax;
1552 }
1553 
1554 
1555 double Curve::distanceToPoint(double xpos, double dx, double ypos) const {
1556 // find the y distance between the curve and a point. return 1.0E300 if this distance is undefined. i don't want to use -1 because it will make the code which uses this function messy.
1557   VectorPtr xv = *_inputVectors.find(XVECTOR);
1558   if (!xv) {
1559     return 1.0E300; // anything better we can do?
1560   }
1561 
1562   double distance = 1.0E300;
1563 
1564   int i_near_x = getIndexNearXY(xpos, dx, ypos);
1565   double near_x, near_y;
1566   point(i_near_x, near_x, near_y);
1567 
1568   if (fabs(near_x - xpos) < dx) {
1569     distance = fabs(ypos - near_y); // initial estimate.
1570   }
1571 
1572   if (hasLines() && xv->isRising()) {
1573     // if hasLines then we should find the distance between the curve and the point, not the data and 
1574     //  the point. if isRising because it is (probably) to slow to use this technique if the data is 
1575     //  unordered. borrowed from indexNearX. use binary search to find the indices immediately above 
1576     //  and below our xpos.
1577     int i_top = NS - 1;
1578     int i_bot = 0;
1579 
1580     while (i_bot + 1 < i_top) {
1581       int i0 = (i_top + i_bot)/2;
1582 
1583       double rX = xv->interpolate(i0, NS);
1584       if (xpos < rX) {
1585         i_top = i0;
1586       } else {
1587         i_bot = i0;
1588       }
1589     }
1590     // end borrowed
1591 
1592     double x_bot, y_bot, x_top, y_top;
1593     point(i_bot, x_bot, y_bot);
1594     point(i_top, x_top, y_top);
1595 
1596     if (x_bot <= xpos && x_top >= xpos) {
1597       near_y = (y_top - y_bot) / (x_top - x_bot) * (xpos - x_bot) + y_bot; // calculate y value for line segment between x_bot and x_top.
1598       
1599       if (fabs(ypos - near_y) < distance) {
1600         distance = fabs(ypos - near_y);
1601       }
1602     }
1603   }
1604 
1605   return distance;
1606 }
1607 
1608 
1609 QSize Curve::legendSymbolSize(QPainter *p) {
1610   return QSize(p->fontMetrics().height()*3.5, p->fontMetrics().height());
1611 }
1612 
1613 
1614 void Curve::paintLegendSymbol(QPainter *p, const QSize &size) {
1615   QRect bound(QPoint(0,0),size);
1616 
1617   int width;
1618   
1619   if (lineWidth() == 0) {
1620     width = 1;
1621   } else {  
1622     width = lineWidth();
1623   }
1624   
1625   p->save();
1626   if (hasLines()) {
1627     // draw a line from left to right centered vertically
1628     p->setPen(QPen(color(), width, Kst::LineStyle[lineStyle()]));
1629     p->drawLine(QLineF(bound.left(), bound.top() + bound.height()*.5,
1630                 bound.right(), bound.top() + bound.height()*.5));
1631   }
1632   if (hasPoints()) {
1633     // draw a point in the middle
1634     p->setPen(QPen(color(), width));
1635     double point_dim = pointDim(p->window());
1636     CurvePointSymbol::draw(PointType, p, bound.left() + bound.width()*.5, bound.top() + bound.height()*.5, point_dim);
1637   }
1638   p->restore();
1639 }
1640 
1641 QString Curve::_automaticDescriptiveName() const {
1642   return tr("%1 vs %2").arg(yVector()->descriptiveName()).arg(xVector()->descriptiveName());
1643 }
1644 
1645 QString Curve::descriptionTip() const {
1646   QString tip;
1647 
1648   tip = tr("Curve: %1\nX: %2\nY: %3").arg(Name()).arg(xVector()->descriptionTip()).arg(yVector()->descriptionTip());
1649 
1650   if (hasXError()) {
1651     tip += tr("\nX+ Error: %1").arg(xErrorVector()->Name());
1652   }
1653 
1654   if (hasXMinusError()) {
1655     tip += tr("\nX- Error: %1").arg(xMinusErrorVector()->Name());
1656   }
1657 
1658   if (hasYError()) {
1659     tip += tr("\nY+ Error: %1").arg(yErrorVector()->Name());
1660   }
1661 
1662   if (hasYMinusError()) {
1663     tip += tr("\nY- Error: %1").arg(yMinusErrorVector()->Name());
1664   }
1665 
1666   if (hasLines()) {
1667     tip += tr("\nLines: Width %1 and Style %2").arg(lineWidth()).arg(lineStyle());
1668   }
1669 
1670   if (hasPoints()) {
1671     tip += tr("\nPoints: Style %1").arg(pointType());
1672   }
1673 
1674   if (hasBars()) {
1675     tip += tr("\nBars");
1676   }
1677 
1678   return tip;
1679 }
1680 
1681 
1682 double Curve::ns_maxX(int zoom_level) const {
1683   return xVector()->ns_max(zoom_level);
1684 }
1685 
1686 double Curve::ns_minX(int zoom_level) const {
1687   return xVector()->ns_min(zoom_level);
1688 }
1689 
1690 double Curve::ns_maxY(int zoom_level) const {
1691   return yVector()->ns_max(zoom_level);
1692 }
1693 
1694 double Curve::ns_minY(int zoom_level) const {
1695   return yVector()->ns_min(zoom_level);
1696 }
1697 
1698 
1699 }
1700 // vim: ts=2 sw=2 et