From c22d576984c1232c4c8ede2d0abf3728b8314512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Sun, 22 Nov 2020 14:38:52 +0100 Subject: [PATCH] Qwt dependency removed, drag&drop for traces added --- .github/workflows/Build.yml | 23 +- Software/PC_Application/Application.pro | 7 +- .../CustomWidgets/qwtplotpiecewisecurve.cpp | 29 - Software/PC_Application/Traces/traceplot.cpp | 102 ++- Software/PC_Application/Traces/traceplot.h | 11 + .../PC_Application/Traces/tracesmithchart.cpp | 102 +-- .../PC_Application/Traces/tracesmithchart.h | 7 +- .../PC_Application/Traces/tracewidget.cpp | 1 - .../PC_Application/Traces/tracexyplot.cpp | 726 +++++++----------- Software/PC_Application/Traces/tracexyplot.h | 59 +- .../PC_Application/qwtplotpiecewisecurve.h | 61 -- Software/PC_Application/unit.cpp | 23 +- Software/PC_Application/unit.h | 1 + 13 files changed, 415 insertions(+), 737 deletions(-) delete mode 100644 Software/PC_Application/CustomWidgets/qwtplotpiecewisecurve.cpp delete mode 100644 Software/PC_Application/qwtplotpiecewisecurve.h diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml index e6c2ea5..d4d9847 100644 --- a/.github/workflows/Build.yml +++ b/.github/workflows/Build.yml @@ -16,7 +16,7 @@ jobs: - name: Install dependencies run: | - sudo apt-get install -y libusb-1.0-0-dev libqwt-qt5-dev qt5-default qt5-qmake qtbase5-dev + sudo apt-get install -y libusb-1.0-0-dev qt5-default qt5-qmake qtbase5-dev - name: Build application run: | @@ -42,36 +42,18 @@ jobs: with: path: ${{ runner.workspace }}/Qt key: ${{ runner.os }}-QtCache - - - name: Cache Qwt - id: cache-qwt - uses: pat-s/always-upload-cache@v2.1.0 - with: - path: C:\Qwt-6.1.4 - key: ${{ runner.os }}-QwtCache - name: Install Qt uses: jurplel/install-qt-action@v2 with: arch: 'win64_mingw73' cached: ${{ steps.cache-qt.outputs.cache-hit }} - - - name: Download and Install Qwt - if: steps.cache-qwt.outputs.cache-hit != 'true' - run: | - curl -o qwt.zip -L https://sourceforge.net/projects/qwt/files/qwt/6.1.4/qwt-6.1.4.zip/download - 7z x qwt.zip -r -oQwt - cd Qwt\qwt-6.1.4 - qmake qwt.pro - make install - shell: cmd - name: Download libusb run: | curl -o libusb.7z -L https://github.com/libusb/libusb/releases/download/v1.0.23/libusb-1.0.23.7z 7z x libusb.7z -r -olibusb Xcopy /E /I /Y libusb\include ..\Qt\5.12.9\mingw73_64\include - Xcopy /E /I /Y libusb\MinGW64\static C:\Qwt-6.1.4\lib dir dir ..\Qt\5.12.9 dir ..\Qt\5.12.9\mingw73_64 @@ -91,7 +73,6 @@ jobs: copy ..\..\..\..\Qt\5.12.9\mingw73_64\bin\libgcc_s_seh-1.dll . copy "..\..\..\..\Qt\5.12.9\mingw73_64\bin\libstdc++-6.dll" . copy ..\..\..\..\Qt\5.12.9\mingw73_64\bin\Qt5OpenGL.dll . - copy C:\Qwt-6.1.4\lib\qwt.dll . shell: cmd - name: Upload @@ -128,4 +109,4 @@ jobs: path: | VNA_embedded.elf combined.vnafw - \ No newline at end of file + diff --git a/Software/PC_Application/Application.pro b/Software/PC_Application/Application.pro index 7b5c8e9..dddd718 100644 --- a/Software/PC_Application/Application.pro +++ b/Software/PC_Application/Application.pro @@ -46,7 +46,6 @@ HEADERS += \ averaging.h \ mode.h \ preferences.h \ - qwtplotpiecewisecurve.h \ touchstone.h \ unit.h @@ -62,7 +61,6 @@ SOURCES += \ Calibration/sourcecaldialog.cpp \ CustomWidgets/colorpickerbutton.cpp \ CustomWidgets/informationbox.cpp \ - CustomWidgets/qwtplotpiecewisecurve.cpp \ CustomWidgets/siunitedit.cpp \ CustomWidgets/tilewidget.cpp \ CustomWidgets/toggleswitch.cpp \ @@ -101,10 +99,7 @@ SOURCES += \ unit.cpp LIBS += -lusb-1.0 -unix:INCLUDEPATH += /usr/include/qwt -unix:LIBS += -L/usr/lib/ -lqwt-qt5 -win32:INCLUDEPATH += C:\Qwt-6.1.4\include -win32:LIBS += -LC:\Qwt-6.1.4\lib -lqwt +unix:LIBS += -L/usr/lib/ QT += widgets diff --git a/Software/PC_Application/CustomWidgets/qwtplotpiecewisecurve.cpp b/Software/PC_Application/CustomWidgets/qwtplotpiecewisecurve.cpp deleted file mode 100644 index 83293b6..0000000 --- a/Software/PC_Application/CustomWidgets/qwtplotpiecewisecurve.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** - * Qwt Widget Library - * Copyright (C) 1997 Josef Wilgen - * Copyright (C) 2002 Uwe Rathmann - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the Qwt License, Version 1.0 - *****************************************************************************/ - -#include "qwtplotpiecewisecurve.h" - -void QwtPlotPiecewiseCurve::drawCurve(QPainter *p, int style, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &canvasRect, int from, int to) const -{ - if (to < 0) - to = dataSize() - 1; - - int first, last = from; - while (last <= to) - { - first = last; - while (first <= to && (isNaN(sample(first).y()))) - ++first; - last = first; - while (last <= to && !isNaN(sample(last).y())) - ++last; - if (first <= to) - QwtPlotCurve::drawCurve(p, style, xMap, yMap, canvasRect, first, last - 1); - } -} diff --git a/Software/PC_Application/Traces/traceplot.cpp b/Software/PC_Application/Traces/traceplot.cpp index ea65352..b7abc71 100644 --- a/Software/PC_Application/Traces/traceplot.cpp +++ b/Software/PC_Application/Traces/traceplot.cpp @@ -7,9 +7,14 @@ std::set TracePlot::plots; +using namespace std; + TracePlot::TracePlot(TraceModel &model, QWidget *parent) : QWidget(parent), - model(model) + model(model), + selectedMarker(nullptr), + dropPending(false), + dropTrace(nullptr) { contextmenu = new QMenu(); markedForDeletion = false; @@ -119,6 +124,75 @@ void TracePlot::paintEvent(QPaintEvent *event) draw(p); } + +void TracePlot::mousePressEvent(QMouseEvent *event) +{ + auto clickPoint = event->pos() - QPoint(marginLeft, marginTop); + // check if click was near a marker + unsigned int closestDistance = numeric_limits::max(); + TraceMarker *closestMarker = nullptr; + for(auto t : traces) { + if(!t.second) { + // this trace is disabled, skip + continue; + } + auto markers = t.first->getMarkers(); + for(auto m : markers) { + if(!m->isMovable()) { + continue; + } + Trace::Data d; + d.S = m->getData(); + d.frequency = m->getFrequency(); + auto markerPoint = dataToPixel(d); + if(markerPoint.isNull()) { + // invalid, skip + continue; + } + auto diff = markerPoint - clickPoint; + unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y(); + if(distance < closestDistance) { + closestDistance = distance; + closestMarker = m; + } + } + } + if(closestDistance <= 400) { + selectedMarker = closestMarker; + } else { + selectedMarker = nullptr; + } +} + +void TracePlot::mouseMoveEvent(QMouseEvent *event) +{ + if(selectedMarker) { + auto t = selectedMarker->trace(); + auto clickPoint = event->pos() - QPoint(marginLeft, marginTop); + auto samples = t->size(); + if(!samples) { + return; + } + double closestDistance = numeric_limits::max(); + unsigned int closestIndex = 0; + for(unsigned int i=0;isample(i); + auto plotPoint = dataToPixel(data); + if (plotPoint.isNull()) { + // destination point outside of currently displayed range + continue; + } + auto diff = plotPoint - clickPoint; + unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y(); + if(distance < closestDistance) { + closestDistance = distance; + closestIndex = i; + } + } + selectedMarker->setFrequency(t->sample(closestIndex).frequency); + } +} + void TracePlot::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasFormat("trace/pointer")) { @@ -129,24 +203,28 @@ void TracePlot::dragEnterEvent(QDragEnterEvent *event) auto trace = (Trace*) dropPtr; if(supported(trace)) { event->acceptProposedAction(); + dropPending = true; + dropTrace = trace; } } + triggerReplot(); } void TracePlot::dropEvent(QDropEvent *event) { - if (event->mimeData()->hasFormat("trace/pointer")) { - auto data = event->mimeData()->data("trace/pointer"); - QDataStream stream(&data, QIODevice::ReadOnly); - quintptr dropPtr; - stream >> dropPtr; - auto trace = (Trace*) dropPtr; - qDebug() << "Dropped" << trace << ", encoded as" << stream; - - if(supported(trace)) { - enableTrace(trace, true); - } + if(dropTrace) { + traceDropped(dropTrace, event->pos() - - QPoint(marginLeft, marginTop)); } + dropPending = false; + dropTrace = nullptr; +} + +void TracePlot::dragLeaveEvent(QDragLeaveEvent *event) +{ + Q_UNUSED(event) + dropPending = false; + dropTrace = nullptr; + triggerReplot(); } std::set TracePlot::getPlots() diff --git a/Software/PC_Application/Traces/traceplot.h b/Software/PC_Application/Traces/traceplot.h index 7186d8f..508a7d5 100644 --- a/Software/PC_Application/Traces/traceplot.h +++ b/Software/PC_Application/Traces/traceplot.h @@ -40,9 +40,15 @@ protected: bool markedForDeletion; static std::set plots; + virtual QPoint dataToPixel(Trace::Data d) = 0; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + // handle trace drops void dragEnterEvent(QDragEnterEvent *event) override; void dropEvent(QDropEvent *event) override; + void dragLeaveEvent(QDragLeaveEvent *event) override; + virtual void traceDropped(Trace *t, QPoint position){ Q_UNUSED(t) Q_UNUSED(position)}; protected slots: void newTraceAvailable(Trace *t); @@ -57,6 +63,11 @@ protected: static constexpr unsigned int marginRight = 0; double sweep_fmin, sweep_fmax; TraceModel &model; + TraceMarker *selectedMarker; + + bool dropPending; + QPoint dropPosition; + Trace *dropTrace; }; diff --git a/Software/PC_Application/Traces/tracesmithchart.cpp b/Software/PC_Application/Traces/tracesmithchart.cpp index ce8452e..dc8d915 100644 --- a/Software/PC_Application/Traces/tracesmithchart.cpp +++ b/Software/PC_Application/Traces/tracesmithchart.cpp @@ -1,4 +1,4 @@ -#include "tracesmithchart.h" +#include "tracesmithchart.h" #include #include #include @@ -38,83 +38,20 @@ void TraceSmithChart::axisSetupDialog() dialog->show(); } -QPoint TraceSmithChart::plotToPixel(std::complex S) +QPoint TraceSmithChart::dataToPixel(Trace::Data d) { - return transform.map(QPoint(S.real() * smithCoordMax, -S.imag() * smithCoordMax)); -} - -std::complex TraceSmithChart::pixelToPlot(const QPoint &pos) -{ - QPointF inv = transform.inverted().map(pos); - return complex(inv.x()/smithCoordMax, -inv.y()/smithCoordMax); -} - -void TraceSmithChart::mousePressEvent(QMouseEvent *event) -{ - auto clickPoint = event->pos(); - unsigned int closestDistance = numeric_limits::max(); - TraceMarker *closestMarker = nullptr; - for(auto t : traces) { - auto markers = t.first->getMarkers(); - for(auto m : markers) { - if(!m->isMovable()) { - continue; - } - if (limitToSpan && (m->getFrequency() < sweep_fmin || m->getFrequency() > sweep_fmax)) { - // marker outside of currently displayed range - continue; - } - auto S = m->getData(); - auto markerPoint = plotToPixel(S); - auto yDiff = abs(markerPoint.y() - clickPoint.y()); - auto xDiff = abs(markerPoint.x() - clickPoint.x()); - unsigned int distance = xDiff * xDiff + yDiff * yDiff; - if(distance < closestDistance) { - closestDistance = distance; - closestMarker = m; - } - } - } - if(closestDistance <= 400) { - selectedMarker = closestMarker; - } else { - selectedMarker = nullptr; - } -} - -void TraceSmithChart::mouseMoveEvent(QMouseEvent *event) -{ - if(selectedMarker) { - auto t = selectedMarker->trace(); - auto mouseS = pixelToPlot(event->pos()); - auto samples = t->size(); - if(!samples) { - return; - } - double closestDistance = numeric_limits::max(); - unsigned int closestIndex = 0; - for(unsigned int i=0;isample(i); - if (limitToSpan && (data.frequency < sweep_fmin || data.frequency > sweep_fmax)) { - // destination point outside of currently displayed range - continue; - } - auto distance = norm(data.S - mouseS); - if(distance < closestDistance) { - closestDistance = distance; - closestIndex = i; - } - } - selectedMarker->setFrequency(t->sample(closestIndex).frequency); + if(d.frequency < sweep_fmin || d.frequency > sweep_fmax) { + return QPoint(); } + return transform.map(QPoint(d.S.real() * smithCoordMax, -d.S.imag() * smithCoordMax)); } void TraceSmithChart::draw(QPainter &p) { - p.setBrush(palette().windowText()); auto pref = Preferences::getInstance(); // translate coordinate system so that the smith chart sits in the origin has a size of 1 auto w = p.window(); + p.save(); p.translate(w.width()/2, w.height()/2); auto scale = qMin(w.height(), w.width()) / (2.0 * smithCoordMax); p.scale(scale, scale); @@ -160,7 +97,7 @@ void TraceSmithChart::draw(QPainter &p) { // trace marked invisible continue; } - pen = QPen(trace->color(), 1.5); + pen = QPen(trace->color(), 1); pen.setCosmetic(true); p.setPen(pen); int nPoints = trace->size(); @@ -194,6 +131,31 @@ void TraceSmithChart::draw(QPainter &p) { } } } + if(dropPending) { + p.setOpacity(0.5); + p.setBrush(Qt::white); + p.setPen(Qt::white); + p.drawEllipse(-smithCoordMax, -smithCoordMax, 2*smithCoordMax, 2*smithCoordMax); + p.restore(); + auto font = p.font(); + font.setPixelSize(20); + p.setFont(font); + p.setOpacity(1.0); + p.setPen(Qt::white); + auto text = "Drop here to add\n" + dropTrace->name() + "\nto Smith chart"; + p.drawText(p.window(), Qt::AlignCenter, text); + } else { + p.restore(); + } +} + +void TraceSmithChart::traceDropped(Trace *t, QPoint position) +{ + Q_UNUSED(t) + Q_UNUSED(position); + if(supported(t)) { + enableTrace(t, true); + } } //void TraceSmithChart::paintEvent(QPaintEvent * /* the event */) diff --git a/Software/PC_Application/Traces/tracesmithchart.h b/Software/PC_Application/Traces/tracesmithchart.h index 2e565e8..d800362 100644 --- a/Software/PC_Application/Traces/tracesmithchart.h +++ b/Software/PC_Application/Traces/tracesmithchart.h @@ -18,15 +18,13 @@ protected: static constexpr double screenUsage = 0.9; static constexpr double smithCoordMax = 4096; - QPoint plotToPixel(std::complex S); - std::complex pixelToPlot(const QPoint &pos); + QPoint dataToPixel(Trace::Data d) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; //void paintEvent(QPaintEvent *event) override; virtual void updateContextMenu() override; bool supported(Trace *t) override; virtual void draw(QPainter& painter) override; + virtual void traceDropped(Trace *t, QPoint position) override; QPen textPen; QPen chartLinesPen; QPen thinPen; @@ -40,7 +38,6 @@ protected: QPainterPath thickArcsPath; QTransform transform; - TraceMarker *selectedMarker; }; #endif // TRACESMITHCHART_H diff --git a/Software/PC_Application/Traces/tracewidget.cpp b/Software/PC_Application/Traces/tracewidget.cpp index 210406e..75c9673 100644 --- a/Software/PC_Application/Traces/tracewidget.cpp +++ b/Software/PC_Application/Traces/tracewidget.cpp @@ -89,7 +89,6 @@ bool TraceWidget::eventFilter(QObject *, QEvent *event) QByteArray encodedPointer; QDataStream stream(&encodedPointer, QIODevice::WriteOnly); stream << quintptr(dragTrace); - qDebug() << "Dragging" << dragTrace << ", encoded as" << stream; mimeData->setData("trace/pointer", encodedPointer); drag->setMimeData(mimeData); diff --git a/Software/PC_Application/Traces/tracexyplot.cpp b/Software/PC_Application/Traces/tracexyplot.cpp index 22a2b3b..0f8893e 100644 --- a/Software/PC_Application/Traces/tracexyplot.cpp +++ b/Software/PC_Application/Traces/tracexyplot.cpp @@ -1,26 +1,18 @@ #include "tracexyplot.h" #include -//#include "qwtplotpiecewisecurve.h" -//#include "qwt_series_data.h" #include "trace.h" #include #include -//#include -//#include -//#include #include "tracemarker.h" -//#include -//#include #include "xyplotaxisdialog.h" #include #include #include "Util/util.h" #include "unit.h" +#include using namespace std; -//set TraceXYPlot::allPlots; - const set TraceXYPlot::YAxisTypes = {TraceXYPlot::YAxisType::Disabled, TraceXYPlot::YAxisType::Magnitude, TraceXYPlot::YAxisType::Phase, @@ -29,97 +21,8 @@ const set TraceXYPlot::YAxisTypes = {TraceXYPlot::YAxisT TraceXYPlot::YAxisType::Step, TraceXYPlot::YAxisType::Impedance}; -static double FrequencyAxisTransformation(TraceXYPlot::YAxisType type, complex data) { - switch(type) { - case TraceXYPlot::YAxisType::Magnitude: return 20*log10(abs(data)); break; - case TraceXYPlot::YAxisType::Phase: return arg(data) * 180.0 / M_PI; break; - case TraceXYPlot::YAxisType::VSWR: - if(abs(data) < 1.0) { - return (1+abs(data)) / (1-abs(data)); - } - break; - default: break; - } - return numeric_limits::quiet_NaN(); -} -static double TimeAxisTransformation(TraceXYPlot::YAxisType type, Trace *t, int index) { - auto timeData = t->getTDR()[index]; - switch(type) { - case TraceXYPlot::YAxisType::Impulse: return timeData.impulseResponse; break; - case TraceXYPlot::YAxisType::Step: return timeData.stepResponse; break; - case TraceXYPlot::YAxisType::Impedance: - if(abs(timeData.stepResponse) < 1.0) { - return 50 * (1+timeData.stepResponse) / (1-timeData.stepResponse); - } - break; - default: break; - } - return numeric_limits::quiet_NaN(); -} - -//class QwtTraceSeries : public QwtSeriesData { -//public: -// QwtTraceSeries(Trace &t, TraceXYPlot::YAxisType Ytype, const TraceXYPlot *plot) -// : QwtSeriesData(), -// Ytype(Ytype), -// plot(plot), -// t(t){} -// size_t size() const override { -// switch(Ytype) { -// case TraceXYPlot::YAxisType::Magnitude: -// case TraceXYPlot::YAxisType::Phase: -// case TraceXYPlot::YAxisType::VSWR: -// return t.size(); -// case TraceXYPlot::YAxisType::Impulse: -// case TraceXYPlot::YAxisType::Step: -// case TraceXYPlot::YAxisType::Impedance: -// return t.getTDR().size(); -// default: -// return 0; -// } -// } -// QPointF sample(size_t i) const override { -// switch(Ytype) { -// case TraceXYPlot::YAxisType::Magnitude: -// case TraceXYPlot::YAxisType::Phase: -// case TraceXYPlot::YAxisType::VSWR: { -// Trace::Data d = t.sample(i); -// QPointF p; -// p.setX(d.frequency); -// p.setY(FrequencyAxisTransformation(Ytype, d.S)); -// return p; -// } -// case TraceXYPlot::YAxisType::Impulse: -// case TraceXYPlot::YAxisType::Step: -// case TraceXYPlot::YAxisType::Impedance: { -// auto sample = t.getTDR()[i]; -// QPointF p; -// if(plot->XAxis.type == TraceXYPlot::XAxisType::Time) { -// p.setX(sample.time); -// } else { -// p.setX(sample.distance); -// } -// p.setY(TimeAxisTransformation(Ytype, &t, i)); -// return p; -// } -// default: -// return QPointF(); -// } - -// } -// QRectF boundingRect() const override { -// return qwtBoundingRect(*this); -// } - -//private: -// TraceXYPlot::YAxisType Ytype; -// const TraceXYPlot *plot; -// Trace &t; -//}; - TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent) - : TracePlot(model, parent), - selectedMarker(nullptr) + : TracePlot(model, parent) { YAxis[0].log = false; YAxis[0].type = YAxisType::Disabled; @@ -140,57 +43,13 @@ TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent) XAxis.rangeMin = 0; XAxis.mode = XAxisMode::UseSpan; - - -// plot = new QwtPlot(this); - -// auto canvas = new QwtPlotCanvas(plot); -// canvas->setFrameStyle(QFrame::Plain); -// plot->setCanvas(canvas); -// plot->setAutoFillBackground(true); -// grid = new QwtPlotGrid(); -// grid->attach(plot); -// setColorFromPreferences(); - -// auto selectPicker = new XYplotPicker(plot->xBottom, plot->yLeft, QwtPicker::NoRubberBand, QwtPicker::ActiveOnly, plot->canvas()); -// selectPicker->setStateMachine(new QwtPickerClickPointMachine); - -// drawPicker = new XYplotPicker(plot->xBottom, plot->yLeft, QwtPicker::NoRubberBand, QwtPicker::ActiveOnly, plot->canvas()); -// drawPicker->setStateMachine(new QwtPickerDragPointMachine); -// drawPicker->setTrackerPen(QPen(Qt::white)); - -// // Marker selection -// connect(selectPicker, SIGNAL(selected(QPointF)), this, SLOT(clicked(QPointF)));; -// // Marker movement -// connect(drawPicker, SIGNAL(moved(QPointF)), this, SLOT(moved(QPointF))); - -// auto layout = new QGridLayout; -// layout->addWidget(plot); -// layout->setContentsMargins(0, 0, 0, 0); -// setLayout(layout); -// plot->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - initializeTraceInfo(); -// setAutoFillBackground(true); - // Setup default axis setYAxis(0, YAxisType::Magnitude, false, false, -120, 20, 10); setYAxis(1, YAxisType::Phase, false, false, -180, 180, 30); // enable autoscaling and set for full span (no information about actual span available yet) updateSpan(0, 6000000000); setXAxis(XAxisType::Frequency, XAxisMode::UseSpan, 0, 6000000000, 600000000); - -// allPlots.insert(this); -} - -TraceXYPlot::~TraceXYPlot() -{ -// for(int axis = 0;axis < 2;axis++) { -// for(auto pd : curves[axis]) { -// delete pd.second.curve; -// } -// } -// delete drawPicker; -// allPlots.erase(this); + initializeTraceInfo(); } void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType type, bool log, bool autorange, double min, double max, double div) @@ -215,20 +74,7 @@ void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType type, bool log, bool } } YAxis[axis].type = type; - for(auto t : tracesAxis[axis]) { - // supported but needs an adjusted QwtSeriesData -// auto td = curves[axis][t]; -// td.data = createQwtSeriesData(*t, axis); - // call to setSamples deletes old QwtSeriesData -// td.curve->setSamples(td.data); - if(axis == 0) { - // update marker data -// auto marker = t->getMarkers(); -// for(auto m : marker) { -// markerDataChanged(m); -// } - } if(isTDRtype(type)) { t->addTDRinterest(); } @@ -240,14 +86,6 @@ void TraceXYPlot::setYAxis(int axis, TraceXYPlot::YAxisType type, bool log, bool YAxis[axis].rangeMax = max; YAxis[axis].rangeDiv = div; updateAxisTicks(); - // enable/disable y axis -// auto qwtaxis = axis == 0 ? QwtPlot::yLeft : QwtPlot::yRight; -// plot->enableAxis(qwtaxis, type != YAxisType::Disabled); -// if(autorange) { -// plot->setAxisAutoScale(qwtaxis, true); -// } else { -// plot->setAxisScale(qwtaxis, min, max, div); -// } updateContextMenu(); replot(); } @@ -277,12 +115,13 @@ void TraceXYPlot::updateSpan(double min, double max) updateAxisTicks(); } -//void TraceXYPlot::updateGraphColors() -//{ -// for(auto p : allPlots) { -// p->setColorFromPreferences(); -// } -//} +void TraceXYPlot::replot() +{ + if(XAxis.mode != XAxisMode::Manual || YAxis[0].autorange || YAxis[1].autorange) { + updateAxisTicks(); + } + TracePlot::replot(); +} bool TraceXYPlot::isTDRtype(TraceXYPlot::YAxisType type) { @@ -345,12 +184,16 @@ void TraceXYPlot::updateContextMenu() bool TraceXYPlot::supported(Trace *) { // potentially possible to add every kind of trace (depends on axis) - return true; + if(YAxis[0].type != YAxisType::Disabled || YAxis[1].type != YAxisType::Disabled) { + return true; + } else { + // no axis + return false; + } } void TraceXYPlot::draw(QPainter &p) { - p.setBrush(palette().windowText()); auto pref = Preferences::getInstance(); constexpr int yAxisSpace = 50; @@ -360,9 +203,9 @@ void TraceXYPlot::draw(QPainter &p) auto pen = QPen(pref.General.graphColors.axis, 0); pen.setCosmetic(true); p.setPen(pen); - int plotAreaLeft = YAxis[0].type == YAxisType::Disabled ? yAxisDisabledSpace : yAxisSpace; - int plotAreaWidth = w.width(); - int plotAreaBottom = w.height() - xAxisSpace; + plotAreaLeft = YAxis[0].type == YAxisType::Disabled ? yAxisDisabledSpace : yAxisSpace; + plotAreaWidth = w.width(); + plotAreaBottom = w.height() - xAxisSpace; if(YAxis[0].type != YAxisType::Disabled) { plotAreaWidth -= yAxisSpace; } else { @@ -373,7 +216,9 @@ void TraceXYPlot::draw(QPainter &p) } else { plotAreaWidth -= yAxisDisabledSpace; } - p.drawRect(plotAreaLeft, 0, plotAreaWidth, w.height()-xAxisSpace); + + auto plotRect = QRect(plotAreaLeft, 0, plotAreaWidth + 1, plotAreaBottom); + p.drawRect(plotRect); // draw axis types QString labelX; @@ -393,9 +238,11 @@ void TraceXYPlot::draw(QPainter &p) int significantDigits = floor(log10(max)) - floor(log10(step)) + 1; bool displayFullFreq = significantDigits <= 5; constexpr int displayLastDigits = 4; + QString prefixes = "fpnum kMG"; + QString commonPrefix = QString(); if(!displayFullFreq) { - auto fullFreq = Unit::ToString(XAxis.ticks.front(), "", " kMG", significantDigits); - + auto fullFreq = Unit::ToString(XAxis.ticks.front(), "", prefixes, significantDigits); + commonPrefix = fullFreq.at(fullFreq.size() - 1); auto front = fullFreq; front.truncate(fullFreq.size() - displayLastDigits); auto back = fullFreq; @@ -410,11 +257,17 @@ void TraceXYPlot::draw(QPainter &p) for(auto t : XAxis.ticks) { auto xCoord = Util::Scale(t, XAxis.ticks.front(), XAxis.ticks.back(), plotAreaLeft, plotAreaLeft + plotAreaWidth); - auto tickValue = Unit::ToString(t, "", "fpnum kMG", significantDigits); + auto tickValue = Unit::ToString(t, "", prefixes, significantDigits); p.setPen(QPen(pref.General.graphColors.axis, 1)); if(displayFullFreq) { p.drawText(QRect(xCoord - 40, plotAreaBottom + 5, 80, AxisLabelSize), Qt::AlignHCenter, tickValue); } else { + // check if the same prefix was used as in the fullFreq string + if(tickValue.at(tickValue.size() - 1) != commonPrefix) { + // prefix changed, we reached the next order of magnitude. Force same prefix as in fullFreq and add extra digit + tickValue = Unit::ToString(t, "", commonPrefix, significantDigits + 1); + } + tickValue.remove(0, tickValue.size() - displayLastDigits); QRect bounding; p.drawText(QRect(xCoord - 40, plotAreaBottom + 5, 80, AxisLabelSize), Qt::AlignHCenter, tickValue, &bounding); @@ -482,67 +335,138 @@ void TraceXYPlot::draw(QPainter &p) } } - auto toPlotCoordinates = [=](double x, double y, const class YAxis& ax) -> QPointF { - QPointF p; - p.setX(Util::Scale(x, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth)); - p.setY(Util::Scale(y, ax.rangeMin, ax.rangeMax, plotAreaBottom, 0)); + auto toPlotCoordinates = [=](const QPointF& point, const class YAxis& ax) -> QPoint { + QPoint p; + p.setX(Util::Scale(point.x(), XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth)); + p.setY(Util::Scale(point.y(), ax.rangeMin, ax.rangeMax, plotAreaBottom, 0)); return p; }; // plot traces - p.setClipRect(QRect(plotAreaLeft + 1, 1, plotAreaWidth - 2, plotAreaBottom - 1)); - switch(XAxis.type) { - case XAxisType::Frequency: - for(auto t : tracesAxis[i]) { - if(!t->isVisible()) { + p.setClipRect(QRect(plotRect.x()+1, plotRect.y()+1, plotRect.width()-2, plotRect.height()-2)); + for(auto t : tracesAxis[i]) { + if(!t->isVisible()) { + continue; + } + pen = QPen(t->color(), 1); + pen.setCosmetic(true); + if(i == 1) { + pen.setStyle(Qt::DotLine); + } else { + pen.setStyle(Qt::SolidLine); + } + p.setPen(pen); + auto nPoints = numTraceSamples(t); + for(unsigned int j=1;jcolor(), 1.5); - pen.setCosmetic(true); - if(i == 1) { - pen.setStyle(Qt::DotLine); - } else { - pen.setStyle(Qt::SolidLine); + + // scale to plot coordinates + auto p1 = toPlotCoordinates(last, YAxis[i]); + auto p2 = toPlotCoordinates(now, YAxis[i]); + if(!plotRect.contains(p1) && !plotRect.contains(p2)) { + // completely out of frame + continue; } - p.setPen(pen); - int nPoints = t->size(); - for(int j=1;jsample(j-1); - auto now = t->sample(j); - - auto yLast = transformFrequencyY(last.S, YAxis[i].type); - auto yNow = transformFrequencyY(now.S, YAxis[i].type); - - if(isnan(yLast) || isnan(yNow) || isinf(yLast) || isinf(yNow)) { + // draw line + p.drawLine(p1, p2); + } + if(i == 0 && nPoints > 0) { + // only draw markers on primary YAxis and if the trace has at least one point + auto markers = t->getMarkers(); + for(auto m : markers) { + if ((m->getFrequency() < XAxis.rangeMin || m->getFrequency() > XAxis.rangeMax)) { continue; } - - // scale to plot coordinates - auto p1 = toPlotCoordinates(last.frequency, yLast, YAxis[i]); - auto p2 = toPlotCoordinates(now.frequency, yNow, YAxis[i]); - // draw line - p.drawLine(p1, p2); + QPointF markerPoint = QPointF(m->getFrequency(), transformY(m->getData(), YAxis[i].type)); + auto point = toPlotCoordinates(markerPoint, YAxis[i]); + if(!plotRect.contains(point)) { + // out of screen + continue; + } + auto symbol = m->getSymbol(); + point += QPoint(-symbol.width()/2, -symbol.height()); + p.drawPixmap(point, symbol); } } - break; - case XAxisType::Time: - case XAxisType::Distance: - // TODO - break; } p.setClipping(false); } + + if(dropPending) { + p.setOpacity(0.5); + p.setBrush(Qt::white); + p.setPen(Qt::white); + if(YAxis[0].type == YAxisType::Disabled || YAxis[1].type == YAxisType::Disabled) { + // only one axis enabled, show drop area over whole plot + p.drawRect(plotRect); + auto font = p.font(); + font.setPixelSize(20); + p.setFont(font); + p.setOpacity(1.0); + p.setPen(Qt::white); + auto text = "Drop here to add\n" + dropTrace->name() + "\nto XY-plot"; + p.drawText(plotRect, Qt::AlignCenter, text); + } else { + // both axis enabled show regions + auto leftRect = plotRect; + leftRect.setWidth(plotRect.width() * 0.3); + auto centerRect = plotRect; + centerRect.setX(centerRect.x() + plotRect.width() * 0.35); + centerRect.setWidth(plotRect.width() * 0.3); + auto rightRect = plotRect; + rightRect.setX(rightRect.x() + plotRect.width() * 0.7); + rightRect.setWidth(plotRect.width() * 0.3); + p.drawRect(leftRect); + p.drawRect(centerRect); + p.drawRect(rightRect); + p.setOpacity(1.0); + p.setPen(Qt::white); + auto font = p.font(); + font.setPixelSize(20); + p.setFont(font); + p.drawText(leftRect, Qt::AlignCenter, "Drop here to add\nto primary axis"); + p.drawText(centerRect, Qt::AlignCenter, "Drop here to add\nto boths axes"); + p.drawText(rightRect, Qt::AlignCenter, "Drop here to add\nto secondary axis"); + } + } } void TraceXYPlot::updateAxisTicks() { auto createEvenlySpacedTicks = [](vector& ticks, double start, double stop, double step) { ticks.clear(); - for(double tick = start;tick <= stop;tick+= step) { + for(double tick = start; tick - stop < numeric_limits::epsilon() ;tick+= step) { ticks.push_back(tick); } }; + auto createAutomaticTicks = [](vector& ticks, double start, double stop, int minDivisions) -> double { + ticks.clear(); + double max_div_step = (stop - start) / minDivisions; + int zeros = floor(log10(max_div_step)); + double decimals_shift = pow(10, zeros); + max_div_step /= decimals_shift; + if(max_div_step >= 5) { + max_div_step = 5; + } else if(max_div_step >= 2) { + max_div_step = 2; + } else { + max_div_step = 1; + } + auto div_step = max_div_step * decimals_shift; + // round min up to next multiple of div_step + auto start_div = ceil(start / div_step) * div_step; + for(double tick = start_div;tick <= stop;tick += div_step) { + ticks.push_back(tick); + } + return div_step; + }; + if(XAxis.mode == XAxisMode::Manual) { createEvenlySpacedTicks(XAxis.ticks, XAxis.rangeMin, XAxis.rangeMax, XAxis.rangeDiv); } else { @@ -591,30 +515,35 @@ void TraceXYPlot::updateAxisTicks() } XAxis.rangeMin = min; XAxis.rangeMax = max; - constexpr int minDivisions = 8; - double max_div_step = (max - min) / minDivisions; - int zeros = floor(log10(max_div_step)); - double decimals_shift = pow(10, zeros); - max_div_step /= decimals_shift; - if(max_div_step >= 5) { - max_div_step = 5; - } else if(max_div_step >= 2) { - max_div_step = 2; - } else { - max_div_step = 1; - } - auto div_step = max_div_step * decimals_shift; - XAxis.rangeDiv = div_step; - // round min up to next multiple of div_step - auto start_div = ceil(min / div_step) * div_step; - for(double tick = start_div;tick <= max;tick += div_step) { - XAxis.ticks.push_back(tick); - } + XAxis.rangeDiv = createAutomaticTicks(XAxis.ticks, min, max, 8); } for(int i=0;i<2;i++) { if(!YAxis[i].autorange) { createEvenlySpacedTicks(YAxis[i].ticks, YAxis[i].rangeMin, YAxis[i].rangeMax, YAxis[i].rangeDiv); + } else { + // automatic mode, figure out limits + double max = std::numeric_limits::lowest(); + double min = std::numeric_limits::max(); + for(auto t : tracesAxis[i]) { + unsigned int samples = numTraceSamples(t); + for(unsigned int j=0;j max) { + max = point.y(); + } else if(point.y() < min) { + min = point.y(); + } + } + } + // add 5% overrange + auto range = max - min; + min -= range * 0.05; + max += range * 0.05; + YAxis[i].rangeMin = min; + YAxis[i].rangeMax = max; + YAxis[i].rangeDiv = createAutomaticTicks(YAxis[i].ticks, min, max, 8); } } triggerReplot(); @@ -634,54 +563,21 @@ QString TraceXYPlot::AxisTypeToName(TraceXYPlot::YAxisType type) void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled) { if(axis == 0) { - traces[t] = enabled; + TracePlot::enableTrace(t, enabled); } bool alreadyEnabled = tracesAxis[axis].find(t) != tracesAxis[axis].end(); if(alreadyEnabled != enabled) { if(enabled) { tracesAxis[axis].insert(t); -// CurveData cd; -// cd.data = createQwtSeriesData(*t, axis); -// cd.curve = new QwtPlotPiecewiseCurve(); -// cd.curve->attach(plot); -// cd.curve->setYAxis(axis == 0 ? QwtPlot::yLeft : QwtPlot::yRight); -// cd.curve->setSamples(cd.data); -// curves[axis][t] = cd; // connect signals - connect(t, &Trace::dataChanged, this, &TraceXYPlot::triggerReplot); -// connect(t, &Trace::colorChanged, this, &TraceXYPlot::traceColorChanged); -// connect(t, &Trace::visibilityChanged, this, &TraceXYPlot::traceColorChanged); - connect(t, &Trace::visibilityChanged, this, &TraceXYPlot::triggerReplot); - if(axis == 0) { - connect(t, &Trace::markerAdded, this, &TraceXYPlot::markerAdded); - connect(t, &Trace::markerRemoved, this, &TraceXYPlot::markerRemoved); - auto tracemarkers = t->getMarkers(); - for(auto m : tracemarkers) { - markerAdded(m); - } - } if(isTDRtype(YAxis[axis].type)) { t->addTDRinterest(); } -// traceColorChanged(t); } else { if(isTDRtype(YAxis[axis].type)) { t->removeTDRinterest(); } tracesAxis[axis].erase(t); - // clean up and delete -// if(curves[axis].find(t) != curves[axis].end()) { -// delete curves[axis][t].curve; -// curves[axis].erase(t); -// } - int otherAxis = axis == 0 ? 1 : 0; -// if(curves[otherAxis].find(t) == curves[otherAxis].end()) { -// // this trace is not used anymore, disconnect from notifications -// disconnect(t, &Trace::dataChanged, this, &TraceXYPlot::triggerReplot); -// disconnect(t, &Trace::colorChanged, this, &TraceXYPlot::traceColorChanged); -// disconnect(t, &Trace::visibilityChanged, this, &TraceXYPlot::traceColorChanged); -// disconnect(t, &Trace::visibilityChanged, this, &TraceXYPlot::triggerReplot); -// } if(axis == 0) { disconnect(t, &Trace::markerAdded, this, &TraceXYPlot::markerAdded); disconnect(t, &Trace::markerRemoved, this, &TraceXYPlot::markerRemoved); @@ -713,78 +609,7 @@ bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type) return true; } -void TraceXYPlot::updateXAxis() -{ - if(XAxis.mode == XAxisMode::Manual) { -// plot->setAxisScale(QwtPlot::xBottom, XAxis.rangeMin, XAxis.rangeMax, XAxis.rangeDiv); - } else { - // automatic mode, figure out limits - double max = std::numeric_limits::lowest(); - double min = std::numeric_limits::max(); - if(XAxis.mode == XAxisMode::UseSpan) { - min = sweep_fmin; - max = sweep_fmax; - } else if(XAxis.mode == XAxisMode::FitTraces) { - for(auto t : traces) { - bool enabled = (tracesAxis[0].find(t.first) != tracesAxis[0].end() - || tracesAxis[1].find(t.first) != tracesAxis[1].end()); - auto trace = t.first; - if(enabled && trace->isVisible()) { - // this trace is currently displayed - double trace_min = std::numeric_limits::max(); - double trace_max = std::numeric_limits::lowest(); - switch(XAxis.type) { - case XAxisType::Frequency: - trace_min = trace->minFreq(); - trace_max = trace->maxFreq(); - break; - case XAxisType::Time: - trace_min = trace->getTDR().front().time; - trace_max = trace->getTDR().back().time; - break; - case XAxisType::Distance: - trace_min = trace->getTDR().front().distance; - trace_max = trace->getTDR().back().distance; - break; - } - if(trace_min < min) { - min = trace_min; - } - if(trace_max > max) { - max = trace_max; - } - } - } - } - if(min >= max) { - // still at initial values, no traces are active, leave axis unchanged - return; - } - constexpr int minDivisions = 8; - double max_div_step = (max - min) / minDivisions; - int zeros = floor(log10(max_div_step)); - double decimals_shift = pow(10, zeros); - max_div_step /= decimals_shift; - if(max_div_step >= 5) { - max_div_step = 5; - } else if(max_div_step >= 2) { - max_div_step = 2; - } else { - max_div_step = 1; - } - auto div_step = max_div_step * decimals_shift; - // round min up to next multiple of div_step - auto start_div = ceil(min / div_step) * div_step; - QList tickList; - for(double tick = start_div;tick <= max;tick += div_step) { - tickList.append(tick); - } -// QwtScaleDiv scalediv(min, max, QList(), QList(), tickList); -// plot->setAxisScaleDiv(QwtPlot::xBottom, scalediv); - } -} - -double TraceXYPlot::transformFrequencyY(std::complex data, TraceXYPlot::YAxisType type) +double TraceXYPlot::transformY(std::complex data, TraceXYPlot::YAxisType type) { switch(type) { case YAxisType::Magnitude: @@ -796,134 +621,103 @@ double TraceXYPlot::transformFrequencyY(std::complex data, TraceXYPlot:: return (1+abs(data)) / (1-abs(data)); } break; + default: + break; } return numeric_limits::quiet_NaN(); } -//QwtSeriesData *TraceXYPlot::createQwtSeriesData(Trace &t, int axis) -//{ -// return new QwtTraceSeries(t, YAxis[axis].type, this); -//} +QPointF TraceXYPlot::transformY(Trace *t, unsigned int sample, TraceXYPlot::YAxisType type) +{ + QPointF ret = QPointF(numeric_limits::quiet_NaN(), numeric_limits::quiet_NaN()); + switch(type) { + case YAxisType::Magnitude: + case YAxisType::Phase: + case YAxisType::VSWR: { + auto d = t->sample(sample); + ret.setY(transformY(d.S, type)); + ret.setX(d.frequency); + } + break; + case YAxisType::Impulse: + ret.setY(t->getTDR()[sample].impulseResponse); + if(XAxis.type == XAxisType::Distance) { + ret.setX(t->getTDR()[sample].distance); + } else { + ret.setX(t->getTDR()[sample].time); + } + break; + case YAxisType::Step: + ret.setY(t->getTDR()[sample].stepResponse); + if(XAxis.type == XAxisType::Distance) { + ret.setX(t->getTDR()[sample].distance); + } else { + ret.setX(t->getTDR()[sample].time); + } + break; + case YAxisType::Impedance: { + auto step = t->getTDR()[sample].stepResponse; + if(abs(step) < 1.0) { + ret.setY(50 * (1+step) / (1-step)); + } + if(XAxis.type == XAxisType::Distance) { + ret.setX(t->getTDR()[sample].distance); + } else { + ret.setX(t->getTDR()[sample].time); + } + } + break; + case YAxisType::Disabled: + case YAxisType::Last: + // no valid axis + break; + } + return ret; +} -//void TraceXYPlot::traceColorChanged(Trace *t) -//{ -// for(int axis = 0;axis < 2;axis++) { -// if(curves[axis].find(t) != curves[axis].end()) { -// // trace active, change the pen color -// if(t->isVisible()) { -// if(axis == 0) { -// curves[axis][t].curve->setPen(t->color()); -// } else { -// curves[axis][t].curve->setPen(t->color(), 1.0, Qt::DashLine); -// } -// for(auto m : t->getMarkers()) { -// if(markers.count(m)) { -// markers[m]->attach(plot); -// } -// } -// } else { -// curves[axis][t].curve->setPen(t->color(), 0.0, Qt::NoPen); -// for(auto m : t->getMarkers()) { -// if(markers.count(m)) { -// markers[m]->detach(); -// } -// } -// } -// } -// } -//} +unsigned int TraceXYPlot::numTraceSamples(Trace *t) +{ + if(XAxis.type == XAxisType::Frequency) { + return t->size(); + } else { + return t->getTDR().size(); + } +} -//void TraceXYPlot::markerAdded(TraceMarker *m) -//{ -// if(markers.count(m)) { -// return; -// } -// auto qwtMarker = new QwtPlotMarker; -// markers[m] = qwtMarker; -// markerSymbolChanged(m); -// connect(m, &TraceMarker::symbolChanged, this, &TraceXYPlot::markerSymbolChanged); -// connect(m, &TraceMarker::dataChanged, this, &TraceXYPlot::markerDataChanged); -// markerDataChanged(m); -// qwtMarker->attach(plot); -// triggerReplot(); -//} - -//void TraceXYPlot::markerRemoved(TraceMarker *m) -//{ -// disconnect(m, &TraceMarker::symbolChanged, this, &TraceXYPlot::markerSymbolChanged); -// disconnect(m, &TraceMarker::dataChanged, this, &TraceXYPlot::markerDataChanged); -// if(markers.count(m)) { -// markers[m]->detach(); -// delete markers[m]; -// markers.erase(m); -// } -// triggerReplot(); -//} - -//void TraceXYPlot::markerDataChanged(TraceMarker *m) -//{ -// auto qwtMarker = markers[m]; -// qwtMarker->setXValue(m->getFrequency()); -// qwtMarker->setYValue(FrequencyAxisTransformation(YAxis[0].type, m->getData())); -// triggerReplot(); -//} - -//void TraceXYPlot::markerSymbolChanged(TraceMarker *m) -//{ -// auto qwtMarker = markers[m]; -// qwtMarker->setSymbol(nullptr); - -// QwtSymbol *sym=new QwtSymbol; -// sym->setPixmap(m->getSymbol()); -// sym->setPinPoint(QPointF(m->getSymbol().width()/2, m->getSymbol().height())); -// qwtMarker->setSymbol(sym); -// triggerReplot(); -//} - -//void TraceXYPlot::clicked(const QPointF pos) -//{ -// auto clickPoint = drawPicker->plotToPixel(pos); -// unsigned int closestDistance = numeric_limits::max(); -// TraceMarker *closestMarker = nullptr; -// for(auto m : markers) { -// if(!m.first->isMovable()) { -// continue; -// } -// auto markerPoint = drawPicker->plotToPixel(m.second->value()); -// auto yDiff = abs(markerPoint.y() - clickPoint.y()); -// auto xDiff = abs(markerPoint.x() - clickPoint.x()); -// unsigned int distance = xDiff * xDiff + yDiff * yDiff; -// if(distance < closestDistance) { -// closestDistance = distance; -// closestMarker = m.first; -// } -// } -// if(closestDistance <= 400) { -// selectedMarker = closestMarker; -// selectedCurve = curves[0][selectedMarker->trace()].curve; -// } else { -// selectedMarker = nullptr; -// selectedCurve = nullptr; -// } -//} - -//void TraceXYPlot::moved(const QPointF pos) -//{ -// if(!selectedMarker || !selectedCurve) { -// return; -// } -// selectedMarker->setFrequency(pos.x()); -//} - -//void TraceXYPlot::setColorFromPreferences() -//{ -// auto pref = Preferences::getInstance(); -// plot->setCanvasBackground(pref.General.graphColors.background); -// auto pal = plot->palette(); -// pal.setColor(QPalette::Window, pref.General.graphColors.background); -// pal.setColor(QPalette::WindowText, pref.General.graphColors.axis); -// pal.setColor(QPalette::Text, pref.General.graphColors.axis); -// plot->setPalette(pal); -// grid->setPen(pref.General.graphColors.divisions); -//} +QPoint TraceXYPlot::dataToPixel(Trace::Data d) +{ + if(d.frequency < XAxis.rangeMin || d.frequency > XAxis.rangeMax) { + return QPoint(); + } + auto y = transformY(d.S, YAxis[0].type); + QPoint p; + p.setX(Util::Scale(d.frequency, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth)); + p.setY(Util::Scale(y, YAxis[0].rangeMin, YAxis[0].rangeMax, plotAreaBottom, 0)); + return p; +} +void TraceXYPlot::traceDropped(Trace *t, QPoint position) +{ + if(YAxis[0].type == YAxisType::Disabled && YAxis[1].type == YAxisType::Disabled) { + // no Y axis enabled, unable to drop + return; + } + if(YAxis[0].type == YAxisType::Disabled) { + // only axis 1 enabled + enableTraceAxis(t, 1, true); + return; + } + if(YAxis[1].type == YAxisType::Disabled) { + // only axis 0 enabled + enableTraceAxis(t, 0, true); + return; + } + // both axis enabled, check drop position + auto drop = Util::Scale(position.x(), plotAreaLeft, plotAreaLeft + plotAreaWidth, 0.0, 1.0); + if(drop < 0.66) { + enableTraceAxis(t, 0, true); + } + if(drop > 0.33) { + enableTraceAxis(t, 1, true); + } +} diff --git a/Software/PC_Application/Traces/tracexyplot.h b/Software/PC_Application/Traces/tracexyplot.h index 8f1c645..65ac871 100644 --- a/Software/PC_Application/Traces/tracexyplot.h +++ b/Software/PC_Application/Traces/tracexyplot.h @@ -3,35 +3,13 @@ #include "traceplot.h" #include -//#include -//#include -//#include -//#include -//#include -//#include - -//// Derived plotpicker, exposing transformation functions -//class XYplotPicker : public QwtPlotPicker { -// Q_OBJECT -//public: -// XYplotPicker(int xAxis, int yAxis, RubberBand rubberBand, DisplayMode trackerMode, QWidget *w) -// : QwtPlotPicker(xAxis, yAxis, rubberBand, trackerMode, w) {}; -// QPoint plotToPixel(const QPointF &pos) { -// return transform(pos); -// } -// QPointF pixelToPlot(const QPoint &pos) { -// return invTransform(pos); -// } -//}; class TraceXYPlot : public TracePlot { friend class XYplotAxisDialog; -// friend class QwtTraceSeries; Q_OBJECT public: TraceXYPlot(TraceModel &model, QWidget *parent = nullptr); - ~TraceXYPlot(); enum class YAxisType { Disabled = 0, @@ -61,9 +39,8 @@ public: void setXAxis(XAxisType type, XAxisMode mode, double min, double max, double div); void enableTrace(Trace *t, bool enabled) override; void updateSpan(double min, double max) override; + void replot() override; - // Applies potentially changed colors to all XY-plots -// static void updateGraphColors(); bool isTDRtype(YAxisType type); public slots: @@ -76,23 +53,16 @@ protected: private slots: void updateAxisTicks(); -// void traceColorChanged(Trace *t); -// void markerAdded(TraceMarker *m) override; -// void markerRemoved(TraceMarker *m) override; -// void markerDataChanged(TraceMarker *m); -// void markerSymbolChanged(TraceMarker *m); - -// void clicked(const QPointF pos); -// void moved(const QPointF pos); private: static constexpr int AxisLabelSize = 10; -// void setColorFromPreferences(); QString AxisTypeToName(YAxisType type); void enableTraceAxis(Trace *t, int axis, bool enabled); bool supported(Trace *t, YAxisType type); - void updateXAxis(); - double transformFrequencyY(std::complex data, YAxisType type); -// QwtSeriesData *createQwtSeriesData(Trace &t, int axis); + double transformY(std::complex data, YAxisType type); + QPointF transformY(Trace *t, unsigned int sample, YAxisType type); + unsigned int numTraceSamples(Trace *t); + QPoint dataToPixel(Trace::Data d) override; + void traceDropped(Trace *t, QPoint position) override; std::set tracesAxis[2]; @@ -120,22 +90,7 @@ private: YAxis YAxis[2]; XAxis XAxis; -// using CurveData = struct { -// QwtPlotCurve *curve; -// QwtSeriesData *data; -// }; - -// std::map curves[2]; -// std::map markers; -// QwtPlot *plot; -// QwtPlotGrid *grid; - TraceMarker *selectedMarker; -// QwtPlotCurve *selectedCurve; - -// XYplotPicker *drawPicker; - - // keep track of all created plots for changing colors -// static std::set allPlots; + int plotAreaLeft, plotAreaWidth, plotAreaBottom; }; #endif // TRACEXYPLOT_H diff --git a/Software/PC_Application/qwtplotpiecewisecurve.h b/Software/PC_Application/qwtplotpiecewisecurve.h deleted file mode 100644 index 2d6ee4c..0000000 --- a/Software/PC_Application/qwtplotpiecewisecurve.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** - * Qwt Widget Library - * Copyright (C) 1997 Josef Wilgen - * Copyright (C) 2002 Uwe Rathmann - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the Qwt License, Version 1.0 - *****************************************************************************/ - -#ifndef QWT_PLOT_PIECEWISE_CURVE_H -#define QWT_PLOT_PIECEWISE_CURVE_H - -#include "qwt_plot_curve.h" - -/*! - \brief A class which draws piecewise curves - - This class can be used to display data with missing (NaN) values as a - piecewise curve in the x-y plane. -*/ -class QWT_EXPORT QwtPlotPiecewiseCurve: public QwtPlotCurve -{ -public: - explicit QwtPlotPiecewiseCurve(); - explicit QwtPlotPiecewiseCurve(const QwtText &title); - explicit QwtPlotPiecewiseCurve(const QString &title); - ~QwtPlotPiecewiseCurve(); - - virtual void drawCurve(QPainter *p, int style, - const QwtScaleMap &xMap, const QwtScaleMap &yMap, - const QRectF &canvasRect, int from, int to) const override; - -private: - static bool isNaN(double x); -}; - -inline QwtPlotPiecewiseCurve::QwtPlotPiecewiseCurve(): QwtPlotCurve() -{ -} - -inline QwtPlotPiecewiseCurve::QwtPlotPiecewiseCurve(const QwtText &title): - QwtPlotCurve(title) -{ -} - -inline QwtPlotPiecewiseCurve::QwtPlotPiecewiseCurve(const QString &title): - QwtPlotCurve(title) -{ -} - -inline QwtPlotPiecewiseCurve::~QwtPlotPiecewiseCurve() -{ - -} - -inline bool QwtPlotPiecewiseCurve::isNaN(double x) -{ - return x != x; -} - -#endif diff --git a/Software/PC_Application/unit.cpp b/Software/PC_Application/unit.cpp index 197beb4..bb957ed 100644 --- a/Software/PC_Application/unit.cpp +++ b/Software/PC_Application/unit.cpp @@ -37,35 +37,30 @@ QString Unit::ToString(double value, QString unit, QString prefixes, int precisi if(isnan(value) || isinf(value)) { sValue.append("NaN"); return sValue; - } else if(value == 0.0) { + } else if(abs(value) <= numeric_limits::epsilon()) { sValue.append("0 "); } else { if(value < 0) { sValue.append('-'); value = -value; } - int preDotDigits = log10(value) + 2; - int prefixIndex = prefixes.indexOf(' '); + int prefixIndex = 0; //prefixes.indexOf(' '); + int preDotDigits = log10(value / SIPrefixToFactor(prefixes[prefixIndex].toLatin1())) + 1; while(preDotDigits > 3 && prefixIndex < prefixes.length() - 1) { - value /= 1000.0; - preDotDigits -= 3; prefixIndex++; + preDotDigits = log10(value / SIPrefixToFactor(prefixes[prefixIndex].toLatin1())) + 1; } - while(preDotDigits<=1 && prefixIndex > 0) { - value *= 1000.0; - preDotDigits += 3; - prefixIndex--; - } + value /= SIPrefixToFactor(prefixes[prefixIndex].toLatin1()); stringstream ss; ss << std::fixed; if(preDotDigits >= 0) { - if(precision - preDotDigits + 1 < 0) { + if(precision - preDotDigits < 0) { ss << std::setprecision(0); } else { - ss << std::setprecision(precision - preDotDigits + 1); + ss << std::setprecision(precision - preDotDigits); } } else { - ss << std::setprecision(precision - 1); + ss << std::setprecision(precision - 2); } ss << value; sValue.append(QString::fromStdString(ss.str())); @@ -89,6 +84,6 @@ double Unit::SIPrefixToFactor(char prefix) case 'G': return 1e9; break; case 'T': return 1e12; break; case 'P': return 1e15; break; - default: return 0; break; + default: return 1e0; break; } } diff --git a/Software/PC_Application/unit.h b/Software/PC_Application/unit.h index 3bc4677..db8522e 100644 --- a/Software/PC_Application/unit.h +++ b/Software/PC_Application/unit.h @@ -6,6 +6,7 @@ namespace Unit { double FromString(QString string, QString unit = QString(), QString prefixes = " "); + // prefixed need to be in ascending order (e.g. "m kMG" is okay, whjle "MkG" does not work) QString ToString(double value, QString unit = QString(), QString prefixes = " ", int precision = 6); double SIPrefixToFactor(char prefix); };