Qwt dependency removed, drag&drop for traces added
This commit is contained in:
parent
25196fbc30
commit
c22d576984
23
.github/workflows/Build.yml
vendored
23
.github/workflows/Build.yml
vendored
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -7,9 +7,14 @@
|
||||
|
||||
std::set<TracePlot*> 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<unsigned int>::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<double>::max();
|
||||
unsigned int closestIndex = 0;
|
||||
for(unsigned int i=0;i<samples;i++) {
|
||||
auto data = t->sample(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 *> TracePlot::getPlots()
|
||||
|
@ -40,9 +40,15 @@ protected:
|
||||
bool markedForDeletion;
|
||||
static std::set<TracePlot*> 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;
|
||||
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "tracesmithchart.h"
|
||||
#include "tracesmithchart.h"
|
||||
#include <QPainter>
|
||||
#include <array>
|
||||
#include <math.h>
|
||||
@ -38,83 +38,20 @@ void TraceSmithChart::axisSetupDialog()
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
QPoint TraceSmithChart::plotToPixel(std::complex<double> S)
|
||||
QPoint TraceSmithChart::dataToPixel(Trace::Data d)
|
||||
{
|
||||
return transform.map(QPoint(S.real() * smithCoordMax, -S.imag() * smithCoordMax));
|
||||
}
|
||||
|
||||
std::complex<double> TraceSmithChart::pixelToPlot(const QPoint &pos)
|
||||
{
|
||||
QPointF inv = transform.inverted().map(pos);
|
||||
return complex<double>(inv.x()/smithCoordMax, -inv.y()/smithCoordMax);
|
||||
}
|
||||
|
||||
void TraceSmithChart::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
auto clickPoint = event->pos();
|
||||
unsigned int closestDistance = numeric_limits<unsigned int>::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<double>::max();
|
||||
unsigned int closestIndex = 0;
|
||||
for(unsigned int i=0;i<samples;i++) {
|
||||
auto data = t->sample(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 */)
|
||||
|
@ -18,15 +18,13 @@ protected:
|
||||
static constexpr double screenUsage = 0.9;
|
||||
static constexpr double smithCoordMax = 4096;
|
||||
|
||||
QPoint plotToPixel(std::complex<double> S);
|
||||
std::complex<double> 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
|
||||
|
@ -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);
|
||||
|
@ -1,26 +1,18 @@
|
||||
#include "tracexyplot.h"
|
||||
#include <QGridLayout>
|
||||
//#include "qwtplotpiecewisecurve.h"
|
||||
//#include "qwt_series_data.h"
|
||||
#include "trace.h"
|
||||
#include <cmath>
|
||||
#include <QFrame>
|
||||
//#include <qwt_plot_canvas.h>
|
||||
//#include <qwt_scale_div.h>
|
||||
//#include <qwt_plot_layout.h>
|
||||
#include "tracemarker.h"
|
||||
//#include <qwt_symbol.h>
|
||||
//#include <qwt_picker_machine.h>
|
||||
#include "xyplotaxisdialog.h"
|
||||
#include <preferences.h>
|
||||
#include <QPainter>
|
||||
#include "Util/util.h"
|
||||
#include "unit.h"
|
||||
#include <QDebug>
|
||||
|
||||
using namespace std;
|
||||
|
||||
//set<TraceXYPlot*> TraceXYPlot::allPlots;
|
||||
|
||||
const set<TraceXYPlot::YAxisType> TraceXYPlot::YAxisTypes = {TraceXYPlot::YAxisType::Disabled,
|
||||
TraceXYPlot::YAxisType::Magnitude,
|
||||
TraceXYPlot::YAxisType::Phase,
|
||||
@ -29,97 +21,8 @@ const set<TraceXYPlot::YAxisType> TraceXYPlot::YAxisTypes = {TraceXYPlot::YAxisT
|
||||
TraceXYPlot::YAxisType::Step,
|
||||
TraceXYPlot::YAxisType::Impedance};
|
||||
|
||||
static double FrequencyAxisTransformation(TraceXYPlot::YAxisType type, complex<double> 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<double>::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<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
//class QwtTraceSeries : public QwtSeriesData<QPointF> {
|
||||
//public:
|
||||
// QwtTraceSeries(Trace &t, TraceXYPlot::YAxisType Ytype, const TraceXYPlot *plot)
|
||||
// : QwtSeriesData<QPointF>(),
|
||||
// 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<double>(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<double>(x, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth));
|
||||
p.setY(Util::Scale<double>(y, ax.rangeMin, ax.rangeMax, plotAreaBottom, 0));
|
||||
auto toPlotCoordinates = [=](const QPointF& point, const class YAxis& ax) -> QPoint {
|
||||
QPoint p;
|
||||
p.setX(Util::Scale<double>(point.x(), XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth));
|
||||
p.setY(Util::Scale<double>(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;j<nPoints;j++) {
|
||||
auto last = transformY(t, j-1, YAxis[i].type);
|
||||
auto now = transformY(t, j, YAxis[i].type);
|
||||
|
||||
if(isnan(last.y()) || isnan(now.y()) || isinf(last.y()) || isinf(now.y())) {
|
||||
continue;
|
||||
}
|
||||
pen = QPen(t->color(), 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;j<nPoints;j++) {
|
||||
auto last = t->sample(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<double>& 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<double>::epsilon() ;tick+= step) {
|
||||
ticks.push_back(tick);
|
||||
}
|
||||
};
|
||||
|
||||
auto createAutomaticTicks = [](vector<double>& 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<double>::lowest();
|
||||
double min = std::numeric_limits<double>::max();
|
||||
for(auto t : tracesAxis[i]) {
|
||||
unsigned int samples = numTraceSamples(t);
|
||||
for(unsigned int j=0;j<samples;j++) {
|
||||
auto point = transformY(t, j, YAxis[i].type);
|
||||
|
||||
if(point.y() > 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<double>::lowest();
|
||||
double min = std::numeric_limits<double>::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<double>::max();
|
||||
double trace_max = std::numeric_limits<double>::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<double> tickList;
|
||||
for(double tick = start_div;tick <= max;tick += div_step) {
|
||||
tickList.append(tick);
|
||||
}
|
||||
// QwtScaleDiv scalediv(min, max, QList<double>(), QList<double>(), tickList);
|
||||
// plot->setAxisScaleDiv(QwtPlot::xBottom, scalediv);
|
||||
}
|
||||
}
|
||||
|
||||
double TraceXYPlot::transformFrequencyY(std::complex<double> data, TraceXYPlot::YAxisType type)
|
||||
double TraceXYPlot::transformY(std::complex<double> data, TraceXYPlot::YAxisType type)
|
||||
{
|
||||
switch(type) {
|
||||
case YAxisType::Magnitude:
|
||||
@ -796,134 +621,103 @@ double TraceXYPlot::transformFrequencyY(std::complex<double> data, TraceXYPlot::
|
||||
return (1+abs(data)) / (1-abs(data));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
//QwtSeriesData<QPointF> *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<double>::quiet_NaN(), numeric_limits<double>::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<unsigned int>::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<double>(d.frequency, XAxis.rangeMin, XAxis.rangeMax, plotAreaLeft, plotAreaLeft + plotAreaWidth));
|
||||
p.setY(Util::Scale<double>(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<double>(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);
|
||||
}
|
||||
}
|
||||
|
@ -3,35 +3,13 @@
|
||||
|
||||
#include "traceplot.h"
|
||||
#include <set>
|
||||
//#include <qwt_plot.h>
|
||||
//#include <qwt_plot_curve.h>
|
||||
//#include <qwt_series_data.h>
|
||||
//#include <qwt_plot_marker.h>
|
||||
//#include <qwt_plot_grid.h>
|
||||
//#include <qwt_plot_picker.h>
|
||||
|
||||
//// 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<double> data, YAxisType type);
|
||||
// QwtSeriesData<QPointF> *createQwtSeriesData(Trace &t, int axis);
|
||||
double transformY(std::complex<double> 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<Trace*> tracesAxis[2];
|
||||
|
||||
@ -120,22 +90,7 @@ private:
|
||||
YAxis YAxis[2];
|
||||
XAxis XAxis;
|
||||
|
||||
// using CurveData = struct {
|
||||
// QwtPlotCurve *curve;
|
||||
// QwtSeriesData<QPointF> *data;
|
||||
// };
|
||||
|
||||
// std::map<Trace*, CurveData> curves[2];
|
||||
// std::map<TraceMarker*, QwtPlotMarker*> markers;
|
||||
// QwtPlot *plot;
|
||||
// QwtPlotGrid *grid;
|
||||
TraceMarker *selectedMarker;
|
||||
// QwtPlotCurve *selectedCurve;
|
||||
|
||||
// XYplotPicker *drawPicker;
|
||||
|
||||
// keep track of all created plots for changing colors
|
||||
// static std::set<TraceXYPlot*> allPlots;
|
||||
int plotAreaLeft, plotAreaWidth, plotAreaBottom;
|
||||
};
|
||||
|
||||
#endif // TRACEXYPLOT_H
|
||||
|
@ -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
|
@ -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<double>::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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user