PlotCursor.cpp
Go to the documentation of this file.
00001 /******************************************************************************
00002  * Copyright (C) 2015 by Ralf Kaestner                                        *
00003  * ralf.kaestner@gmail.com                                                    *
00004  *                                                                            *
00005  * This program is free software; you can redistribute it and/or modify       *
00006  * it under the terms of the Lesser GNU General Public License as published by*
00007  * the Free Software Foundation; either version 3 of the License, or          *
00008  * (at your option) any later version.                                        *
00009  *                                                                            *
00010  * This program is distributed in the hope that it will be useful,            *
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the               *
00013  * Lesser GNU General Public License for more details.                        *
00014  *                                                                            *
00015  * You should have received a copy of the Lesser GNU General Public License   *
00016  * along with this program. If not, see <http://www.gnu.org/licenses/>.       *
00017  ******************************************************************************/
00018 
00019 #include <cmath>
00020 #include <limits>
00021 
00022 #include <QEvent>
00023 #include <QMouseEvent>
00024 #include <QPainter>
00025 #include <QPen>
00026 #include <QResizeEvent>
00027 
00028 #include <qwt/qwt_plot.h>
00029 #include <qwt/qwt_plot_canvas.h>
00030 #include <qwt/qwt_plot_curve.h>
00031 #include <qwt/qwt_scale_widget.h>
00032 
00033 #include <rqt_multiplot/CurveData.h>
00034 #include <rqt_multiplot/PlotCursorMachine.h>
00035 
00036 #include "rqt_multiplot/PlotCursor.h"
00037 
00038 namespace rqt_multiplot {
00039 
00040 /*****************************************************************************/
00041 /* Constructors and Destructor                                               */
00042 /*****************************************************************************/
00043 
00044 PlotCursor::PlotCursor(QwtPlotCanvas* canvas) :
00045   QwtPlotPicker(canvas),
00046   trackPoints_(false),
00047   mouseControl_(false) {
00048   setTrackerMode(QwtPicker::AlwaysOn);
00049   setStateMachine(new PlotCursorMachine());
00050 
00051   setRubberBand(QwtPicker::CrossRubberBand);
00052   setRubberBandPen(Qt::DashLine);
00053 
00054   connect(plot()->axisWidget(xAxis()), SIGNAL(scaleDivChanged()),
00055     this, SLOT(plotXAxisScaleDivChanged()));
00056   connect(plot()->axisWidget(yAxis()), SIGNAL(scaleDivChanged()),
00057     this, SLOT(plotYAxisScaleDivChanged()));
00058 }
00059 
00060 PlotCursor::~PlotCursor() {
00061 }
00062 
00063 /*****************************************************************************/
00064 /* Accessors                                                                 */
00065 /*****************************************************************************/
00066 
00067 void PlotCursor::setActive(bool active, const QPointF& position) {
00068   if (mouseControl_)
00069     return;
00070 
00071   if (active && !isActive()) {
00072     setTrackerMode(QwtPicker::AlwaysOff);
00073 
00074     begin();
00075     append(transform(position));
00076 
00077     currentPosition_ = position;
00078 
00079     emit currentPositionChanged(position);
00080   }
00081   else if (!active && isActive()) {
00082     remove();
00083     end();
00084 
00085     setTrackerMode(QwtPicker::AlwaysOn);
00086   }
00087 }
00088 
00089 void PlotCursor::setCurrentPosition(const QPointF& position) {
00090   if (mouseControl_)
00091     return;
00092 
00093   if (isActive() && (position != currentPosition_)) {
00094     currentPosition_ = position;
00095 
00096     blockSignals(true);
00097     move(transform(position));
00098     blockSignals(false);
00099   }
00100 }
00101 
00102 const QPointF& PlotCursor::getCurrentPosition() const {
00103   return currentPosition_;
00104 }
00105 
00106 void PlotCursor::setTrackPoints(bool track) {
00107   if (track != trackPoints_) {
00108     trackPoints_ = track;
00109 
00110     if (isActive())
00111       updateDisplay();
00112   }
00113 }
00114 
00115 bool PlotCursor::arePointsTracked() const {
00116   return trackPoints_;
00117 }
00118 
00119 bool PlotCursor::hasMouseControl() const {
00120   return mouseControl_;
00121 }
00122 
00123 QRect PlotCursor::getTextRect(const QPointF& point, const QFont& font) const {
00124   QwtText text = trackerTextF(point);
00125 
00126   if (text.isEmpty())
00127     return QRect();
00128 
00129   QSizeF textSize = text.textSize(font);
00130   QRect textRect(0, 0, qCeil(textSize.width()), qCeil(textSize.height()));
00131   QPoint position = transform(point);
00132 
00133   int alignment = Qt::AlignTop | Qt::AlignRight;
00134   int margin = 5;
00135   int x = position.x();
00136   int y = position.y();
00137 
00138   if (alignment & Qt::AlignLeft)
00139     x -= textRect.width() + margin;
00140   else if (alignment & Qt::AlignRight)
00141     x += margin;
00142 
00143   if (alignment & Qt::AlignBottom)
00144     y += margin;
00145   else if (alignment & Qt::AlignTop)
00146     y -= textRect.height() + margin;
00147 
00148   textRect.moveTopLeft(QPoint(x, y));
00149 
00150   #if QWT_VERSION >= 0x060100
00151     int left = qMax(textRect.left(), trackerRect(font).left()+margin);
00152     int right = qMin(textRect.right(), trackerRect(font).right()-margin);
00153     int top = qMax(textRect.top(), trackerRect(font).top()+margin);
00154     int bottom = qMin( textRect.bottom(), trackerRect(font).bottom()-margin);
00155   #else
00156     int left = qMax(textRect.left(), pickRect().left()+margin);
00157     int right = qMin(textRect.right(), pickRect().right()-margin);
00158     int top = qMax(textRect.top(), pickRect().top()+margin);
00159     int bottom = qMin( textRect.bottom(), pickRect().bottom()-margin);
00160   #endif
00161 
00162   textRect.moveBottomRight(QPoint(right, bottom));
00163   textRect.moveTopLeft(QPoint(left, top));
00164 
00165   return textRect;
00166 }
00167 
00168 QwtText PlotCursor::trackerTextF(const QPointF& point) const {
00169   QwtScaleMap xMap = plot()->canvasMap(xAxis());
00170   QwtScaleMap yMap = plot()->canvasMap(yAxis());
00171 
00172   double xPrecision = log10(fabs(xMap.invTransform(1.0)-
00173     xMap.invTransform(0.0)));
00174   double yPrecision = log10(fabs(yMap.invTransform(1.0)-
00175     yMap.invTransform(0.0)));
00176 
00177   QString x, y;
00178 
00179   if ((xPrecision < 0.0) && (fabs(point.x()) >= 1.0))
00180     x.sprintf("%.*f", (int)ceil(fabs(xPrecision)), point.x());
00181   else
00182     x.sprintf("%g", point.x());
00183 
00184   if ((yPrecision < 0.0) && (fabs(point.y()) >= 1.0))
00185     y.sprintf("%.*f", (int)ceil(fabs(yPrecision)), point.y());
00186   else
00187     y.sprintf("%g", point.y());
00188 
00189   return QwtText(x+", "+y);
00190 }
00191 
00192 /*****************************************************************************/
00193 /* Methods                                                                   */
00194 /*****************************************************************************/
00195 
00196 void PlotCursor::drawRubberBand(QPainter* painter) const {
00197   if (dynamic_cast<QWidget*>(painter->device())) {
00198     QPen pen = painter->pen();
00199     QColor penColor = pen.color();
00200 
00201     penColor.setAlphaF(0.3);
00202     pen.setColor(penColor);
00203 
00204     painter->setPen(pen);
00205   }
00206 
00207   QwtPlotPicker::drawRubberBand(painter);
00208 
00209   drawTrackedPoints(painter);
00210 }
00211 
00212 void PlotCursor::begin() {
00213   bool active = isActive();
00214 
00215   QwtPlotPicker::begin();
00216 
00217   if (!active && isActive())
00218     emit activeChanged(true);
00219 }
00220 
00221 void PlotCursor::move(const QPoint& point) {
00222   QPointF newPosition = invTransform(point);
00223 
00224   if (newPosition != currentPosition_) {
00225     currentPosition_ = newPosition;
00226 
00227     updateDisplay();
00228 
00229     emit currentPositionChanged(newPosition);
00230   }
00231 
00232   QwtPlotPicker::move(point);
00233 }
00234 
00235 bool PlotCursor::end(bool ok) {
00236   bool active = isActive();
00237 
00238   bool result = QwtPlotPicker::end(ok);
00239 
00240   if (active && !isActive())
00241     emit activeChanged(false);
00242 
00243   return result;
00244 }
00245 
00246 bool PlotCursor::eventFilter(QObject* object, QEvent* event) {
00247   if (object == plot()->canvas()) {
00248     if (event->type() == QEvent::Enter)
00249       mouseControl_ = true;
00250     else if (event->type() == QEvent::Leave)
00251       mouseControl_ = false;
00252     else if (event->type() == QEvent::MouseButtonRelease)
00253       updateDisplay();
00254   }
00255 
00256   bool result = QwtPlotPicker::eventFilter(object, event);
00257 
00258   if (isActive() && object == plot()->canvas()) {
00259     if (event->type() == QEvent::Resize)
00260       transition(event);
00261   }
00262 
00263   return result;
00264 }
00265 
00266 void PlotCursor::updateDisplay() {
00267   updateTrackedPoints();
00268 
00269   QwtPlotPicker::updateDisplay();
00270 }
00271 
00272 void PlotCursor::updateTrackedPoints() {
00273   trackedPoints_.clear();
00274 
00275   if (!trackPoints_ || !isActive())
00276     return;
00277 
00278   QwtScaleMap map = plot()->canvasMap(xAxis());
00279   double maxDistance = fabs(map.invTransform(1.0)-map.invTransform(0.0));
00280 
00281   for (QwtPlotItemIterator it = plot()->itemList().begin();
00282       it != plot()->itemList().end(); ++it) {
00283     if ((*it)->rtti() == QwtPlotItem::Rtti_PlotCurve) {
00284       QwtPlotCurve* curve = (QwtPlotCurve*)(*it);
00285       CurveData* data = dynamic_cast<CurveData*>(curve->data());
00286 
00287       if (data) {
00288         QVector<size_t> indexes = data->getPointsInDistance(
00289           currentPosition_.x(), maxDistance);
00290 
00291         if (!indexes.isEmpty()) {
00292           TrackedPoint trackedPoint;
00293           trackedPoint.color = curve->pen().color();
00294 
00295           double minDistance = std::numeric_limits<double>::max();
00296 
00297           for (size_t index = 0; index < indexes.count(); ++index) {
00298             QPointF point = data->getPoint(indexes[index]);
00299             QPointF vector = currentPosition_-point;
00300             double distance = vector.x()*vector.x()+vector.y()*vector.y();
00301 
00302             if (distance < minDistance) {
00303               trackedPoint.position = point;
00304               minDistance = distance;
00305             }
00306           }
00307 
00308           trackedPoints_.append(trackedPoint);
00309         }
00310       }
00311     }
00312   }
00313 }
00314 
00315 void PlotCursor::drawTrackedPoints(QPainter* painter) const {
00316   if (!trackPoints_)
00317     return;
00318 
00319   for (size_t index = 0; index < trackedPoints_.count(); ++index) {
00320     QPointF position = trackedPoints_[index].position;
00321     QPoint point = transform(position);
00322 
00323     if (dynamic_cast<QWidget*>(painter->device()))
00324       painter->setPen(trackedPoints_[index].color);
00325 
00326     painter->fillRect(point.x()-3, point.y()-3, 6, 6,
00327       painter->pen().color());
00328 
00329     QRect textRect = getTextRect(position, painter->font());
00330 
00331     if (!textRect.isEmpty()) {
00332       if (dynamic_cast<QWidget*>(painter->device())) {
00333         QwtText label = trackerTextF(position);
00334 
00335         if (!label.isEmpty())
00336           label.draw(painter, textRect);
00337       }
00338       else
00339         painter->fillRect(textRect, painter->pen().color());
00340     }
00341   }
00342 }
00343 
00344 /*****************************************************************************/
00345 /* Slots                                                                     */
00346 /*****************************************************************************/
00347 
00348 void PlotCursor::plotXAxisScaleDivChanged() {
00349   if (isActive()) {
00350     if (mouseControl_) {
00351       QPointF newPosition = currentPosition_;
00352 
00353       newPosition.setX(plot()->canvasMap(xAxis()).invTransform(
00354         pickedPoints()[0].x()));
00355 
00356       if (newPosition != currentPosition_) {
00357         currentPosition_ = newPosition;
00358 
00359         updateDisplay();
00360 
00361         emit currentPositionChanged(newPosition);
00362       }
00363     }
00364     else {
00365       QPoint newPosition = pickedPoints()[0];
00366 
00367       newPosition.setX(plot()->canvasMap(xAxis()).transform(
00368         currentPosition_.x()));
00369 
00370       blockSignals(true);
00371       move(newPosition);
00372       blockSignals(false);
00373     }
00374   }
00375 }
00376 
00377 void PlotCursor::plotYAxisScaleDivChanged() {
00378   if (isActive()) {
00379     if (mouseControl_) {
00380       QPointF newPosition = currentPosition_;
00381 
00382       newPosition.setY(plot()->canvasMap(yAxis()).invTransform(
00383         pickedPoints()[0].y()));
00384 
00385       if (newPosition != currentPosition_) {
00386         currentPosition_ = newPosition;
00387 
00388         updateDisplay();
00389 
00390         emit currentPositionChanged(newPosition);
00391       }
00392     }
00393     else {
00394       QPoint newPosition = pickedPoints()[0];
00395 
00396       newPosition.setY(plot()->canvasMap(yAxis()).transform(
00397         currentPosition_.y()));
00398 
00399       blockSignals(true);
00400       move(newPosition);
00401       blockSignals(false);
00402     }
00403   }
00404 }
00405 
00406 }


rqt_multiplot
Author(s): Ralf Kaestner
autogenerated on Thu Jun 6 2019 21:49:11