From 98f36ef5bb1eb8a8fff254274089fec76191993a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Michal=20Mal=C3=BD?= Date: Sun, 16 Feb 2014 16:29:27 +0100 Subject: [PATCH] Fix peak rendering. --- Anyanka.pro | 12 +- datamanager.cpp | 63 ++---- graphdrawer.cpp | 75 ------- graphdrawer.h | 21 -- graphtoimageexporter.cpp | 48 ++++ graphtoimageexporter.h | 45 ++++ gui/exportgraphtoimagedialog.h | 1 + gui/exportgraphtoimagedialog.ui | 7 + gui/graphview.cpp | 381 ++++++++++++++++---------------- gui/graphview.h | 72 +++--- gui/graphviewcontextmenu.cpp | 13 ++ gui/graphviewcontextmenu.h | 14 +- gui/signalview.cpp | 14 +- gui/signalview.h | 5 +- imagedrawer.cpp | 40 ++++ imagedrawer.h | 30 +++ integrator.cpp | 17 +- integrator.h | 2 +- logger.h | 2 + signal.h | 5 +- signalcontroller.cpp | 332 ++++++++++------------------ signalcontroller.h | 99 ++++++--- signaldrawer.cpp | 297 +++++++++++++++++++++++++ signaldrawer.h | 68 ++++++ singlerunsselectormodel.cpp | 5 +- 25 files changed, 1038 insertions(+), 630 deletions(-) delete mode 100644 graphdrawer.cpp delete mode 100644 graphdrawer.h create mode 100644 graphtoimageexporter.cpp create mode 100644 graphtoimageexporter.h create mode 100644 imagedrawer.cpp create mode 100644 imagedrawer.h create mode 100644 signaldrawer.cpp create mode 100644 signaldrawer.h diff --git a/Anyanka.pro b/Anyanka.pro index d17c3ff..ebc0bae 100644 --- a/Anyanka.pro +++ b/Anyanka.pro @@ -50,8 +50,10 @@ SOURCES += main.cpp\ datafileexporter.cpp \ datawriterbackend.cpp \ csvdatawriterbackend.cpp \ - graphdrawer.cpp \ - gui/exportgraphtoimagedialog.cpp + gui/exportgraphtoimagedialog.cpp \ + graphtoimageexporter.cpp \ + imagedrawer.cpp \ + signaldrawer.cpp HEADERS += \ datafilesloader.h \ @@ -82,8 +84,10 @@ HEADERS += \ datafileexporter.h \ datawriterbackend.h \ csvdatawriterbackend.h \ - graphdrawer.h \ - gui/exportgraphtoimagedialog.h + gui/exportgraphtoimagedialog.h \ + graphtoimageexporter.h \ + imagedrawer.h \ + signaldrawer.h FORMS += \ gui/mainwindow.ui \ diff --git a/datamanager.cpp b/datamanager.cpp index 09957a9..c10e0de 100644 --- a/datamanager.cpp +++ b/datamanager.cpp @@ -21,6 +21,7 @@ */ #include "datamanager.h" +#include "imagedrawer.h" #include "logger.h" #include "gui/exportgraphtoimagedialog.h" #include "gui/exportrawdatadialog.h" @@ -341,7 +342,7 @@ std::shared_ptr DataManager::loadSingleRun(QDir& dir) void DataManager::showOneSignal(std::shared_ptr ctrl) { - SignalView* swp = new SignalView(); + SignalView* swp = new SignalView(ctrl); swp->setDataTableModel(ctrl->dataTableModel()); swp->setIntegrationTableModel(ctrl->integrationTableModel()); @@ -352,22 +353,23 @@ void DataManager::showOneSignal(std::shared_ptr ctrl) swp->setTypeText(QString::fromStdString(ctrl->signal()->resourceToString())); swp->setXUnits(QString::fromStdString(ctrl->signal()->xunitToString())); swp->setYUnits(QString::fromStdString(ctrl->signal()->yunitToString())); - connect(swp->m_graphView, SIGNAL(crosshairErased()), ctrl.get(), SLOT(onViewCrosshairErased())); + + /*connect(swp->m_graphView, SIGNAL(crosshairErased()), ctrl.get(), SLOT(onViewCrosshairErased())); connect(swp->m_graphView, SIGNAL(crosshairMoved(int,int)), ctrl.get(), SLOT(onViewCrosshairMoved(int,int))); connect(swp->m_graphView, SIGNAL(integrated(int,int,int,int)), ctrl.get(), SLOT(onViewIntegrated(int,int,int,int))); connect(swp->m_graphView, SIGNAL(redrawNeeded()), ctrl.get(), SLOT(onViewRedrawNeeded())); connect(swp->m_graphView, SIGNAL(resized(int,int)), ctrl.get(), SLOT(onViewResized(int,int))); - connect(swp->m_graphView, SIGNAL(showContextMenuReq(int,int,QPoint)), ctrl.get(), SLOT(onViewShowContextMenu(int,int,QPoint))); + connect(swp->m_graphView, SIGNAL(showContextMelnuReq(int,int,QPoint)), ctrl.get(), SLOT(onViewShowContextMenu(int,int,QPoint))); connect(swp->m_graphView, SIGNAL(zoomed(int,int,int,int)), ctrl.get(), SLOT(onViewZoomed(int,int,int,int))); connect(ctrl.get(), SIGNAL(viewDrawGraph(double*,size_t,double,double)), swp->m_graphView, SLOT(onUpdateGraph(double*,size_t,double,double))); connect(ctrl.get(), SIGNAL(viewCtxMenuClosed()), swp->m_graphView, SLOT(onCtxMenuClosed())); - connect(ctrl.get(), SIGNAL(viewDrawIntegration(int,double,int,double,int,double,QString,QString,bool)), swp->m_graphView, - SLOT(onDrawIntegration(int,double,int,double,int,double,QString,QString,bool))); + connect(ctrl.get(), SIGNAL(viewDrawIntegration(int,double,int,double,int,double,QString,QString)), swp->m_graphView, + SLOT(onDrawIntegration(int,double,int,double,int,double,QString,QString))); connect(ctrl.get(), SIGNAL(viewUpdateCurrentValues(double,double)), swp, SLOT(onUpdateCurrentValues(double,double))); - connect(ctrl.get(), SIGNAL(viewRemoveCurrentValues()), swp, SLOT(onRemoveCurrentValues())); + connect(ctrl.get(), SIGNAL(viewRemoveCurrentValues()), swp, SLOT(onRemoveCurrentValues()));*/ emit addToDashboard(swp); - ctrl->draw(); + swp->m_graphView->refresh(); } /* Public slots */ @@ -384,13 +386,11 @@ void DataManager::onExportGraphToImage() if (sr == nullptr) return; - for (const std::pair>& p : sr->allControllers()) - dlg.addSignal(p.second->currentStartX(), p.second->currentStartY(), p.second->currentStopX(), p.second->currentStopY(), p.first); + for (const std::pair>& p : sr->allControllers()) { + dlg.addSignal(p.second->fromXAbs(), p.second->fromYAbs(), p.second->toXAbs(), p.second->toYAbs(), p.first); + } if (dlg.exec() == QDialog::Accepted) { - GraphDrawer drawer; - double* data; - size_t fromIdx, toIdx; double fromX = 0, toX = 0; double fromY = 0, toY = 0; int iw, ih; @@ -424,44 +424,23 @@ void DataManager::onExportGraphToImage() } qDebug() << fromX << fromY << toX << toY; - /* Get from an to indices */ - const std::vector& tvpairs = sig->values(); - uint idx; - for (idx = 0; idx < tvpairs.size(); idx++) { - if (tvpairs.at(idx).first >= fromX) { - fromIdx = idx; - break; - } - } - if (idx == tvpairs.size()) { - QMessageBox::warning(nullptr, "Error while exporting to image", "Value \"From X\" is higher than the range of the dataset."); - return; - } - for (;idx < tvpairs.size(); idx++) { - if (tvpairs.at(idx).first >= toX) { - toIdx = idx; - break; - } - } - if (idx == tvpairs.size()) - toIdx = idx - 1; - - qDebug() << "fromIdx" << fromIdx << "toIdx" << toIdx << "size" << tvpairs.size(); - data = ctrl->generateDrawData(fromIdx, toIdx); - if (data == nullptr) { + /*if (data == nullptr) { QMessageBox::warning(nullptr, "Error while exporting to image", "Cannot generate data to draw. Some values are probably invalid."); return; - } + }*/ + + ImageDrawer imgDrawer(iw, ih, this); + connect(ctrl.get(), SIGNAL(viewDrawGraph(double*,size_t,double,double)), &imgDrawer, SLOT(onUpdateGraph(double*,size_t,double,double))); + connect(ctrl.get(), SIGNAL(viewDrawIntegration(int,double,int,double,int,double,QString,QString)), &imgDrawer, SLOT(onDrawIntegration(int,double,int,double,int,double,QString,QString))); + //ctrl->onViewResized(iw, ih); + //ctrl->onViewZoomedByValues(fromX, fromY, toX, toY); - QPixmap* pixmap = drawer.drawGraph(data, toIdx - fromIdx, iw, ih, fromY, toY); - QImage result = pixmap->toImage(); + QImage result = imgDrawer.pixmap()->toImage(); imageWriter.setFileName(dlg.path() + "." + dlg.imageFormat()); imageWriter.setFormat(dlg.imageFormat().toLatin1()); if (!imageWriter.write(result)) QMessageBox::critical(nullptr, "Error while exporting to image", "Image could not have been written."); - delete pixmap; - delete data; } } diff --git a/graphdrawer.cpp b/graphdrawer.cpp deleted file mode 100644 index 44c0ed8..0000000 --- a/graphdrawer.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "graphdrawer.h" -#include - -#include - -GraphDrawer::GraphDrawer(QObject *parent) : - QObject(parent) -{ -} - -QPixmap* GraphDrawer::drawGraph(const double* const data, const size_t len, size_t w, size_t h, double min, double max) -{ - QPixmap* pixmap; - QPainter p; - int fromX = 0; - int fromY; - double xStep; - double yStep; - - if (w < 1 || h < 1 || data == nullptr || len < 1) - return nullptr; - - pixmap = new QPixmap(w, h); - p.begin(pixmap); - p.fillRect(0, 0, w, h, Qt::white); - p.setPen(QColor(Qt::blue)); - - xStep = static_cast(w) / len; - yStep = static_cast(h) / fabs(max - min); - fromY = h - yStep * data[0] - min; - for (uint i = 1; i < len; i++) { - int toX = xStep * i; - int toY = h - yStep * (data[i] - min); - p.drawLine(fromX, fromY, toX, toY); - - fromX = toX; - fromY = toY; - } - - p.end(); - return pixmap; -} - -bool GraphDrawer::overlayIntegratedPeak(QPixmap* canvas, int startX, int startY, int stopX, int stopY, int peakStartX, int peakStartY, const QString& time, - const QString& area) -{ - QPainter p; - QFont font("arial", 10); - QFontMetrics fm(font); - int textWidth; - if (canvas == nullptr) - return false; - - p.begin(canvas); - p.setPen(Qt::red); - p.drawLine(startX, startY, stopX, stopY); - - /* Draw AREA and TIME caption */ - p.setFont(font); - p.setPen(Qt::black); - textWidth = fm.width(time); - if (peakStartY - textWidth < 2) - peakStartY += textWidth - peakStartY + 2; - p.save(); - p.translate(peakStartX, peakStartY); - p.rotate(-90); - p.drawText(0, 0, time); - p.rotate(+90); - p.restore(); - p.drawText(peakStartX + 5, peakStartY, area); - p.end(); - - //TODO: Report back what region of the canvas has been updated to allow drawing optimizations - return true; -} diff --git a/graphdrawer.h b/graphdrawer.h deleted file mode 100644 index ad4987b..0000000 --- a/graphdrawer.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef GRAPHDRAWER_H -#define GRAPHDRAWER_H - -#include - -class GraphDrawer : public QObject -{ - Q_OBJECT -public: - explicit GraphDrawer(QObject* parent = nullptr); - QPixmap* drawGraph(const double* const data, const size_t len, size_t w, size_t h, double min, double max); - bool overlayIntegratedPeak(QPixmap* canvas, int startX, int startY, int stopX, int stopY, int peakStartX, int peakStartY, const QString& time, - const QString& area); - -signals: - -public slots: - -}; - -#endif // GRAPHDRAWER_H diff --git a/graphtoimageexporter.cpp b/graphtoimageexporter.cpp new file mode 100644 index 0000000..5ce57c5 --- /dev/null +++ b/graphtoimageexporter.cpp @@ -0,0 +1,48 @@ +#include "graphtoimageexporter.h" + +GraphToImageExporter::GraphToImageExporter(QObject* parent) : + QObject(parent) +{ +} + +GraphToImageExporter::ReturnCode GraphToImageExporter::setParameters(const double fromX, const double fromY, const double toX, const double toY, const int width, + const int height, const QByteArray format, const QString& filename, + const std::shared_ptr ctrl) +{ + uint idx; + + if (fromX >= toX) + return ReturnCode::E_INVALID_BOUNDS; + if (filename.length() == 0) + return ReturnCode::E_INVALID_FILENAME; + if (ctrl == nullptr) + return ReturnCode::E_NULL_CONTROLLER; + if (width < 1 || height < 1) + return ReturnCode::E_INVALID_SIZE; + + /* Get from an to indices */ + const std::vector& tvpairs = ctrl->signal()->values(); + for (idx = 0; idx < tvpairs.size(); idx++) { + if (tvpairs.at(idx).first >= fromX) { + m_fromIdx = idx; + break; + } + } + if (idx == tvpairs.size()) + return ReturnCode::E_INVALID_BOUNDS; + for (;idx < tvpairs.size(); idx++) { + if (tvpairs.at(idx).first >= toX) { + m_toIdx = idx; + break; + } + } + if (idx == tvpairs.size()) + m_toIdx = idx - 1; + + + //m_data = ctrl->generateDrawData(m_fromIdx, m_toIdx); + if (m_data == nullptr) + return ReturnCode::E_NULL_DATA; + + return ReturnCode::SUCCESS; +} diff --git a/graphtoimageexporter.h b/graphtoimageexporter.h new file mode 100644 index 0000000..d16cb0f --- /dev/null +++ b/graphtoimageexporter.h @@ -0,0 +1,45 @@ +#ifndef GRAPHTOIMAGEEXPORTER_H +#define GRAPHTOIMAGEEXPORTER_H + +#include "signalcontroller.h" +#include +#include + +class GraphToImageExporter : public QObject +{ + Q_OBJECT +public: + enum class ReturnCode { + SUCCESS, + E_INVALID_BOUNDS, + E_INVALID_FORMAT, + E_INVALID_FILENAME, + E_NULL_CONTROLLER, + E_INVALID_SIZE, + E_NULL_DATA + }; + + explicit GraphToImageExporter(QObject* parent = nullptr); + ReturnCode setParameters(const double fromX, const double fromY, const double toX, const double toY, const int width, const int height, + const QByteArray format, const QString& filename, const std::shared_ptr ctrl); + QList supportedImageFormats(); + +private: + const std::shared_ptr m_ctrl; + double* m_data; + QString m_filename; + double m_fromIdx; + double m_fromY; + double m_toIdx; + double m_toY; + int m_height; + int m_width; + QImageWriter m_writer; + +signals: + +public slots: + +}; + +#endif // GRAPHTOIMAGEEXPORTER_H diff --git a/gui/exportgraphtoimagedialog.h b/gui/exportgraphtoimagedialog.h index ff9d087..51db700 100644 --- a/gui/exportgraphtoimagedialog.h +++ b/gui/exportgraphtoimagedialog.h @@ -22,6 +22,7 @@ public: double fromY() const; QString imageFormat() const; int imageHeight() const; + bool includePeaks() const; QString path() const; QString selectedSignal() const; double toX() const; diff --git a/gui/exportgraphtoimagedialog.ui b/gui/exportgraphtoimagedialog.ui index 4540eb2..43e2bea 100644 --- a/gui/exportgraphtoimagedialog.ui +++ b/gui/exportgraphtoimagedialog.ui @@ -109,6 +109,13 @@ + + + + Include integrated peaks + + + diff --git a/gui/graphview.cpp b/gui/graphview.cpp index 1a0fcdf..0d2128e 100644 --- a/gui/graphview.cpp +++ b/gui/graphview.cpp @@ -21,26 +21,43 @@ */ #include "gui/graphview.h" +#include "logger.h" #include #include -GraphView::GraphView(QWidget* parent) : - QWidget(parent), - m_ctxMenuOpen(false), - m_backbuffer(nullptr), - m_plainGraph(nullptr), +const double GraphView::DEFAULT_Y_MARGIN = 0.05; +const QString GraphView::ME_SENDER_STR("GraphView"); + +GraphView::GraphView(std::shared_ptr controller, QWidget* parent) : + QWidget(parent), SignalDrawer(controller), m_mouseMode(GraphView::MouseMode::CROSSHAIR), - m_drawData(nullptr), - m_graphCrosshairX(-1), - m_graphCrosshairY(-1) + m_graphCrosshairXPix(-1), + m_graphCrosshairYPix(-1) { - m_graphDrawer = std::unique_ptr(new GraphDrawer); + setDefaultZoom(); setMouseTracking(true); setCursor(Qt::BlankCursor); + + connect(&m_ctxMenu, SIGNAL(deletePeak(QPoint)), this, SLOT(onCtxMenuDeletePeak(QPoint))); + connect(&m_ctxMenu, SIGNAL(zoomOut()), this, SLOT(onCtxMenuZoomOut())); } -/* Public functions */ +/** Public methods **/ + +bool GraphView::refresh(QRegion reg) +{ + bool ret = draw(m_relXMin, m_relYMin, m_relXMax, m_relYMax); + if (ret) { + if (reg.isEmpty()) + update(); + else + update(reg); + } else + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, __QFUNC__ + " unable to draw pixmap"); + + return ret; +} void GraphView::mouseDoubleClickEvent(QMouseEvent* ev) { @@ -49,7 +66,6 @@ void GraphView::mouseDoubleClickEvent(QMouseEvent* ev) case Qt::LeftButton: break; case Qt::RightButton: - break; default: break; } @@ -74,78 +90,84 @@ void GraphView::leaveEvent(QEvent *) void GraphView::mouseMoveEvent(QMouseEvent* ev) { + const int xPix = ev->x(); + const int yPix = ev->y(); + switch (m_mouseMode) { case GraphView::MouseMode::CROSSHAIR: - this->drawCrosshair(ev->x(), ev->y()); + drawCrosshair(xPix, yPix); break; case GraphView::MouseMode::ZOOM: - this->drawZoomRect(ev->x(), ev->y()); + drawZoomRect(xPix, yPix); break; case GraphView::MouseMode::INTEGRATE: - this->drawIntegrationBaseline(ev->x(), ev->y()); + drawIntegrationBaseline(xPix, yPix); break; } - emit crosshairMoved(ev->x(), ev->y()); + updateValuesUnderCrosshair(xPix); + emit crosshairMoved(xPix, yPix); } void GraphView::mousePressEvent(QMouseEvent* ev) { - if (m_ctxMenuOpen) - return; - switch (ev->button()) { case Qt::LeftButton: switch (m_mouseMode) { case GraphView::MouseMode::CROSSHAIR: if (m_graphCtrlMode == GraphControlModes::ZOOM) { - m_zoomRectStartX = ev->x(); - m_zoomRectStartY = ev->y(); - m_zoomRectLastX = ev->x(); - m_zoomRectLastY = ev->y(); - this->eraseCrosshair(true); + m_zoomRectStartXPix = ev->x(); + m_zoomRectStartYPix = ev->y(); + m_zoomRectLastXPix = ev->x(); + m_zoomRectLastYPix = ev->y(); + eraseCrosshair(true); m_mouseMode = GraphView::MouseMode::ZOOM; } else if (m_graphCtrlMode == GraphControlModes::INTEGRATE) { - m_integrateStartX = ev->x(); - m_integrateStartY = ev->y(); - m_integrateStopX = ev->x(); - m_integrateStopY = ev->y(); - this->eraseCrosshair(true); - m_graphCrosshairX = -1; + m_integrateStartXPix = ev->x(); + m_integrateStartYPix = ev->y(); + m_integrateStopXPix = ev->x(); + m_integrateStopYPix = ev->y(); + eraseCrosshair(true); + m_graphCrosshairXPix = -1; m_mouseMode = GraphView::MouseMode::INTEGRATE; } break; case GraphView::MouseMode::ZOOM: - if (m_zoomRectLastX < m_zoomRectStartX) - std::swap(m_zoomRectLastX, m_zoomRectStartX); - if (m_zoomRectLastY < m_zoomRectStartY) - std::swap(m_zoomRectLastY, m_zoomRectStartY); - emit zoomed(m_zoomRectStartX, m_zoomRectStartY, m_zoomRectLastX, m_zoomRectLastY); + if (m_zoomRectLastXPix < m_zoomRectStartXPix) + std::swap(m_zoomRectLastXPix, m_zoomRectStartXPix); + if (m_zoomRectLastYPix > m_zoomRectStartYPix) + std::swap(m_zoomRectLastYPix, m_zoomRectStartYPix); + + zoom(m_zoomRectStartXPix, m_zoomRectStartYPix, m_zoomRectLastXPix, m_zoomRectLastYPix); m_mouseMode = GraphView::MouseMode::CROSSHAIR; break; case GraphView::MouseMode::INTEGRATE: - if (m_integrateStartX > m_integrateStopX) - std::swap(m_integrateStartX, m_integrateStopX); - emit integrated(m_integrateStartX, m_integrateStartY, m_integrateStopX, m_integrateStopY); - this->eraseIntegrationBaseline(true); - m_integrateStartX = -1; + if (m_integrateStartXPix > m_integrateStopXPix) + std::swap(m_integrateStartXPix, m_integrateStopXPix); + + PeakDrawData pdData = m_controller->integratePeak(xPixToRel(m_integrateStartXPix), yPixToRel(m_integrateStartYPix), + xPixToRel(m_integrateStopXPix), yPixToRel(m_integrateStopYPix)); + eraseIntegrationBaseline(true); + update(renderPeak(pdData)); + m_integrateStartXPix = -1; m_mouseMode = GraphView::MouseMode::CROSSHAIR; break; } + break; case Qt::RightButton: switch (m_mouseMode) { case GraphView::MouseMode::CROSSHAIR: - m_ctxMenuOpen = true; - this->eraseCrosshair(true); - emit showContextMenuReq(ev->x(), ev->y(), ev->globalPos()); + eraseCrosshair(true); + showContextMenu(ev->pos(), ev->globalPos()); break; case GraphView::MouseMode::ZOOM: - this->eraseZoomRect(true); + eraseZoomRect(true); m_mouseMode = GraphView::MouseMode::CROSSHAIR; break; case GraphView::MouseMode::INTEGRATE: - this->eraseIntegrationBaseline(true); + eraseIntegrationBaseline(true); + m_integrateStartXPix = -1; m_mouseMode = GraphView::MouseMode::CROSSHAIR; break; default: @@ -160,48 +182,44 @@ void GraphView::mousePressEvent(QMouseEvent* ev) void GraphView::paintEvent(QPaintEvent* ev) { QPainter p(this); - if (m_backbuffer != nullptr) - p.drawPixmap(0, 0, width(), height(), *m_backbuffer); + if (m_pixmap != nullptr) + p.drawPixmap(0, 0, dwidth(), dheight(), *m_pixmap); } void GraphView::resizeEvent(QResizeEvent* ev) { - if (ev->size().width() > 1 && ev->size().height() > 1) { - emit resized(ev->size().width(), ev->size().height()); - emit redrawNeeded(); + if (setDimensions(ev->size().width(), ev->size().height())) { + draw(m_relXMin, m_relYMin, m_relXMax, m_relYMax); + emit resized(dwidth(), dheight()); } } -/* Private functions */ +/** Private methods **/ -void GraphView::drawCrosshair(const int x, const int y) +void GraphView::drawCrosshair(const int xPix, const int yPix) { QPainter p; - int w, h; - if (m_backbuffer == nullptr) { - qDebug() << "NPTR"; + if (m_pixmap == nullptr) { + //qDebug() << "NPTR"; return; } - w = this->width(); - h = this->height(); - QRegion reg = eraseCrosshair(); - p.begin(m_backbuffer); + p.begin(m_pixmap); p.setPen(Qt::black); /* Draw new crosshair */ - p.drawLine(0, y, w, y); /* Horizontal */ - p.drawLine(x, 0, x, h); /* Vertical */ + p.drawLine(0, yPix, dwidth(), yPix); /* Horizontal */ + p.drawLine(xPix, 0, xPix, dheight()); /* Vertical */ p.end(); /* New lines */ - reg += QRect(0, y, w, 1); - reg += QRect(x, 0, 1, h); + reg += QRect(0, yPix, width(), 1); + reg += QRect(xPix, 0, 1, height()); - m_graphCrosshairX = x; - m_graphCrosshairY = y; + m_graphCrosshairXPix = xPix; + m_graphCrosshairYPix = yPix; update(reg); } @@ -210,86 +228,67 @@ void GraphView::drawIntegrationBaseline(const int x, const int y) { QPainter p; QRegion reg; - int w = this->width(); - int h = this->height(); int lstartX, lstartY, lstopX, lstopY; - if (m_backbuffer == nullptr) + if (m_pixmap == nullptr) return; reg = eraseIntegrationBaseline(); - if (m_integrateStartX > m_integrateStopX) { - lstartX = (m_integrateStopX > 1) ? m_integrateStopX - 1 : 0; - lstopX = (m_integrateStartX < w-1) ? m_integrateStartX + 1 : w-1; + if (m_integrateStartXPix > m_integrateStopXPix) { + lstartX = (m_integrateStopXPix > 1) ? m_integrateStopXPix - 1 : 0; + lstopX = (m_integrateStartXPix < dwidth()-1) ? m_integrateStartXPix + 1 : dwidth()-1; } else { - lstartX = (m_integrateStartX > 1) ? m_integrateStartX -1 : 0; - lstopX = (m_integrateStopX < w-1) ? m_integrateStopX + 1 : w-1; + lstartX = (m_integrateStartXPix > 1) ? m_integrateStartXPix -1 : 0; + lstopX = (m_integrateStopXPix < dwidth()-1) ? m_integrateStopXPix + 1 : dwidth()-1; } - if (m_integrateStartY > m_integrateStopY) { - lstartY = (m_integrateStopY > 1) ? m_integrateStopY - 1 : 0; - lstopY = (m_integrateStartY < h-1) ? m_integrateStartY + 1 : h-1; + if (m_integrateStartYPix > m_integrateStopYPix) { + lstartY = (m_integrateStopYPix > 1) ? m_integrateStopYPix - 1 : 0; + lstopY = (m_integrateStartYPix < dheight()-1) ? m_integrateStartYPix + 1 : dheight()-1; } else { - lstartY = (m_integrateStartY > 1) ? m_integrateStartY - 1 : 0; - lstopY = (m_integrateStopY < h-1) ? m_integrateStopY + 1 : h-1; + lstartY = (m_integrateStartYPix > 1) ? m_integrateStartYPix - 1 : 0; + lstopY = (m_integrateStopYPix < dheight()-1) ? m_integrateStopYPix + 1 : dheight()-1; } - p.begin(m_backbuffer); + p.begin(m_pixmap); /* Draw new line */ p.setPen(Qt::black); - p.drawLine(m_integrateStartX, m_integrateStartY, x, y); + p.drawLine(m_integrateStartXPix, m_integrateStartYPix, x, y); p.end(); reg += QRect(lstartX, lstartY, lstopX, lstopY); - m_integrateStopX = x; - m_integrateStopY = y; + m_integrateStopXPix = x; + m_integrateStopYPix = y; update(reg); } - -void GraphView::drawGraph() -{ - int w = this->width(); - int h = this->height(); - QPixmap* fresh = m_graphDrawer->drawGraph(m_drawData, m_drawDataLen, w, h, m_drawDataMin, m_drawDataMax); - if (fresh == nullptr) - return; - - if (m_backbuffer != nullptr) - delete m_backbuffer; - m_backbuffer = fresh; - if (m_plainGraph != nullptr) - delete m_plainGraph; - m_plainGraph = new QPixmap(*m_backbuffer); -} - void GraphView::drawZoomRect(const int x, const int y) { QPainter p; QRegion reg; int lstartX, lstartY, lstopX, lstopY; - if (m_backbuffer == nullptr) + if (m_pixmap == nullptr) return; - if (x < m_zoomRectStartX) { + if (x < m_zoomRectStartXPix) { lstartX = x; - lstopX = m_zoomRectStartX; + lstopX = m_zoomRectStartXPix; } else { - lstartX = m_zoomRectStartX; + lstartX = m_zoomRectStartXPix; lstopX = x; } - if (y < m_zoomRectStartY) { + if (y < m_zoomRectStartYPix) { lstartY = y; - lstopY = m_zoomRectStartY; + lstopY = m_zoomRectStartYPix; } else { - lstartY = m_zoomRectStartY; + lstartY = m_zoomRectStartYPix; lstopY = y; } reg = eraseZoomRect(); - p.begin(m_backbuffer); + p.begin(m_pixmap); p.setPen(Qt::green); // Top line p.drawLine(lstartX, lstartY, lstopX, lstartY); @@ -306,32 +305,28 @@ void GraphView::drawZoomRect(const int x, const int y) reg += QRect(lstopX, lstartY, 1, lstopY - lstartY + 1); //Right line; reg += QRect(lstartX, lstopY, lstopX - lstartX, 1); // Bottom line - m_zoomRectLastX = x; - m_zoomRectLastY = y; + m_zoomRectLastXPix = x; + m_zoomRectLastYPix = y; update(reg); } -QRegion GraphView::eraseCrosshair(bool apply) +QRegion GraphView::eraseCrosshair(const bool apply) { QPainter p; - int w, h; - if (m_backbuffer == nullptr) + if (m_pixmap == nullptr) return QRegion(); - w = this->width(); - h = this->height(); - - if (m_graphCrosshairX != -1 || m_graphCrosshairY != -1) { + if (m_graphCrosshairXPix != -1 || m_graphCrosshairYPix != -1) { QRegion reg; - p.begin(m_backbuffer); - p.drawPixmap(m_graphCrosshairX, 0, *m_plainGraph, m_graphCrosshairX, 0, 1, h); - p.drawPixmap(0, m_graphCrosshairY, *m_plainGraph, 0, m_graphCrosshairY, w, 1); + p.begin(m_pixmap); + p.drawPixmap(m_graphCrosshairXPix, 0, *m_background, m_graphCrosshairXPix, 0, 1, dheight()); + p.drawPixmap(0, m_graphCrosshairYPix, *m_background, 0, m_graphCrosshairYPix, dwidth(), 1); p.end(); - reg = QRect(0, m_graphCrosshairY, w, 1); - reg += QRect(m_graphCrosshairX, 0, 1, h); + reg = QRect(0, m_graphCrosshairYPix, dwidth(), 1); + reg += QRect(m_graphCrosshairXPix, 0, 1, dheight()); if (apply) update(reg); @@ -347,33 +342,29 @@ QRegion GraphView::eraseIntegrationBaseline(bool apply) QPainter p; QRegion reg; int lstartX, lstartY, lstopX, lstopY; - int w, h; - if (m_backbuffer == nullptr) + if (m_pixmap == nullptr) return QRegion(); - if (m_integrateStartX < 0) + if (m_integrateStartXPix < 0) return QRegion(); - w = this->width(); - h = this->height(); - - p.begin(m_backbuffer); - if (m_integrateStartX > m_integrateStopX) { - lstartX = (m_integrateStopX > 1) ? m_integrateStopX - 1 : 0; - lstopX = (m_integrateStartX < w-1) ? m_integrateStartX + 1 : w-1; + p.begin(m_pixmap); + if (m_integrateStartXPix > m_integrateStopXPix) { + lstartX = (m_integrateStopXPix > 1) ? m_integrateStopXPix - 1 : 0; + lstopX = (m_integrateStartXPix < dwidth()-1) ? m_integrateStartXPix + 1 : dwidth()-1; } else { - lstartX = (m_integrateStartX > 1) ? m_integrateStartX -1 : 0; - lstopX = (m_integrateStopX < w-1) ? m_integrateStopX + 1 : w-1; + lstartX = (m_integrateStartXPix > 1) ? m_integrateStartXPix -1 : 0; + lstopX = (m_integrateStopXPix < dwidth()-1) ? m_integrateStopXPix + 1 : dwidth()-1; } - if (m_integrateStartY > m_integrateStopY) { - lstartY = (m_integrateStopY > 1) ? m_integrateStopY - 1 : 0; - lstopY = (m_integrateStartY < h-1) ? m_integrateStartY + 1 : h-1; + if (m_integrateStartYPix > m_integrateStopYPix) { + lstartY = (m_integrateStopYPix > 1) ? m_integrateStopYPix - 1 : 0; + lstopY = (m_integrateStartYPix < dheight()-1) ? m_integrateStartYPix + 1 : dheight()-1; } else { - lstartY = (m_integrateStartY > 1) ? m_integrateStartY - 1 : 0; - lstopY = (m_integrateStopY < h-1) ? m_integrateStopY + 1 : h-1; + lstartY = (m_integrateStartYPix > 1) ? m_integrateStartYPix - 1 : 0; + lstopY = (m_integrateStopYPix < dheight()-1) ? m_integrateStopYPix + 1 : dheight()-1; } - p.drawPixmap(lstartX, lstartY, *m_plainGraph, lstartX, lstartY, lstopX - lstartX+1, lstopY - lstartY+1); //FIXME: +1 should not be needed! + p.drawPixmap(lstartX, lstartY, *m_background, lstartX, lstartY, lstopX - lstartX+1, lstopY - lstartY+1); //FIXME: +1 should not be needed! p.end(); reg = QRect(lstartX, lstartY, lstopX - lstartX +1, lstopY - lstartY + 1); @@ -384,40 +375,40 @@ QRegion GraphView::eraseIntegrationBaseline(bool apply) return reg; } -QRegion GraphView::eraseZoomRect(bool apply) +QRegion GraphView::eraseZoomRect(const bool apply) { int lstartX, lstartY, lstopX, lstopY; QPainter p; QRegion reg; - if (m_backbuffer == nullptr) + if (m_pixmap == nullptr) return QRegion(); - if (m_zoomRectLastX < m_zoomRectStartX) { - lstartX = m_zoomRectLastX; - lstopX = m_zoomRectStartX; + if (m_zoomRectLastXPix < m_zoomRectStartXPix) { + lstartX = m_zoomRectLastXPix; + lstopX = m_zoomRectStartXPix; } else { - lstartX = m_zoomRectStartX; - lstopX = m_zoomRectLastX; + lstartX = m_zoomRectStartXPix; + lstopX = m_zoomRectLastXPix; } - if (m_zoomRectLastY < m_zoomRectStartY) { - lstartY = m_zoomRectLastY; - lstopY = m_zoomRectStartY; + if (m_zoomRectLastYPix < m_zoomRectStartYPix) { + lstartY = m_zoomRectLastYPix; + lstopY = m_zoomRectStartYPix; } else { - lstartY = m_zoomRectStartY; - lstopY = m_zoomRectLastY; + lstartY = m_zoomRectStartYPix; + lstopY = m_zoomRectLastYPix; } /* Erase any existing lines */ - p.begin(m_backbuffer); + p.begin(m_pixmap); // TODO: It might not be necessary to erase all lines every time - OPTIMIZE THIS !!! // Top line - p.drawPixmap(lstartX, lstartY, *m_plainGraph, lstartX, lstartY, lstopX - lstartX, 1); + p.drawPixmap(lstartX, lstartY, *m_background, lstartX, lstartY, lstopX - lstartX, 1); // Left line - p.drawPixmap(lstartX, lstartY, *m_plainGraph, lstartX, lstartY, 1, lstopY - lstartY); + p.drawPixmap(lstartX, lstartY, *m_background, lstartX, lstartY, 1, lstopY - lstartY); // Right line - p.drawPixmap(lstopX, lstartY, *m_plainGraph, lstopX, lstartY, 1, lstopY - lstartY + 1); + p.drawPixmap(lstopX, lstartY, *m_background, lstopX, lstartY, 1, lstopY - lstartY + 1); // Bottom line - p.drawPixmap(lstartX, lstopY, *m_plainGraph, lstartX, lstopY, lstopX - lstartX + 1, 1); + p.drawPixmap(lstartX, lstopY, *m_background, lstartX, lstopY, lstopX - lstartX + 1, 1); p.end(); reg = QRect(lstartX, lstartY, lstopX - lstartX, 1); // Top line @@ -431,49 +422,59 @@ QRegion GraphView::eraseZoomRect(bool apply) return reg; } -/* Public slots */ -void GraphView::onDrawIntegration(const int fromX, const double fromY, const int toX, const double toY, const int peakX, const double peakY, - const QString& time, const QString& area, const bool valley) +void GraphView::showContextMenu(const QPoint& pos, const QPoint& globalPos) +{ + m_ctxMenu.setRelPixPos(pos); + m_ctxMenu.exec(globalPos); +} + +void GraphView::updateValuesUnderCrosshair(const int xPix) +{ + double x = xPixToRel(xPix); + Signal::TimeValuePair tvp = m_controller->getXYValues(x); + + emit crosshairValuesUpdated(tvp.first, tvp.second); +} + +void GraphView::unzoom() { - int w = this->width(); - int h = this->height(); - double xStep = static_cast(w) / m_drawDataLen; - double yStep = static_cast(h) / (m_drawDataMax - m_drawDataMin); - int startX = floor(fromX * xStep + 0.5); - int stopX = floor(toX * xStep + 0.5); - int startY = floor((h - yStep * (fromY - m_drawDataMin)) + 0.5); - int stopY = floor((h - yStep * (toY - m_drawDataMin)) + 0.5); - int peakStartX = floor(peakX * xStep + 0.5); - int peakStartY = floor(h - yStep * (peakY - m_drawDataMin) + 0.5); - - if (m_graphDrawer->overlayIntegratedPeak(m_plainGraph, startX, startY, stopX, stopY, peakStartX, peakStartY, time, area)) { - //TODO: Optimize so that the whole graph is not redrawn on each integration - QPainter pBB(m_backbuffer); - pBB.drawPixmap(QRect(0, 0, w, h), *m_plainGraph); + setDefaultZoom(); + if (draw(m_relXMin, m_relYMin, m_relXMax, m_relYMax)) update(); - } + else + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, __QFUNC__ + " unable to draw pixmap"); } -void GraphView::onUpdateGraph(double* data, size_t len, double min, double max) +void GraphView::setDefaultZoom() { - if (m_drawData != nullptr) - delete[] m_drawData; + double range = SignalController::RELATIVE_MAX - SignalController::RELATIVE_MIN; - m_drawData = data; - m_drawDataLen = len; - m_drawDataMax = max; - m_drawDataMin = min; + m_relXMax = SignalController::RELATIVE_MAX; + m_relXMin = SignalController::RELATIVE_MIN; + m_relYMax = SignalController::RELATIVE_MAX + (range * DEFAULT_Y_MARGIN); + m_relYMin = SignalController::RELATIVE_MIN - (range * DEFAULT_Y_MARGIN); +} - this->drawGraph(); - this->update(); +void GraphView::zoom(const int fromXPix, const int fromYPix, const int toXPix, const int toYPix) +{ + bool ret = draw(xPixToRel(fromXPix), yPixToRel(fromYPix), xPixToRel(toXPix), yPixToRel(toYPix)); + if (ret) + update(); + else + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, __QFUNC__ + " unable to draw pixmap"); } -GraphView::~GraphView() +/** Private slots **/ +void GraphView::onCtxMenuDeletePeak(const QPoint pixPos) { - if (m_backbuffer != nullptr) - delete m_backbuffer; - if (m_plainGraph != nullptr) - delete m_plainGraph; - if (m_drawData != nullptr) - delete[] m_drawData; + const PeakDrawData pdData = m_controller->deletePeak(xPixToRel(pixPos.x())); + QRegion reg = erasePeak(pdData); + refresh(reg); } + +void GraphView::onCtxMenuZoomOut() +{ + unzoom(); +} + +/* Public slots */ diff --git a/gui/graphview.h b/gui/graphview.h index 9511d82..179e481 100644 --- a/gui/graphview.h +++ b/gui/graphview.h @@ -23,13 +23,14 @@ #ifndef GRAPHVIEW_H #define GRAPHVIEW_H -#include -#include -#include "graphdrawer.h" +#include "gui/graphviewcontextmenu.h" +#include "signaldrawer.h" #include "mathhelpers.h" #include "metatypes.h" +#include +#include -class GraphView : public QWidget +class GraphView : public QWidget, protected SignalDrawer { Q_OBJECT public: @@ -39,66 +40,63 @@ public: INTEGRATE }; - explicit GraphView(QWidget* parent = nullptr); - ~GraphView(); + explicit GraphView(std::shared_ptr controller, QWidget* parent = nullptr); void leaveEvent(QEvent* ); void mouseDoubleClickEvent(QMouseEvent* ); void mouseMoveEvent(QMouseEvent* ev); void mousePressEvent(QMouseEvent* ev); void paintEvent(QPaintEvent* ev); + bool refresh(QRegion reg = QRegion()); private: - void drawCrosshair(const int x, const int y); - void drawGraph(); + void drawCrosshair(const int xPix, const int yPix); void drawIntegrationBaseline(const int x, const int y); void drawZoomRect(const int x, const int y); - QRegion eraseCrosshair(bool apply = false); - QRegion eraseIntegrationBaseline(bool apply = false); - QRegion eraseZoomRect(bool apply = false); - + QRegion eraseCrosshair(const bool apply = false); + QRegion eraseIntegrationBaseline(const bool apply = false); + QRegion eraseZoomRect(const bool apply = false); void resizeEvent(QResizeEvent* ev); + void unzoom(); + void updateValuesUnderCrosshair(const int xPix); + void setDefaultZoom(); + void showContextMenu(const QPoint& pos, const QPoint& globalPos); + void zoom(const int fromXPix, const int fromYPix, const int toXPix, const int toYPix); - std::unique_ptr m_graphDrawer; - bool m_ctxMenuOpen; - QPixmap* m_backbuffer; - QPixmap* m_plainGraph; + GraphViewContextMenu m_ctxMenu; MouseMode m_mouseMode; GraphControlModes m_graphCtrlMode; - double* m_drawData; - size_t m_drawDataLen; - double m_drawDataMax; - double m_drawDataMin; /* Crosshair */ - int m_graphCrosshairX; - int m_graphCrosshairY; + int m_graphCrosshairXPix; + int m_graphCrosshairYPix; /* Integration baseline */ - int m_integrateStartX; - int m_integrateStartY; - int m_integrateStopX; - int m_integrateStopY; + int m_integrateStartXPix; + int m_integrateStartYPix; + int m_integrateStopXPix; + int m_integrateStopYPix; /* Zooming rectangle */ - int m_zoomRectStartX; - int m_zoomRectStartY; - int m_zoomRectLastX; - int m_zoomRectLastY; + int m_zoomRectStartXPix; + int m_zoomRectStartYPix; + int m_zoomRectLastXPix; + int m_zoomRectLastYPix; + + static const double DEFAULT_Y_MARGIN; + static const QString ME_SENDER_STR; + +private slots: + void onCtxMenuDeletePeak(const QPoint pixPos); + void onCtxMenuZoomOut(); public slots: void onControlModeChanged(GraphControlModes mode) { m_graphCtrlMode = mode; } - void onCtxMenuClosed() { m_ctxMenuOpen = false; } - void onDrawIntegration(const int fromX, const double fromY, const int toX, const double toY, const int peakX, const double peakY, - const QString& time, const QString& area, const bool valley); - void onUpdateGraph(double* data, size_t len, double min, double max); signals: + void crosshairValuesUpdated(const double time, const double value); void crosshairErased(); void crosshairMoved(const int x, const int y); - void integrated(const int fromX, const int fromY, const int toX, const int toY); void mouseCursorLeft(); - void redrawNeeded(); void resized(const int x, const int y); - void showContextMenuReq(const int x, const int y, const QPoint& globalPos); void zoomed(const int fromX, const int fromY, const int toX, const int toY); }; diff --git a/gui/graphviewcontextmenu.cpp b/gui/graphviewcontextmenu.cpp index 84552ce..2a2a40d 100644 --- a/gui/graphviewcontextmenu.cpp +++ b/gui/graphviewcontextmenu.cpp @@ -28,4 +28,17 @@ GraphViewContextMenu::GraphViewContextMenu(QWidget* parent) : m_acZoomOut = addAction("Zoom out"); addSeparator(); m_acDeletePeak = addAction("Delete peak"); + + connect(m_acDeletePeak, SIGNAL(triggered()), this, SLOT(onDeletePeakTriggered())); + connect(m_acZoomOut, SIGNAL(triggered()), this, SLOT(onZoomOutTriggered())); +} + +void GraphViewContextMenu::onDeletePeakTriggered() +{ + emit deletePeak(m_relPixPos); +} + +void GraphViewContextMenu::onZoomOutTriggered() +{ + emit zoomOut(); } diff --git a/gui/graphviewcontextmenu.h b/gui/graphviewcontextmenu.h index 1896f1c..3615a0c 100644 --- a/gui/graphviewcontextmenu.h +++ b/gui/graphviewcontextmenu.h @@ -31,15 +31,23 @@ class GraphViewContextMenu : public QMenu Q_OBJECT public: explicit GraphViewContextMenu(QWidget* parent = nullptr); + void setRelPixPos(const QPoint& relPixPos) { m_relPixPos = relPixPos; } +private: QAction* m_acDeletePeak; QAction* m_acZoomOut; -private: + QPoint m_relPixPos; -signals: + static const QString ME_SENDER_STR; -public slots: +private slots: + void onDeletePeakTriggered(); + void onZoomOutTriggered(); + +signals: + void deletePeak(const QPoint pos); + void zoomOut(); }; diff --git a/gui/signalview.cpp b/gui/signalview.cpp index 21ae43f..35682e7 100644 --- a/gui/signalview.cpp +++ b/gui/signalview.cpp @@ -30,15 +30,19 @@ //#include -SignalView::SignalView(QWidget* parent) : +SignalView::SignalView(std::shared_ptr controller, QWidget* parent) : QWidget(parent), + m_controller(controller), ui(new Ui::SignalView) { ui->setupUi(this); - m_graphView = new GraphView(this); + m_graphView = new GraphView(controller, this); ui->qw_container->setLayout(new QVBoxLayout()); ui->qw_container->layout()->addWidget(m_graphView); + + connect(m_graphView, SIGNAL(crosshairValuesUpdated(double,double)), this, SLOT(onUpdateCrosshairValues(double,double))); + connect(m_graphView, SIGNAL(mouseCursorLeft()), this, SLOT(onRemoveCurrentValues())); } /* Public functions */ @@ -78,11 +82,11 @@ void SignalView::setYUnits(const QString &text) ui->ql_yUnits->setText(text); } -void SignalView::onUpdateCurrentValues(double x, double y) +void SignalView::onUpdateCrosshairValues(const double time, const double value) { QLocale l = QLocale::system(); - ui->qle_xValue->setText(l.toString(x)); - ui->qle_yValue->setText(l.toString(y)); + ui->qle_xValue->setText(l.toString(time)); + ui->qle_yValue->setText(l.toString(value)); } void SignalView::onRemoveCurrentValues() diff --git a/gui/signalview.h b/gui/signalview.h index 4989f1b..e7c2ad6 100644 --- a/gui/signalview.h +++ b/gui/signalview.h @@ -38,7 +38,7 @@ class SignalView : public QWidget { Q_OBJECT public: - explicit SignalView(QWidget* parent = nullptr); + explicit SignalView(std::shared_ptr controller, QWidget* parent = nullptr); ~SignalView(); void mouseDoubleClickEvent(QMouseEvent* ev); void setDataTableModel(SignalDataTableModel* model); @@ -50,10 +50,11 @@ public: GraphView* m_graphView; private: + std::shared_ptr m_controller; Ui::SignalView* ui; public slots: - void onUpdateCurrentValues(double x, double y); + void onUpdateCrosshairValues(const double time, const double value); void onRemoveCurrentValues(); private slots: diff --git a/imagedrawer.cpp b/imagedrawer.cpp new file mode 100644 index 0000000..0ba340c --- /dev/null +++ b/imagedrawer.cpp @@ -0,0 +1,40 @@ +#include "imagedrawer.h" + +#include + +ImageDrawer::ImageDrawer(const int width, const int height, QObject* parent) : + QObject(parent), + m_height(height), + m_width(width) +{ +} + +void ImageDrawer::onUpdateGraph(double* data, size_t len, double min, double max) +{ + if (data == nullptr) + return; + m_drawDataMin = min; + m_drawDataMax = max; + + //m_pixmap = m_graphDrawer->drawGraph(data, len, m_width, m_height, min, max); +} + +void ImageDrawer::onDrawIntegration(const int fromX, const double fromY, const int toX, const double toY, const int peakStartX, const double peakStartY, + const QString& time, const QString& area) +{ + double xStep = static_cast(m_width); + double yStep = static_cast(m_height) / (m_drawDataMax - m_drawDataMin); + int startX = floor(fromX * xStep + 0.5); + int stopX = floor(toX * xStep + 0.5); + int startY = floor((m_height - yStep * (fromY - m_drawDataMin)) + 0.5); + int stopY = floor((m_height - yStep * (toY - m_drawDataMin)) + 0.5); + int peakX = floor(peakStartX * xStep + 0.5); + int peakY = floor(m_height - yStep * (peakStartY - m_drawDataMin) + 0.5); + + if (m_pixmap == nullptr) + return; + + qDebug() << "drawing peak"; + + //m_graphDrawer->overlayIntegratedPeak(m_pixmap, startX, startY, stopX, stopY, peakX, peakY, time, area); +} diff --git a/imagedrawer.h b/imagedrawer.h new file mode 100644 index 0000000..c3c2973 --- /dev/null +++ b/imagedrawer.h @@ -0,0 +1,30 @@ +#ifndef IMAGEDRAWER_H +#define IMAGEDRAWER_H + +#include + +class ImageDrawer : public QObject +{ + Q_OBJECT +public: + explicit ImageDrawer(const int width, const int height, QObject* parent = nullptr); + const QPixmap* pixmap() const { return m_pixmap; } + +private: + QPixmap* m_pixmap; + int m_drawDataMin; + int m_drawDataMax; + + const int m_height; + const int m_width; + +signals: + +public slots: + void onDrawIntegration(const int fromX, const double fromY, const int toX, const double toY, const int peakStartX, const double peakStartY, + const QString& time, const QString& area); + void onUpdateGraph(double* data, size_t len, double min, double max); + +}; + +#endif // IMAGEDRAWER_H diff --git a/integrator.cpp b/integrator.cpp index 8a811d2..9c5daab 100644 --- a/integrator.cpp +++ b/integrator.cpp @@ -23,6 +23,8 @@ #include "integrator.h" #include "logger.h" +#include + const QString Integrator::ME_SENDER_STR("Integrator"); Integrator::Integrator(std::shared_ptr signal, QObject* parent) : @@ -30,17 +32,20 @@ Integrator::Integrator(std::shared_ptr signal, QObject* parent) : m_signal(signal) {} -Integrator::ReturnCode Integrator::deletePeak(size_t idx) +std::shared_ptr Integrator::deletePeak(size_t idx) { + std::shared_ptr peak; std::multimap>::iterator it = m_peaks.begin(); + while (it != m_peaks.end() && it->first <= idx) { if (it->first <= idx && it->second->toIdx() > idx) { /* Peak is surrounding the cursor */ + peak = it->second; m_peaks.erase(it); - return ReturnCode::SUCCESS; + return peak; } it++; } - return ReturnCode::E_NO_PEAK; + return nullptr; } Integrator::ReturnCode Integrator::integrate(size_t startIdx, size_t stopIdx, double startY, double stopY, std::shared_ptr& peak) @@ -63,14 +68,16 @@ Integrator::ReturnCode Integrator::integrate(size_t startIdx, size_t stopIdx, do return ReturnCode::E_INVAL; } + qDebug() << "INTEG" << startIdx << stopIdx << startY << stopY; + slope = (stopY - startY) / (stopIdx - startIdx); /* Try to find first intersection */ if (startY < m_signal->valueAt(startIdx)) { expectDownward = true; - //qDebug() << "Expecting downward peak"; + qDebug() << "Expecting downward peak"; } else { expectDownward = false; - //qDebug() << "Expecting upward peak"; + qDebug() << "Expecting upward peak"; } for (i = startIdx; i <= stopIdx; i++) { double value = m_signal->valueAt(i); diff --git a/integrator.h b/integrator.h index 15fd076..8b76108 100644 --- a/integrator.h +++ b/integrator.h @@ -42,7 +42,7 @@ public: }; explicit Integrator(std::shared_ptr signal, QObject* parent = nullptr); - ReturnCode deletePeak(size_t idx); + std::shared_ptr deletePeak(size_t idx); ReturnCode integrate(size_t startIdx, size_t stopIdx, double startY, double stopY, std::shared_ptr& peak); const std::shared_ptr peakByIdx(const int idx) const; std::multimap>::const_iterator peaksCBegin() const { return m_peaks.cbegin(); } diff --git a/logger.h b/logger.h index 2288688..c9e3bf0 100644 --- a/logger.h +++ b/logger.h @@ -30,6 +30,8 @@ #include #include +#define __QFUNC__ QString(__func__) + QString("()") + class LocalLogger; typedef std::shared_ptr LocalLoggerPtr; diff --git a/signal.h b/signal.h index f9bb4b9..4d64e40 100644 --- a/signal.h +++ b/signal.h @@ -55,6 +55,7 @@ public: explicit Signal(const Equipment equipment, const Resource resource, const double samplingRate, const YUnit yunit, const uint16_t wavelengthAbs, const uint16_t wavelengthRef, const std::vector& values, QObject* parent = nullptr); + uint count() const { return m_values.size(); } std::string descriptiveName() const; Equipment equipment() const { return m_equipment; } std::vector::const_iterator iteratorFrom(const size_t idx) const; @@ -67,9 +68,9 @@ public: double samplingRate() const { return m_samplingRate; } double valueAt(const size_t idx) const; const std::vector& values() const { return m_values; } - std::vector::const_iterator valuesBegin() const { return m_values.begin(); } + std::vector::const_iterator pairsBegin() const { return m_values.cbegin(); } size_t valuesCount() const { return m_values.size(); } - std::vector::const_iterator valuesEnd() const { return m_values.end(); } + std::vector::const_iterator pairsEnd() const { return m_values.cend(); } uint16_t wavelengthAbsorbed() const { return m_wavelengthAbs; } uint16_t wavelengthReference() const { return m_wavelengthRef; } XUnit xunit() const { return m_xunit; } diff --git a/signalcontroller.cpp b/signalcontroller.cpp index 2714860..520bc8c 100644 --- a/signalcontroller.cpp +++ b/signalcontroller.cpp @@ -26,6 +26,8 @@ #include +const double SignalController::RELATIVE_MAX = 100.0; +const double SignalController::RELATIVE_MIN = 0.0; const QString SignalController::ME_SENDER_STR("SignalController"); SignalController::SignalController(std::shared_ptr signal, QObject* parent) : @@ -35,283 +37,189 @@ SignalController::SignalController(std::shared_ptr signal, QObject* pare m_integrator = std::shared_ptr(new Integrator(signal)); m_integTblModel = new IntegrationTableModel(m_integrator); m_dtblModel = new SignalDataTableModel(signal); - - m_yMin = m_signal->minimum() - m_signal->minimum()*0.05; - m_yMax = m_signal->maximum() + m_signal->maximum()*0.05; - m_fromIdx = 0; m_toIdx = m_signal->valuesCount()-1; - - connect(m_ctxMenu.m_acZoomOut, SIGNAL(triggered()), this, SLOT(onCtxMenuUnzoom())); - connect(m_ctxMenu.m_acDeletePeak, SIGNAL(triggered()), this, SLOT(onCtxMenuDeletePeak())); -} - -double SignalController::currentStartX() const -{ - return m_signal->pairAt(m_fromIdx).first; } -double SignalController::currentStartY() const -{ - return m_yMin; -} - -double SignalController::currentStopX() const -{ - return m_signal->pairAt(m_toIdx).first; -} +/** Public methods **/ -double SignalController::currentStopY() const +PeakDrawData SignalController::deletePeak(const double x) { - return m_yMax; -} + size_t idx = relToIdx(x); + std::shared_ptr peak; -/* Public methods */ -void SignalController::draw() -{ - drawGraph(); - drawAllPeaks(); -} + if (idx > m_signal->count()-1) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, __QFUNC__ + " idx value " + QString::number(idx) + " out of bounds!"); + return PeakDrawData(); + } -/* Private methods */ + m_integTblModel->prepReset(); + peak = m_integrator->deletePeak(idx); -void SignalController::drawAllPeaks() -{ - std::multimap>::const_iterator cit = m_integrator->peaksCBegin(); - while (cit != m_integrator->peaksCEnd()) - drawIntegratedPeak((cit++)->second); + if (peak == nullptr) { + Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, __QFUNC__ + " no peak to delete (idx " + QString::number(idx) + ")"); + m_integTblModel->doneReset(); + return PeakDrawData(); + } + m_integTblModel->doneReset(); + return genPeakDrawData(peak); } -void SignalController::drawFullGraph() +std::shared_ptr SignalController::getGraphDrawData(const double fromX, const double toX) { - m_yMin = m_signal->minimum() - m_signal->minimum()*0.05; - m_yMax = m_signal->maximum() + m_signal->maximum()*0.05; - if (m_signal->valuesCount() < 1) - return; - m_fromIdx = 0; m_toIdx = m_signal->valuesCount()-1; + size_t fromIdx, toIdx; + double* ddata; + size_t ddataLen; - qDebug() << m_yMin << m_yMax; + if (toX <= fromX) { + Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " fromX (" + QString::number(fromX) + ") lower than toX (" + QString::number(toX) + ")"); + return std::shared_ptr(new GraphDrawData()); + } - drawGraph(); -} + fromIdx = relToIdx(fromX); + toIdx = relToIdx(toX); -void SignalController::drawGraph() -{ - size_t len = m_toIdx - m_fromIdx; - double* arr = generateDrawData(m_fromIdx, m_toIdx); - if (arr != nullptr) - emit viewDrawGraph(arr, len, m_yMin, m_yMax); -} - -void SignalController::drawIntegratedPeak(const std::shared_ptr peak) -{ - int fromX, toX; - double fromY, toY; - bool valley; - QLocale l; - if (peak->toIdx() < m_fromIdx || peak->fromIdx() > m_toIdx) /* Peak is not in view X-wise, skip it */ - return; - - /* Check if the start of the baseline is in view */ - if (peak->fromIdx()-1 >= m_fromIdx) { - fromX = peak->fromIdx() - m_fromIdx; - fromY = peak->fromY(); - } else { - fromX = 0; - double bls = peak->baselineSlope(); - fromY = bls * (m_fromIdx - peak->fromIdx()) + peak->fromY(); - qDebug() << "Start OOR" << fromY << bls; + if (toIdx >= m_signal->count()) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, __QFUNC__ + " toIdx (" + QString::number(toIdx) + ") out of bounds, max (" + + QString::number(m_signal->count()-1) + ") !"); + return std::shared_ptr(new GraphDrawData()); } - /* Check if the end of the baseline is in view */ - if (peak->toIdx() <= m_toIdx) { - toX = peak->toIdx() - m_fromIdx; - toY = peak->toY(); - } else { - double bls = peak->baselineSlope(); - toX = m_toIdx - m_fromIdx; - toY = bls * (m_toIdx - peak->fromIdx()) + peak->fromY(); - qDebug() << "Stop OOR" << toY << bls; + + ddataLen = toIdx - fromIdx + 1; + ddata = new double[ddataLen]; + for (size_t idx = fromIdx; idx <= toIdx; idx++) { + double current = valueToRel(m_signal->valueAt(idx)); + ddata[idx - fromIdx] = current; } - /*qDebug("Drawing integration"); - qDebug() << "SX" << fromX << "SY" << fromY << "EX" << toX << "EY" << toY; - qDebug() << "Slope P" << peak->baselineSlope() << "Slope M" << (toY-fromY)/(toX-fromX); - qDebug("---");*/ - - l = QLocale::system(); - const QString peakTime = l.toString(peak->peakTime(), 'f', 4); - const QString peakArea = QString("A: ") + l.toString(peak->auc(), 'f', 4); - if (peak->type() == IntegratedPeak::Type::PEAK) - valley = false; - else - valley = true; - emit viewDrawIntegration(fromX, fromY, toX, toY, peak->peakIdx() - m_fromIdx, m_signal->valueAt(peak->peakIdx()), peakTime, peakArea, valley); + return std::shared_ptr(new GraphDrawData(ddata, ddataLen)); } -double* SignalController::generateDrawData(size_t from, size_t to) +std::vector SignalController::getPeaksDrawData(const double fromX, const double fromY, const double toX, const double toY) { - double* arr; - size_t len; + std::vector peaks; + size_t fromIdx, toIdx; + std::multimap>::const_iterator cit = m_integrator->peaksCBegin(); - if (from > to) { - Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "From index lower than to index"); - return nullptr; + if (toX <= fromX) { + Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " toX is lower than fromX"); + return peaks; } - if (from == to) - return nullptr; - if (to >= m_signal->valuesCount() || from >= m_signal->valuesCount()) { - Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Invalid value index"); - return nullptr; + if (toY <= fromY) { + Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " toY is lower than fromY"); + return peaks; } - len = to - from; - - arr = new double[len]; - for (uint idx = 0; idx < len; idx++) - arr[idx] = m_signal->valueAt(idx + from); - - return arr; -} -bool SignalController::updateConstraints(const int fromX, const int fromY, const int toX, const int toY) -{ - size_t xRange = m_toIdx - m_fromIdx; - double yRange; - if (m_viewWidth < 1) { - Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Invalid viewWidth value " + QString::number(m_viewWidth)); - return false; - } - if (m_viewHeight < 1) { - Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Invalid viewHeight value " + QString::number(m_viewHeight)); - return false; - } - if (fromX > toX) { - Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "fromX > toX"); - return false; - } - if (fromY > toY) { - Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "fromY > toY"); - return false; + fromIdx = relToIdx(fromX); + toIdx = relToIdx(toX); + if (toIdx > m_signal->count()) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, __QFUNC__ + " toIdx (" + QString::number(toIdx) + ") out of bounds, max (" + + QString::number(m_signal->count()-1) + ") !"); + return peaks; } - m_toIdx = (xRange * toX) / m_viewWidth + m_fromIdx; - m_fromIdx = (xRange * fromX) / m_viewWidth + m_fromIdx; + while (cit != m_integrator->peaksCEnd()) { + std::shared_ptr peak = cit->second; - yRange = m_yMax - m_yMin; - m_yMin = (yRange * toY / -m_viewHeight) + m_yMax; - m_yMax = (yRange * fromY / -m_viewHeight) + m_yMax; + if (peak->toIdx() < fromIdx || peak->fromIdx() > toIdx) /* Peak is not in view X-wise, skip it */ + continue; - //qDebug() << fromX << fromY << toX << toY; - //qDebug() << m_fromIdx << m_toIdx << m_yMin << m_yMax; - - return true; -} - -double SignalController::yValToCoord(double y) -{ - return m_viewHeight / (m_yMax - m_yMin) * (y - m_yMin); -} - -/* Public slots */ - -void SignalController::onCtxMenuUnzoom() -{ - drawFullGraph(); - drawAllPeaks(); -} - -void SignalController::onCtxMenuDeletePeak() -{ - size_t xRange = m_toIdx - m_fromIdx; - size_t idx = (xRange * m_ctxMenuX) / m_viewWidth + m_fromIdx; - - m_integTblModel->prepReset(); - Integrator::ReturnCode ret = m_integrator->deletePeak(idx); - - switch (ret) { - case Integrator::ReturnCode::SUCCESS: - onViewRedrawNeeded(); - break; - case Integrator::ReturnCode::E_NO_PEAK: - Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "No peak to delete."); - break; - default: - break; + peaks.push_back(genPeakDrawData(peak)); + cit++; } - m_integTblModel->doneReset(); + return peaks; } -void SignalController::onViewCrosshairErased() +Signal::TimeValuePair SignalController::getXYValues(const double x) { - emit viewRemoveCurrentValues(); -} - -void SignalController::onViewCrosshairMoved(const int x, const int y) -{ - Q_UNUSED(y); + size_t idx = relToIdx(x); + if (idx >= m_signal->count()) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, __QFUNC__ + " idx value " + QString::number(idx) + " out of bounds!"); + return Signal::TimeValuePair(0, 0); + } - size_t xRange = m_toIdx - m_fromIdx; - int valueIdx = (xRange * x) / m_viewWidth + m_fromIdx; - Signal::TimeValuePair tvp = m_signal->pairAt(valueIdx); - emit viewUpdateCurrentValues(tvp.first, tvp.second); + return m_signal->pairAt(idx); } -void SignalController::onViewIntegrated(const int fromX, const int fromY, const int toX, const int toY) +PeakDrawData SignalController::integratePeak(const double fromX, const double fromY, const double toX, const double toY) { - size_t xRange = m_toIdx - m_fromIdx; - size_t startIdx = (xRange * fromX) / m_viewWidth + m_fromIdx; - size_t stopIdx = (xRange * toX) / m_viewWidth + m_fromIdx; - double yRange = m_yMax - m_yMin; - double blStartY = (yRange * (m_viewHeight - fromY) / m_viewHeight) + m_yMin; - double blStopY = (yRange * (m_viewHeight - toY) / m_viewHeight) + m_yMin; + size_t fromIdx, toIdx; + double fromVal, toVal; Integrator::ReturnCode ret; std::shared_ptr peak; + fromIdx = relToIdx(fromX); + toIdx = relToIdx(toX); + if (fromIdx >= m_signal->count()) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, __QFUNC__ + " fromIdx (" + QString::number(fromIdx) + ") out of bounds!"); + return PeakDrawData(); + } + if (toIdx >= m_signal->count()) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, __QFUNC__ + " toIdx (" + QString::number(toIdx) + ") out of bounds!"); + return PeakDrawData(); + } + fromVal = relToValue(fromY); + toVal = relToValue(toY); + m_integTblModel->prepReset(); - ret = m_integrator->integrate(startIdx, stopIdx, blStartY, blStopY, peak); + ret = m_integrator->integrate(fromIdx, toIdx, fromVal, toVal, peak); switch (ret) { case Integrator::ReturnCode::SUCCESS: - drawIntegratedPeak(peak); + m_integTblModel->doneReset(); + return genPeakDrawData(peak); break; case Integrator::ReturnCode::E_NO_FIRST_INTERSECT: - Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "no first intersect"); + Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, __QFUNC__ + "no first intersection"); break; case Integrator::ReturnCode::E_NO_SECOND_INTERSECT: - Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "no first intersect"); + Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, __QFUNC__ + "no second intersection"); break; default: break; } - m_integTblModel->doneReset(); + + return PeakDrawData(); +} + +/** Private methods **/ + +PeakDrawData SignalController::genPeakDrawData(const std::shared_ptr peak) +{ + return PeakDrawData(idxToRel(peak->fromIdx()), valueToRel(peak->fromY()), + idxToRel(peak->toIdx()), valueToRel(peak->toY()), + idxToRel(peak->peakIdx()), valueToRel(m_signal->valueAt(peak->peakIdx())), + peak->peakTime(), peak->auc()); } -void SignalController::onViewRedrawNeeded() +double SignalController::idxToRel(const size_t idx) { - drawGraph(); - drawAllPeaks(); + return idx * (RELATIVE_MAX - RELATIVE_MIN) / m_signal->count(); } -void SignalController::onViewResized(const int w, const int h) +double SignalController::valueToRel(const double value) { - qDebug() << "Setting GW size" << w << h; - m_viewHeight = h; - m_viewWidth = w; + const double y = m_signal->maximum() - m_signal->minimum(); + const double ret = (value - m_signal->minimum()) * (RELATIVE_MAX - RELATIVE_MIN) / y; + //qDebug() << __QFUNC__ << value << ret; + return ret; } -void SignalController::onViewShowContextMenu(const int x, const int y, const QPoint& globalPos) +size_t SignalController::relToIdx(const double rel) { - m_ctxMenuX = x; m_ctxMenuY = y; - m_ctxMenu.exec(globalPos); - emit viewCtxMenuClosed(); + size_t ret = (m_signal->count() - 1) * (rel - RELATIVE_MIN) / (RELATIVE_MAX - RELATIVE_MIN); + //qDebug() << __QFUNC__ << rel << ret << m_signal->count(); + return ret; } -void SignalController::onViewZoomed(const int fromX, const int fromY, const int toX, const int toY) +double SignalController::relToValue(const double rel) { - if (!updateConstraints(fromX, fromY, toX, toY)) - return; - drawGraph(); - drawAllPeaks(); + const double y = m_signal->maximum() - m_signal->minimum(); + const double val = (rel - RELATIVE_MIN) * y / (RELATIVE_MAX - RELATIVE_MIN) + m_signal->minimum(); + return val; } +/** Public slots **/ + SignalController::~SignalController() { delete m_dtblModel; diff --git a/signalcontroller.h b/signalcontroller.h index 1fae1a2..3e53a3d 100644 --- a/signalcontroller.h +++ b/signalcontroller.h @@ -23,7 +23,6 @@ #ifndef SIGNALCONTROLLER_H #define SIGNALCONTROLLER_H -#include "gui/graphviewcontextmenu.h" #include "integrationtablemodel.h" #include "integrator.h" #include "signal.h" @@ -31,51 +30,95 @@ #include #include +struct GraphDrawData { + GraphDrawData() : + ddata(nullptr), ddataLen(0) {} + GraphDrawData(const double* ddata, const size_t ddataLen) : + ddata(ddata), ddataLen(ddataLen) {} + ~GraphDrawData() + { + if (ddata) + delete[] ddata; + } + + const double* ddata; + const size_t ddataLen; +}; + +struct PeakDrawData { + PeakDrawData() : fromX(0), fromY(0), toX(0), toY(0), peakX(0), peakY(0), auc(0), time(0) {} + PeakDrawData(const double fromX, const double fromY, const double toX, const double toY, + const double peakX, const double peakY, const double auc, const double time) : + fromX(fromX), fromY(fromY), toX(toX), toY(toY), peakX(peakX), peakY(peakY), auc(auc), time(time) {} + + const double fromX, fromY, toX, toY; + const double peakX, peakY; + const double auc, time; +}; + class SignalController : public QObject { Q_OBJECT public: explicit SignalController(std::shared_ptr signal, QObject* parent = nullptr); ~SignalController(); - double currentYMax() const { return m_yMax; } + /*double currentYMax() const { return m_yMax; } double currentYMin() const { return m_yMin; } double currentStartX() const; double currentStartY() const; double currentStopX() const; double currentStopY() const; - void draw(); + void draw();*/ SignalDataTableModel* dataTableModel() { return m_dtblModel; } - double* generateDrawData(size_t from, size_t to); + PeakDrawData deletePeak(const double x); + //double* generateDrawData(size_t from, size_t to); + Signal::TimeValuePair getXYValues(const double x); + std::shared_ptr getGraphDrawData(const double fromX, const double toX); + std::vector getPeaksDrawData(const double fromX, const double fromY, const double toX, const double toY); + double fromXAbs() const { return m_signal->timeAt(0); } + double toXAbs() const { return m_signal->timeAt(m_signal->count()-1); } + double fromYAbs() const { return m_signal->valueAt(0); } + double toYAbs() const { return m_signal->valueAt(m_signal->count()-1); } + PeakDrawData integratePeak(const double fromX, const double fromY, const double toX, const double toY); IntegrationTableModel* integrationTableModel() { return m_integTblModel;} const std::shared_ptr signal() const { return m_signal; } const std::shared_ptr cIntegrator() const { return m_integrator; } + static const double RELATIVE_MAX; + static const double RELATIVE_MIN; + private: - GraphViewContextMenu m_ctxMenu; - int m_ctxMenuX; - int m_ctxMenuY; + //GraphViewContextMenu m_ctxMenu; + //int m_ctxMenuX; + //int m_ctxMenuY; SignalDataTableModel* m_dtblModel; - uint m_fromIdx; - uint m_toIdx; + //uint m_fromIdx; + //uint m_toIdx; IntegrationTableModel* m_integTblModel; std::shared_ptr m_integrator; std::shared_ptr m_signal; - int m_viewHeight; - int m_viewWidth; - double m_yMax; - double m_yMin; - - void drawAllPeaks(); - void drawFullGraph(); - void drawGraph(); + //int m_viewHeight; + //int m_viewWidth; + //double m_yMax; + //double m_yMin; + + double idxToRel(const size_t idx); + double valueToRel(const double value); + size_t relToIdx(const double rel); + double relToValue(const double rel); + + //void drawAllPeaks(); + //void drawFullGraph(); + //void drawGraph(); void drawIntegratedPeak(const std::shared_ptr peak); - bool updateConstraints(const int fromX, const int fromY, const int toX, const int toY); - double yValToCoord(double y); + PeakDrawData genPeakDrawData(const std::shared_ptr peak); + //bool updateConstraints(const int fromX, const int fromY, const int toX, const int to); + //double yValToCoord(double y); static const QString ME_SENDER_STR; public slots: - void onCtxMenuUnzoom(); + /*void onCtxMenuUnzoom(); void onCtxMenuDeletePeak(); void onViewCrosshairErased(); void onViewCrosshairMoved(const int x, const int y); @@ -83,18 +126,16 @@ public slots: void onViewRedrawNeeded(); void onViewResized(const int w, const int h); void onViewShowContextMenu(const int x, const int y, const QPoint& globalPos); - void onViewZoomed(const int fromX, const int fromY, const int toX, const int toY); + bool onViewZoomed(const int fromX, const int fromY, const int toX, const int toY); + bool onViewZoomedByValues(const double fromX, const double fromY, const double toX, const double toY);*/ signals: - void viewCtxMenuClosed(); - void viewDrawGraph(double* data, size_t len, double min, double max); - void viewDrawIntegration(const int fromX, const double fromY, const int toX, const double toY, const int peakX, const double peakY, const QString& peakTime, - const QString& peakArea, const bool valley); - void viewUpdateCurrentValues(double x, double y); - void viewRemoveCurrentValues(); + //void viewCtxMenuClosed(); + //void viewDrawGraph(double* data, size_t len, double min, double max); + //void viewDrawIntegration(int startX, double startY, int stopX, double stopY, int peakStartX, double peakStartY, const QString& time, const QString& area); + //void viewUpdateCurrentValues(double x, double y); + //void viewRemoveCurrentValues(); void fillDataList(); - - }; #endif // SIGNALCONTROLLER_H diff --git a/signaldrawer.cpp b/signaldrawer.cpp new file mode 100644 index 0000000..b246a4d --- /dev/null +++ b/signaldrawer.cpp @@ -0,0 +1,297 @@ +#include "logger.h" +#include "signaldrawer.h" + +#include + +const QString SignalDrawer::ME_SENDER_STR("SignalDrawer"); + +SignalDrawer::SignalDrawer(std::shared_ptr controller) : + m_controller(controller), + m_background(nullptr), + m_pixmap(nullptr) +{ +} + +/** Public methods **/ + +Constraints SignalDrawer::currentConstraints() +{ + return Constraints(m_relXMin, m_relYMin, m_relXMax, m_relYMax); +} + +bool SignalDrawer::draw(const double fromX, const double fromY, const double toX, const double toY) +{ + bool ret; + + setNewRelativeConstraints(fromX, fromY, toX, toY); + + ret = drawGraph(fromX, fromY, toX, toY); + if (!ret) { + Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " cannot draw graph"); + restoreRelativeConstraints(); + return ret; + } + ret = drawPeaks(fromX, fromY, toX, toY); + if (!ret) { + Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " cannot draw integrated peaks"); + restoreRelativeConstraints(); + return ret; + } + + return ret; +} + +bool SignalDrawer::setDimensions(const int width, const int height) +{ + if (width < 0 || height < 0) { + Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " invalid dimensions (" + QString::number(width) + ", " + QString::number(height) + ")"); + return false; + } + + m_width = width; + m_height = height; + + return draw(); +} + +/** Protected methods **/ + +bool SignalDrawer::drawGraph(const double fromX, const double fromY, const double toX, const double toY) +{ + std::shared_ptr gdData = m_controller->getGraphDrawData(fromX, toX); + if (gdData->ddataLen == 0) + return false; + + m_gdData = gdData; + + return renderGraph(fromY, toY); +} + +bool SignalDrawer::drawPeaks(const double fromX, const double fromY, const double toX, const double toY) +{ + std::vector pdData = m_controller->getPeaksDrawData(fromX, fromY, toX, toY); + if (pdData.size() < 1) + return true; + + for (const PeakDrawData& pd : pdData) { + QRegion reg = renderPeak(pd); + if (reg.isEmpty()) return false; + } + + return true; +} + +QRegion SignalDrawer::erasePeak(const PeakDrawData& pd) +{ + QPainter p; + QFont font("arial", 10); + QFontMetrics fm(font); + QString aucText, timeText; + int tTextHeight, tTextWidth, aTextWidth; + int fromXPix, fromYPix; + int toXPix, toYPix; + int peakX, peakY; + int beginXPix, endXPix; + + if (m_background == nullptr) { + Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " null pointer to pixmap"); + return QRegion(); + } + /* No peak */ + if (pd.auc == 0) + return QRegion(); + + fromXPix = relToXPix(pd.fromX); fromYPix = relToYPix(pd.fromY); + toXPix = relToXPix(pd.toX); toYPix = relToYPix(pd.toY); + peakX = relToXPix(pd.peakX); peakY = relToYPix(pd.peakY); + aucText = m_locale.toString(pd.auc, 'f', 4); + timeText = m_locale.toString(pd.time, 'f', 4); + tTextWidth = fm.width(timeText); + if (peakY - tTextWidth < 2) + peakY += tTextWidth - peakY + 2; + tTextHeight = fm.height(); + aTextWidth = fm.width(aucText); + + if (peakX - tTextHeight < fromXPix) + beginXPix = (peakX - tTextHeight > 0) ? peakX - tTextHeight : 0; + else + beginXPix = fromXPix; + qDebug() << __QFUNC__ << beginXPix << fromXPix << tTextHeight; + if (peakX + 5 + aTextWidth > toXPix) + endXPix = peakX + 5 + aTextWidth; + else + endXPix = toXPix; + + return QRegion(beginXPix, 0, endXPix - beginXPix, m_height); +} + +bool SignalDrawer::renderGraph(const double fromY, const double toY) +{ + QPixmap* pixmap; + QPainter p; + double xStep, yStep; + int fromXPix = 0, fromYPix; + + if (m_width < 1 || m_height < 1) { + Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " invalid dimensions (" + QString::number(m_width) + ", " + QString::number(m_height) + ")"); + return false; + } + if (m_gdData->ddataLen < 1) { + Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " no data to draw"); + return false; + } + + pixmap = new QPixmap(m_width, m_height); + p.begin(pixmap); + p.fillRect(0, 0, m_width, m_height, Qt::white); + p.setPen(QColor(Qt::blue)); + + xStep = static_cast(m_width) / m_gdData->ddataLen; + yStep = static_cast(m_height) / (toY - fromY); + Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, __QFUNC__ + " yStep: " + QString::number(yStep) + + " fY " + QString::number(fromY) + " tY " + QString::number(toY)); + fromYPix = m_height - yStep * (m_gdData->ddata[0] - fromY); + for (size_t i = 1; i < m_gdData->ddataLen; i++) { + int toXPix = xStep * i; + int toYPix = m_height - yStep * (m_gdData->ddata[i] - fromY); + p.drawLine(fromXPix, fromYPix, toXPix, toYPix); + fromXPix = toXPix; + fromYPix = toYPix; + } + + p.end(); + + if (m_pixmap) + delete m_pixmap; + m_pixmap = pixmap; + if (m_background) + delete m_background; + m_background = new QPixmap(*m_pixmap); + + return true; +} + +QRegion SignalDrawer::renderPeak(const PeakDrawData& pd) +{ + QPainter p; + QFont font("arial", 10); + QFontMetrics fm(font); + QString aucText, timeText; + int tTextHeight, tTextWidth, aTextWidth; + int fromXPix, fromYPix; + int toXPix, toYPix; + int peakX, peakY; + int beginXPix, endXPix; + + if (m_background == nullptr) { + Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " null pointer to pixmap"); + return QRegion(); + } + /* No peak */ + if (pd.auc == 0) + return QRegion(); + + fromXPix = relToXPix(pd.fromX); fromYPix = relToYPix(pd.fromY); + toXPix = relToXPix(pd.toX); toYPix = relToYPix(pd.toY); + peakX = relToXPix(pd.peakX); peakY = relToYPix(pd.peakY); + aucText = m_locale.toString(pd.auc, 'f', 4); + timeText = m_locale.toString(pd.time, 'f', 4); + + p.begin(m_background); + p.setPen(Qt::red); + p.drawLine(fromXPix, fromYPix, toXPix, toYPix); + + /* Draw AREA and TIME caption */ + p.setFont(font); + p.setPen(Qt::black); + tTextWidth = fm.width(timeText); + if (peakY - tTextWidth < 2) + peakY += tTextWidth - peakY + 2; + tTextHeight = fm.height(); + p.save(); + p.translate(peakX, peakY); + p.rotate(-90); + p.drawText(0, 0, timeText); + p.rotate(+90); + p.restore(); + aTextWidth = fm.width(aucText); + p.drawText(peakX + 5, peakY, aucText); + p.end(); + + if (peakX - tTextHeight < fromXPix) + beginXPix = (peakX - tTextHeight > 0) ? peakX - tTextHeight : 0; + else + beginXPix = fromXPix; + qDebug() << __QFUNC__ << beginXPix << fromXPix << tTextHeight; + if (peakX + 5 + aTextWidth > toXPix) + endXPix = peakX + 5 + aTextWidth; + else + endXPix = toXPix; + + /* Copy peak to foreground pixmap */ + p.begin(m_pixmap); + p.drawPixmap(beginXPix, 0, *m_background, beginXPix, 0, endXPix - beginXPix, m_height); + p.end(); + + return QRegion(beginXPix, 0, endXPix - beginXPix, m_height); +} + +/** Private functions **/ + +int SignalDrawer::relToXPix(const double rel) +{ + const int ret = m_width / (m_relXMax - m_relXMin) * (rel - m_relXMin); + //qDebug() << __QFUNC__ << ret; + return ret; +} + +int SignalDrawer::relToYPix(const double rel) +{ + const int ret = m_height - (m_height / (m_relYMax - m_relYMin) * (rel - m_relYMin)); + //qDebug() << __QFUNC__ << ret; + return ret; +} + +double SignalDrawer::xPixToRel(const int pix) +{ + const double ret = (pix * (m_relXMax - m_relXMin) / m_width) + m_relXMin; + //qDebug() << __QFUNC__ << ret; + return ret; +} + +double SignalDrawer::yPixToRel(const int pix) +{ + const double range = m_relYMax - m_relYMin; + const int invPix = m_height - pix; + const double ret = (range / m_height * invPix) + m_relYMin; + //qDebug() << __QFUNC__ << ret; + return ret; +} + +/** Private methods **/ + +void SignalDrawer::restoreRelativeConstraints() +{ + m_relXMin = m_oldRelXMin; + m_relXMax = m_oldRelXMax; + m_relYMin = m_oldRelYMin; + m_relYMax = m_oldRelYMax; +} + +void SignalDrawer::setNewRelativeConstraints(const double fromX, const double fromY, const double toX, const double toY) +{ + m_oldRelXMin = m_relXMin; + m_oldRelXMax = m_relXMax; + m_oldRelYMin = m_relYMin; + m_oldRelYMax = m_relYMax; + + m_relXMin = fromX; + m_relXMax = toX; + m_relYMin = fromY; + m_relYMax = toY; +} + +SignalDrawer::~SignalDrawer() +{ + delete m_pixmap; +} diff --git a/signaldrawer.h b/signaldrawer.h new file mode 100644 index 0000000..95e97ad --- /dev/null +++ b/signaldrawer.h @@ -0,0 +1,68 @@ +#ifndef SIGNALDRAWER_H +#define SIGNALDRAWER_H + +#include "signalcontroller.h" +#include +#include +#include +#include +#include + +struct Constraints { + Constraints(const double fromX, const double fromY, const double toX, const double toY) : + fromX(fromX), fromY(fromY), toX(toX), toY(toY) {} + + const double fromX, fromY, toX, toY; +}; + +class SignalDrawer +{ +public: + SignalDrawer(std::shared_ptr controller); + ~SignalDrawer(); + Constraints currentConstraints(); + int dheight() const { return m_height; } + bool draw(const double fromX = SignalController::RELATIVE_MIN, const double fromY = SignalController::RELATIVE_MIN, + const double toX = SignalController::RELATIVE_MAX, const double toY = SignalController::RELATIVE_MAX); + bool drawGraph(const double fromX, const double fromY, const double toX, const double toY); + bool drawPeaks(const double fromX, const double fromY, const double toX, const double toY); + QRegion erasePeak(const PeakDrawData& pd); + int dwidth() const { return m_width; } + bool renderGraph(const double fromY, const double toY); + QRegion renderPeak(const PeakDrawData& pd); + bool setDimensions(const int width, const int height); + +protected: + std::shared_ptr m_controller; + QLocale m_locale; + QPixmap* m_background; + QPixmap* m_pixmap; + + double m_relXMax; + double m_relXMin; + double m_relYMax; + double m_relYMin; + std::shared_ptr m_gdData; + + int relToXPix(const double rel); + int relToYPix(const double rel); + double xPixToRel(const int pix); + double yPixToRel(const int pix); + +private: + int m_height; + int m_width; + + double m_oldRelXMax; + double m_oldRelXMin; + double m_oldRelYMax; + double m_oldRelYMin; + + void setNewRelativeConstraints(const double fromX, const double fromY, const double toX, const double toY); + void restoreRelativeConstraints(); + + static const QString ME_SENDER_STR; + +}; + +#endif // SIGNALDRAWER_H diff --git a/singlerunsselectormodel.cpp b/singlerunsselectormodel.cpp index 8624726..5e11268 100644 --- a/singlerunsselectormodel.cpp +++ b/singlerunsselectormodel.cpp @@ -53,8 +53,9 @@ int SingleRunsSelectorModel::idxFromKey(const std::string& key) const else idx++; } - if (idx == m_singleRunKeys.size()) - return -1; + /* if (idx == m_singleRunKeys.size()) + * Unknown key */ + return -1; } int SingleRunsSelectorModel::rowCount(const QModelIndex& parent) const -- 2.43.5