summaryrefslogtreecommitdiffstats
path: root/sdrbase/gui/scaleengine.cpp
diff options
context:
space:
mode:
authorChristian Daniel <cd@maintech.de>2013-03-22 11:18:30 +0100
committerChristian Daniel <cd@maintech.de>2013-03-22 11:18:30 +0100
commiteca56e35be841396f6c57bab540a3e1503253d56 (patch)
tree4a0a29cd53a84339e56754c32bf67391c83494fe /sdrbase/gui/scaleengine.cpp
parent2c8c930b39fe069b36d81caa00401d9ac182a9d8 (diff)
monster rework
- pluginify whole project - reorganize directory structure - fix PortAudio detection script - implement generic channelizer - fix several OpenGL problems - rework presets - add audio mixing - too many more
Diffstat (limited to 'sdrbase/gui/scaleengine.cpp')
-rw-r--r--sdrbase/gui/scaleengine.cpp569
1 files changed, 569 insertions, 0 deletions
diff --git a/sdrbase/gui/scaleengine.cpp b/sdrbase/gui/scaleengine.cpp
new file mode 100644
index 0000000..06bde36
--- /dev/null
+++ b/sdrbase/gui/scaleengine.cpp
@@ -0,0 +1,569 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
+// written by Christian Daniel //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see <http://www.gnu.org/licenses/>. //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include <math.h>
+#include <QFontMetrics>
+#include <QDataStream>
+#include "gui/scaleengine.h"
+
+/*
+static double trunc(double d)
+{
+ return (d > 0) ? floor(d) : ceil(d);
+}
+*/
+
+QString ScaleEngine::formatTick(double value, int decimalPlaces, bool fancyTime)
+{
+ if((m_physicalUnit != Unit::Time) || (!fancyTime) || 1) {
+ return QString("%1").arg(value, 0, 'f', decimalPlaces);
+ } else {
+ QString str;
+ double orig = fabs(value);
+ double tmp;
+
+ if(orig >= 86400.0) {
+ tmp = floor(value / 86400.0);
+ str = QString("%1.").arg(tmp, 0, 'f', 0);
+ value -= tmp * 86400.0;
+ if(value < 0.0)
+ value *= -1.0;
+ }
+
+ if(orig >= 3600.0) {
+ tmp = floor(value / 3600.0);
+ str += QString("%1:").arg(tmp, 2, 'f', 0, QChar('0'));
+ value -= tmp * 3600.0;
+ if(value < 0.0)
+ value *= -1.0;
+ }
+
+ if(orig >= 60.0) {
+ tmp = floor(value / 60.0);
+ str += QString("%1:").arg(tmp, 2, 'f', 0, QChar('0'));
+ value -= tmp * 60.0;
+ if(value < 0.0)
+ value *= -1.0;
+ }
+
+ tmp = value;
+ str += QString("%1").arg(tmp, 2, 'f', decimalPlaces, QChar('0'));
+
+ return str;
+ }
+}
+
+void ScaleEngine::calcCharSize()
+{
+ QFontMetricsF fontMetrics(m_font);
+
+ if(m_orientation == Qt::Vertical) {
+ m_charSize = fontMetrics.height();
+ } else {
+ QString str("012345679.,-");
+ int i;
+ float size;
+ float max = 0.0f;
+ for(i = 0; i < str.length(); i++) {
+ size = fontMetrics.width(QString(str[i]));
+ if(size > max)
+ max = size;
+ }
+ m_charSize = max;
+ }
+}
+
+void ScaleEngine::calcScaleFactor()
+{
+ double median;
+
+ median = ((m_rangeMax - m_rangeMin) / 2.0) + m_rangeMin;
+ m_scale = 1.0;
+
+ switch(m_physicalUnit) {
+ case Unit::None:
+ m_unitStr.clear();
+ break;
+
+ case Unit::Frequency:
+ if(median < 1000.0) {
+ m_unitStr = QObject::tr("Hz");
+ } else if(median < 1000000.0) {
+ m_unitStr = QObject::tr("kHz");
+ m_scale = 1000.0;
+ } else if(median < 1000000000.0) {
+ m_unitStr = QObject::tr("MHz");
+ m_scale = 1000000.0;
+ } else if(median < 1000000000000.0){
+ m_unitStr = QObject::tr("GHz");
+ m_scale = 1000000000.0;
+ } else {
+ m_unitStr = QObject::tr("THz");
+ m_scale = 1000000000000.0;
+ }
+ break;
+
+ case Unit::Information:
+ if(median < 1024.0) {
+ m_unitStr = QObject::tr("Bytes");
+ } else if(median < 1048576.0) {
+ m_unitStr = QObject::tr("KiBytes");
+ m_scale = 1024.0;
+ } else if(median < 1073741824.0) {
+ m_unitStr = QObject::tr("MiBytes");
+ m_scale = 1048576.0;
+ } else if(median < 1099511627776.0) {
+ m_unitStr = QObject::tr("GiBytes");
+ m_scale = 1073741824.0;
+ } else if(median < 1125899906842624.0) {
+ m_unitStr = QObject::tr("TiBytes");
+ m_scale = 1099511627776.0;
+ } else {
+ m_unitStr = QObject::tr("PiBytes");
+ m_scale = 1125899906842624.0;
+ }
+ break;
+
+ case Unit::Percent:
+ m_unitStr = QString("%");
+ break;
+
+ case Unit::Decibel:
+ m_unitStr = QString("dB");
+ break;
+
+ case Unit::DecibelMilliWatt:
+ m_unitStr = QString("dBm");
+ break;
+
+ case Unit::DecibelMicroVolt:
+ m_unitStr = QString("dBµV");
+ break;
+
+ case Unit::AngleDegrees:
+ m_unitStr = QString("°");
+
+ case Unit::Time:
+ if(median < 0.001) {
+ m_unitStr = QString("µs");
+ m_scale = 0.000001;
+ } else if(median < 1.0) {
+ m_unitStr = QString("ms");
+ m_scale = 0.001;
+ } else {
+ m_unitStr = QString("s");
+ }
+ break;
+ }
+}
+
+double ScaleEngine::calcMajorTickUnits(double distance, int* retDecimalPlaces)
+{
+ double sign;
+ double log10x;
+ double exponent;
+ double base;
+ int decimalPlaces;
+
+ if(distance == 0.0)
+ return 0.0;
+
+ sign = (distance > 0.0) ? 1.0 : -1.0;
+ log10x = log10(fabs(distance));
+ exponent = floor(log10x);
+ base = pow(10.0, log10x - exponent);
+ decimalPlaces = (int)(-exponent);
+/*
+ if((m_physicalUnit == Unit::Time) && (distance >= 1.0)) {
+ if(retDecimalPlaces != NULL)
+ *retDecimalPlaces = 0;
+ if(distance < 1.0)
+ return 1.0;
+ else if(distance < 5.0)
+ return 5.0;
+ else if(distance < 10.0)
+ return 10.0;
+ else if(distance < 15.0)
+ return 15.0;
+ else if(distance < 30.0)
+ return 30.0;
+ else if(distance < 60.0)
+ return 60.0;
+ else if(distance < 5.0 * 60.0)
+ return 5.0 * 60.0;
+ else if(distance < 10.0 * 60.0)
+ return 10.0 * 60.0;
+ else if(distance < 15.0 * 60.0)
+ return 15.0 * 60.0;
+ else if(distance < 30.0 * 60.0)
+ return 30.0 * 60.0;
+ else if(distance < 3600.0)
+ return 3600.0;
+ else if(distance < 2.0 * 3600.0)
+ return 2.0 * 3600.0;
+ else if(distance < 3.0 * 3600.0)
+ return 3.0 * 3600.0;
+ else if(distance < 6.0 * 3600.0)
+ return 6.0 * 3600.0;
+ else if(distance < 12.0 * 3600.0)
+ return 12.0 * 3600.0;
+ else if(distance < 86000.0)
+ return 86000.0;
+ else if(distance < 2.0 * 86000.0)
+ return 2.0 * 86000.0;
+ else if(distance < 7.0 * 86000.0)
+ return 7.0 * 86000.0;
+ else if(distance < 10.0 * 86000.0)
+ return 10.0 * 86000.0;
+ else if(distance < 30.0 * 86000.0)
+ return 30.0 * 86000.0;
+ else return 90.0 * 86000.0;
+ } else {*/
+ if(base <= 1.0) {
+ base = 1.0;
+ } else if(base <= 2.0) {
+ base = 2.0;
+ } else if(base <= 2.5) {
+ base = 2.5;
+ if(decimalPlaces >= 0)
+ decimalPlaces++;
+ } else if(base <= 5.0) {
+ base = 5.0;
+ } else {
+ base = 10.0;
+ }/*
+ }*/
+
+ if(retDecimalPlaces != NULL) {
+ if(decimalPlaces < 0)
+ decimalPlaces = 0;
+ *retDecimalPlaces = decimalPlaces;
+ }
+
+ return sign * base * pow(10.0, exponent);
+}
+
+int ScaleEngine::calcTickTextSize()
+{
+ int tmp;
+ int tickLen;
+ int decimalPlaces;
+
+ tickLen = 1;
+ tmp = formatTick(m_rangeMin / m_scale, 0).length();
+ if(tmp > tickLen)
+ tickLen = tmp;
+ tmp = formatTick(m_rangeMax / m_scale, 0).length();
+ if(tmp > tickLen)
+ tickLen = tmp;
+
+ calcMajorTickUnits((m_rangeMax - m_rangeMin) / m_scale, &decimalPlaces);
+
+ return tickLen + decimalPlaces + 1;
+}
+
+void ScaleEngine::forceTwoTicks()
+{
+ Tick tick;
+ QFontMetricsF fontMetrics(m_font);
+
+ m_tickList.clear();
+ tick.major = true;
+
+ tick.pos = getPosFromValue(m_rangeMin);
+ tick.text = formatTick(m_rangeMin / m_scale, m_decimalPlaces);
+ tick.textSize = fontMetrics.boundingRect(tick.text).width();
+ if(m_orientation == Qt::Vertical)
+ tick.textPos = tick.pos - fontMetrics.ascent() / 2;
+ else tick.textPos = tick.pos - fontMetrics.boundingRect(tick.text).width() / 2;
+ m_tickList.append(tick);
+
+ tick.pos = getPosFromValue(m_rangeMax);
+ tick.text = formatTick(m_rangeMax / m_scale, m_decimalPlaces);
+ tick.textSize = fontMetrics.boundingRect(tick.text).width();
+ if(m_orientation == Qt::Vertical)
+ tick.textPos = tick.pos - fontMetrics.ascent() / 2;
+ else tick.textPos = tick.pos - fontMetrics.boundingRect(tick.text).width() / 2;
+ m_tickList.append(tick);
+}
+
+void ScaleEngine::reCalc()
+{
+ float majorTickSize;
+ double rangeMinScaled;
+ double rangeMaxScaled;
+ int maxNumMajorTicks;
+ int numMajorTicks;
+ int step;
+ int skip;
+ double value;
+ double value2;
+ int i;
+ int j;
+ Tick tick;
+ float pos;
+ QString str;
+ QFontMetricsF fontMetrics(m_font);
+ float endPos;
+ float lastEndPos;
+ bool done;
+
+ if(!m_recalc)
+ return;
+ m_recalc = false;
+
+ m_tickList.clear();
+
+ calcScaleFactor();
+ rangeMinScaled = m_rangeMin / m_scale;
+ rangeMaxScaled = m_rangeMax / m_scale;
+
+ if(m_orientation == Qt::Vertical) {
+ maxNumMajorTicks = (int)(m_size / (fontMetrics.lineSpacing() * 1.3f));
+ } else {
+ majorTickSize = (calcTickTextSize() + 2) * m_charSize;
+ if(majorTickSize != 0.0)
+ maxNumMajorTicks = (int)(m_size / majorTickSize);
+ else maxNumMajorTicks = 20;
+ }
+
+ m_majorTickValueDistance = calcMajorTickUnits((rangeMaxScaled - rangeMinScaled) / maxNumMajorTicks, &m_decimalPlaces);
+ numMajorTicks = (int)((rangeMaxScaled - rangeMinScaled) / m_majorTickValueDistance);
+
+ if(numMajorTicks == 0) {
+ forceTwoTicks();
+ return;
+ }
+
+ if(maxNumMajorTicks > 0)
+ m_numMinorTicks = (int)(m_size / (maxNumMajorTicks * fontMetrics.height()));
+ else m_numMinorTicks = 0;
+ if(m_numMinorTicks < 1)
+ m_numMinorTicks = 0;
+ else if(m_numMinorTicks < 2)
+ m_numMinorTicks = 1;
+ else if(m_numMinorTicks < 5)
+ m_numMinorTicks = 2;
+ else if(m_numMinorTicks < 10)
+ m_numMinorTicks = 5;
+ else m_numMinorTicks = 10;
+
+ m_firstMajorTickValue = floor(rangeMinScaled / m_majorTickValueDistance) * m_majorTickValueDistance;
+
+ skip = 0;
+
+ if(rangeMinScaled == rangeMaxScaled)
+ return;
+
+ while(true) {
+ m_tickList.clear();
+
+ step = 0;
+ lastEndPos = -100000000;
+ done = true;
+
+ for(i = 0; true; i++) {
+ value = majorTickValue(i);
+
+ for(j = 1; j < m_numMinorTicks; j++) {
+ value2 = value + minorTickValue(j);
+ if(value2 < rangeMinScaled)
+ continue;
+ if(value2 > rangeMaxScaled)
+ break;
+ pos = getPosFromValue((value + minorTickValue(j)) * m_scale);
+ if((pos >= 0) && (pos < m_size)) {
+ tick.pos = pos;
+ tick.major = false;
+ tick.textPos = -1;
+ tick.textSize = -1;
+ tick.text.clear();
+ }
+ m_tickList.append(tick);
+ }
+
+ pos = getPosFromValue(value * m_scale);
+ if(pos < 0.0)
+ continue;
+ if(pos >= m_size)
+ break;
+
+ tick.pos = pos;
+ tick.major = true;
+ tick.textPos = -1;
+ tick.textSize = -1;
+ tick.text.clear();
+
+ if(step % (skip + 1) != 0) {
+ m_tickList.append(tick);
+ step++;
+ continue;
+ }
+ step++;
+
+ str = formatTick(value, m_decimalPlaces);
+ tick.text = str;
+ tick.textSize = fontMetrics.boundingRect(tick.text).width();
+ if(m_orientation == Qt::Vertical) {
+ tick.textPos = pos - fontMetrics.ascent() / 2;
+ endPos = tick.textPos + fontMetrics.ascent();
+ } else {
+ tick.textPos = pos - fontMetrics.boundingRect(tick.text).width() / 2;
+ endPos = tick.textPos + tick.textSize;
+ }
+
+ if(lastEndPos >= tick.textPos) {
+ done = false;
+ break;
+ } else {
+ lastEndPos = endPos;
+ }
+
+ m_tickList.append(tick);
+ }
+ if(done)
+ break;
+ skip++;
+ }
+
+ // make sure we have at least two major ticks with numbers
+ numMajorTicks = 0;
+ for(i = 0; i < m_tickList.count(); i++) {
+ tick = m_tickList.at(i);
+ if(tick.major)
+ numMajorTicks++;
+ }
+ if(numMajorTicks < 2)
+ forceTwoTicks();
+}
+
+double ScaleEngine::majorTickValue(int tick)
+{
+ return m_firstMajorTickValue + (tick * m_majorTickValueDistance);
+}
+
+double ScaleEngine::minorTickValue(int tick)
+{
+ if(m_numMinorTicks < 1)
+ return 0.0;
+ return (m_majorTickValueDistance * tick) / m_numMinorTicks;
+}
+
+ScaleEngine::ScaleEngine() :
+ m_orientation(Qt::Horizontal),
+ m_physicalUnit(Unit::None),
+ m_rangeMin(-1.0),
+ m_rangeMax(1.0),
+ m_recalc(true)
+{
+}
+
+void ScaleEngine::setOrientation(Qt::Orientation orientation)
+{
+ m_orientation = orientation;
+ m_recalc = true;
+}
+
+void ScaleEngine::setFont(const QFont& font)
+{
+ m_font = font;
+ m_recalc = true;
+ calcCharSize();
+}
+
+void ScaleEngine::setSize(float size)
+{
+ if(size > 0.0f) {
+ m_size = size;
+ } else {
+ m_size = 1.0f;
+ }
+ m_recalc = true;
+}
+
+void ScaleEngine::setRange(Unit::Physical physicalUnit, float rangeMin, float rangeMax)
+{
+ double tmpRangeMin;
+ double tmpRangeMax;
+/*
+ if(rangeMin < rangeMax) {
+ tmpRangeMin = rangeMin;
+ tmpRangeMax = rangeMax;
+ } else if(rangeMin > rangeMax) {
+ tmpRangeMin = rangeMax;
+ tmpRangeMax = rangeMin;
+ } else {
+ tmpRangeMin = rangeMin * 0.99;
+ tmpRangeMax = rangeMin * 1.01 + 0.01;
+ }
+*/
+ tmpRangeMin = rangeMin;
+ tmpRangeMax = rangeMax;
+
+ if((tmpRangeMin != m_rangeMin) || (tmpRangeMax != m_rangeMax) || (m_physicalUnit != physicalUnit)) {
+ m_physicalUnit = physicalUnit;
+ m_rangeMin = tmpRangeMin;
+ m_rangeMax = tmpRangeMax;
+ m_recalc = true;
+ }
+}
+
+float ScaleEngine::getPosFromValue(double value)
+{
+ return ((value - m_rangeMin) / (m_rangeMax - m_rangeMin)) * (m_size - 1.0);
+}
+
+float ScaleEngine::getValueFromPos(double pos)
+{
+ return ((pos * (m_rangeMax - m_rangeMin)) / (m_size - 1.0)) + m_rangeMin;
+}
+
+const ScaleEngine::TickList& ScaleEngine::getTickList()
+{
+ reCalc();
+ return m_tickList;
+}
+
+QString ScaleEngine::getRangeMinStr()
+{
+ if(m_unitStr.length() > 0)
+ return QString("%1 %2").arg(formatTick(m_rangeMin / m_scale, m_decimalPlaces, false)).arg(m_unitStr);
+ else return QString("%1").arg(formatTick(m_rangeMin / m_scale, m_decimalPlaces, false));
+}
+
+QString ScaleEngine::getRangeMaxStr()
+{
+ if(m_unitStr.length() > 0)
+ return QString("%1 %2").arg(formatTick(m_rangeMax / m_scale, m_decimalPlaces, false)).arg(m_unitStr);
+ else return QString("%1").arg(formatTick(m_rangeMax / m_scale, m_decimalPlaces, false));
+}
+
+float ScaleEngine::getScaleWidth()
+{
+ float max;
+ float len;
+ int i;
+
+ reCalc();
+ max = 0.0f;
+ for(i = 0; i < m_tickList.count(); i++) {
+ len = m_tickList[i].textSize;
+ if(len > max)
+ max = len;
+ }
+ return max;
+}