From: Michal MalĂ˝ Date: Sun, 16 Feb 2014 15:29:27 +0000 (+0100) Subject: Fix peak rendering. X-Git-Tag: 0.2a~6 X-Git-Url: https://gitweb.devoid-pointer.net/?a=commitdiff_plain;h=98f36ef5bb1eb8a8fff254274089fec76191993a;p=anyanka.git Fix peak rendering. --- 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