Skip to content

Commit

Permalink
improve XY-plot rendering, especially for logarithmic Y-axes
Browse files Browse the repository at this point in the history
  • Loading branch information
jankae committed Apr 16, 2024
1 parent cdfe95a commit 994b536
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 36 deletions.
26 changes: 19 additions & 7 deletions Software/PC_Application/LibreVNA-GUI/Traces/traceaxis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ static double createAutomaticTicks(vector<double>& ticks, double start, double s
}

static void createLogarithmicTicks(vector<double>& ticks, double start, double stop, int minDivisions) {
double mult = 1.0;
if(start < 0.0 && stop < 0.0) {
mult = -1.0;
auto buf = -stop;
stop = -start;
start = buf;
}

// enforce usable log settings
if(start <= 0) {
start = 1.0;
Expand All @@ -55,7 +63,9 @@ static void createLogarithmicTicks(vector<double>& ticks, double start, double s
int zeros = floor(log10(max_div_decade));
double decimals_shift = pow(10, zeros);
max_div_decade /= decimals_shift;
if(max_div_decade < 2) {
if(max_div_decade <= 1) {
max_div_decade = 1;
} else if(max_div_decade < 2) {
max_div_decade = 2;
} else if(max_div_decade < 5) {
max_div_decade = 5;
Expand All @@ -70,15 +80,17 @@ static void createLogarithmicTicks(vector<double>& ticks, double start, double s
step *= 10;
}
do {
ticks.push_back(div);
if(ticks.size() > 1 && div != step && floor(log10(div)) != floor(log10(div - step))) {
ticks.push_back(div * mult);
if(ticks.size() > 1 && div != step && floor(log10(div)+std::numeric_limits<double>::epsilon()) != floor(log10(div - step)+std::numeric_limits<double>::epsilon())) {
// reached a new decade with this switch
step *= 10;
div = step;
} else {
div += step;
}
div += step;
} while(div <= stop);

if(mult == -1.0) {
std::reverse(ticks.begin(), ticks.end());
}
}

YAxis::YAxis()
Expand Down Expand Up @@ -285,7 +297,7 @@ QString YAxis::Prefixes(Type type, TraceModel::DataSource source)
case Type::Real: return "pnum ";
case Type::Imaginary: return "pnum ";
case Type::QualityFactor: return " ";
case Type::AbsImpedance: return " ";
case Type::AbsImpedance: return "m k";
case Type::SeriesR: return "m kM";
case Type::Reactance: return "m kM";
case Type::Capacitance: return "pnum ";
Expand Down
89 changes: 62 additions & 27 deletions Software/PC_Application/LibreVNA-GUI/Traces/tracexyplot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,25 +478,35 @@ void TraceXYPlot::draw(QPainter &p)
step = max / 1000;
}
int significantDigits = floor(log10(max)) - floor(log10(step)) + 1;
if(yAxis[i].getLog() && yAxis[i].getRangeMax()/yAxis[i].getRangeMin() >= 100) {
significantDigits = floor(log10(max)) + 1;
}

int lastTickLabelEnd = std::numeric_limits<int>::max();
for(unsigned int j = 0; j < yAxis[i].getTicks().size(); j++) {
auto yCoord = yAxis[i].transform(yAxis[i].getTicks()[j], w.height() - xAxisSpace, plotAreaTop);
p.setPen(QPen(pref.Graphs.Color.axis, 1));
// draw tickmark on axis
auto tickStart = i == 0 ? plotAreaLeft : plotAreaLeft + plotAreaWidth;
auto tickLen = i == 0 ? -2 : 2;
p.drawLine(tickStart, yCoord, tickStart + tickLen, yCoord);
QString unit = "";
QString prefix = " ";
if(pref.Graphs.showUnits) {
unit = yAxis[i].Unit();
prefix = yAxis[i].Prefixes();
}
auto tickValue = Unit::ToString(yAxis[i].getTicks()[j], unit, prefix, significantDigits);
if(i == 0) {
p.drawText(QRectF(0, yCoord - pref.Graphs.fontSizeAxis/2 - 2, tickStart + 2 * tickLen, pref.Graphs.fontSizeAxis*1.5), Qt::AlignRight, tickValue);
if(yCoord + pref.Graphs.fontSizeAxis >= lastTickLabelEnd) {
// would overlap previous tick label, skip
} else {
p.drawText(QRectF(tickStart + 2 * tickLen + 2, yCoord - pref.Graphs.fontSizeAxis/2 - 2, yAxisSpace, pref.Graphs.fontSizeAxis*1.5), Qt::AlignLeft, tickValue);
QString unit = "";
QString prefix = " ";
if(pref.Graphs.showUnits) {
unit = yAxis[i].Unit();
prefix = yAxis[i].Prefixes();
}
auto tickValue = Unit::ToString(yAxis[i].getTicks()[j], unit, prefix, significantDigits);
QRect bounding;
if(i == 0) {
p.drawText(QRect(0, yCoord - pref.Graphs.fontSizeAxis/2 - 2, tickStart + 2 * tickLen, pref.Graphs.fontSizeAxis*1.5), Qt::AlignRight, tickValue, &bounding);
} else {
p.drawText(QRect(tickStart + 2 * tickLen + 2, yCoord - pref.Graphs.fontSizeAxis/2 - 2, yAxisSpace, pref.Graphs.fontSizeAxis*1.5), Qt::AlignLeft, tickValue, &bounding);
}
lastTickLabelEnd = bounding.y();
}

// tick lines
Expand All @@ -509,8 +519,8 @@ void TraceXYPlot::draw(QPainter &p)
if (pref.Graphs.Color.Ticks.Background.enabled) {
if (j%2)
{
int yCoordTop = yAxis[i].transform(yAxis[i].getTicks()[j], plotAreaTop, w.height() - xAxisSpace);
int yCoordBot = yAxis[i].transform(yAxis[i].getTicks()[j-1], plotAreaTop, w.height() - xAxisSpace);
int yCoordTop = yCoord;
int yCoordBot = yAxis[i].transform(yAxis[i].getTicks()[j-1], w.height() - xAxisSpace, plotAreaTop);
if(yCoordTop > yCoordBot) {
auto buf = yCoordBot;
yCoordBot = yCoordTop;
Expand Down Expand Up @@ -925,26 +935,51 @@ void TraceXYPlot::updateAxisTicks()
}
if(max >= min) {
auto range = max - min;
if(range == 0.0) {
// this could happen if all values in a trace are identical (e.g. imported ideal touchstone files)
if(max == 0.0) {
// simply use +/-1 range
max = 1.0;
min = -1.0;
} else {
// +/-5% around value
max += abs(max * 0.05);
min -= abs(max * 0.05);
if(yAxis[i].getLog()){
// log axis

double maxLog10 = log10(abs(max));
// prevent zero-crossing
if(min <= 0.0 && max > 0) {
min = pow(10, maxLog10 - 3); // just show 3 decades by default
} else if(min >= 0.0 && max < 0) {
// same thing if negative
min = -pow(10, maxLog10 - 3);
}
// add 5% visible range
double ratio = log10(max/min);
max *= pow(10, ratio * 0.05);
min /= pow(10, ratio * 0.05);
} else {
// add 5% of range at both ends
min -= range * 0.05;
max += range * 0.05;
// linear axis
if(range == 0.0) {
// this could happen if all values in a trace are identical (e.g. imported ideal touchstone files)
if(max == 0.0) {
// simply use +/-1 range
max = 1.0;
min = -1.0;
} else {
// +/-5% around value
max += abs(max * 0.05);
min -= abs(max * 0.05);
}
} else {
// add 5% of range at both ends
min -= range * 0.05;
max += range * 0.05;
}
}
} else {
// max/min still at default values, no valid samples are available for this axis, use default range
max = 1.0;
min = -1.0;
if(!yAxis[i].getLog()) {
// linear axis
max = 1.0;
min = -1.0;
} else {
// log axis
max = 100.0;
min = 0.1;
}
}
yAxis[i].set(yAxis[i].getType(), yAxis[i].getLog(), true, min, max, 0);
}
Expand Down
10 changes: 8 additions & 2 deletions Software/PC_Application/LibreVNA-GUI/Traces/xyplotaxisdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
connect(ui->Y1auto, &QCheckBox::toggled, [this](bool checked) {
ui->Y1min->setEnabled(!checked);
ui->Y1max->setEnabled(!checked);
ui->Y1divs->setEnabled(!checked);
ui->Y1divs->setEnabled(!checked && !ui->Y1log->isChecked());
});
connect(ui->Y1log, &QCheckBox::toggled, [this](bool checked) {
ui->Y1divs->setEnabled(!checked && !ui->Y1auto->isChecked());
});

connect(ui->Y2type, qOverload<int>(&QComboBox::currentIndexChanged), [=](int index) {
Expand All @@ -95,7 +98,10 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
connect(ui->Y2auto, &QCheckBox::toggled, [this](bool checked) {
ui->Y2min->setEnabled(!checked);
ui->Y2max->setEnabled(!checked);
ui->Y2divs->setEnabled(!checked);
ui->Y2divs->setEnabled(!checked && !ui->Y1log->isChecked());
});
connect(ui->Y2log, &QCheckBox::toggled, [this](bool checked) {
ui->Y2divs->setEnabled(!checked && !ui->Y2auto->isChecked());
});

connect(ui->Xauto, &QCheckBox::toggled, [this](bool checked) {
Expand Down

0 comments on commit 994b536

Please sign in to comment.