AxisRenderingUtils.cpp
177 lines
| 5.8 KiB
| text/x-c
|
CppLexer
Alexandre Leroux
|
r915 | #include "Visualization/AxisRenderingUtils.h" | ||
#include <Data/ScalarSeries.h> | ||||
Alexandre Leroux
|
r920 | #include <Data/SpectrogramSeries.h> | ||
Alexandre Leroux
|
r915 | #include <Data/VectorSeries.h> | ||
Alexandre Leroux
|
r1009 | #include <Visualization/SqpColorScale.h> | ||
Alexandre Leroux
|
r915 | #include <Visualization/qcustomplot.h> | ||
Alexandre Leroux
|
r928 | Q_LOGGING_CATEGORY(LOG_AxisRenderingUtils, "AxisRenderingUtils") | ||
Alexandre Leroux
|
r915 | namespace { | ||
Alexandre Leroux
|
r916 | const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz"); | ||
/// Format for datetimes on a axis | ||||
const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss"); | ||||
Alexandre Leroux
|
r1027 | const auto NUMBER_FORMAT = 'g'; | ||
const auto NUMBER_PRECISION = 9; | ||||
Alexandre Leroux
|
r916 | /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or | ||
/// non-time data | ||||
Alexandre Leroux
|
r929 | QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis, QCPAxis::ScaleType scaleType) | ||
Alexandre Leroux
|
r916 | { | ||
if (isTimeAxis) { | ||||
auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create(); | ||||
dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT); | ||||
dateTicker->setDateTimeSpec(Qt::UTC); | ||||
return dateTicker; | ||||
} | ||||
Alexandre Leroux
|
r929 | else if (scaleType == QCPAxis::stLogarithmic) { | ||
return QSharedPointer<QCPAxisTickerLog>::create(); | ||||
} | ||||
Alexandre Leroux
|
r916 | else { | ||
// default ticker | ||||
return QSharedPointer<QCPAxisTicker>::create(); | ||||
} | ||||
} | ||||
/** | ||||
* Sets properties of the axis passed as parameter | ||||
* @param axis the axis to set | ||||
* @param unit the unit to set for the axis | ||||
* @param scaleType the scale type to set for the axis | ||||
*/ | ||||
void setAxisProperties(QCPAxis &axis, const Unit &unit, | ||||
QCPAxis::ScaleType scaleType = QCPAxis::stLinear) | ||||
{ | ||||
// label (unit name) | ||||
axis.setLabel(unit.m_Name); | ||||
// scale type | ||||
axis.setScaleType(scaleType); | ||||
Alexandre Leroux
|
r929 | if (scaleType == QCPAxis::stLogarithmic) { | ||
// Scientific notation | ||||
axis.setNumberPrecision(0); | ||||
axis.setNumberFormat("eb"); | ||||
} | ||||
Alexandre Leroux
|
r916 | |||
// ticker (depending on the type of unit) | ||||
Alexandre Leroux
|
r929 | axis.setTicker(axisTicker(unit.m_TimeUnit, scaleType)); | ||
Alexandre Leroux
|
r916 | } | ||
Alexandre Leroux
|
r915 | /** | ||
* Delegate used to set axes properties | ||||
*/ | ||||
template <typename T, typename Enabled = void> | ||||
struct AxisSetter { | ||||
Alexandre Leroux
|
r1009 | static void setProperties(T &, QCustomPlot &, SqpColorScale &) | ||
Alexandre Leroux
|
r915 | { | ||
// Default implementation does nothing | ||||
Alexandre Leroux
|
r928 | qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis properties: unmanaged type of data"; | ||
Alexandre Leroux
|
r915 | } | ||
}; | ||||
/** | ||||
* Specialization of AxisSetter for scalars and vectors | ||||
* @sa ScalarSeries | ||||
* @sa VectorSeries | ||||
*/ | ||||
template <typename T> | ||||
struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value | ||||
or std::is_base_of<VectorSeries, T>::value> > { | ||||
Alexandre Leroux
|
r1009 | static void setProperties(T &dataSeries, QCustomPlot &plot, SqpColorScale &) | ||
Alexandre Leroux
|
r915 | { | ||
Alexandre Leroux
|
r916 | dataSeries.lockRead(); | ||
auto xAxisUnit = dataSeries.xAxisUnit(); | ||||
auto valuesUnit = dataSeries.valuesUnit(); | ||||
dataSeries.unlock(); | ||||
setAxisProperties(*plot.xAxis, xAxisUnit); | ||||
setAxisProperties(*plot.yAxis, valuesUnit); | ||||
Alexandre Leroux
|
r915 | } | ||
}; | ||||
Alexandre Leroux
|
r920 | /** | ||
* Specialization of AxisSetter for spectrograms | ||||
* @sa SpectrogramSeries | ||||
*/ | ||||
template <typename T> | ||||
struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > { | ||||
Alexandre Leroux
|
r1009 | static void setProperties(T &dataSeries, QCustomPlot &plot, SqpColorScale &colorScale) | ||
Alexandre Leroux
|
r920 | { | ||
dataSeries.lockRead(); | ||||
auto xAxisUnit = dataSeries.xAxisUnit(); | ||||
Alexandre Leroux
|
r988 | auto yAxisUnit = dataSeries.yAxisUnit(); | ||
Alexandre Leroux
|
r920 | auto valuesUnit = dataSeries.valuesUnit(); | ||
dataSeries.unlock(); | ||||
setAxisProperties(*plot.xAxis, xAxisUnit); | ||||
Alexandre Leroux
|
r929 | setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic); | ||
Alexandre Leroux
|
r920 | |||
// Displays color scale in plot | ||||
plot.plotLayout()->insertRow(0); | ||||
Alexandre Leroux
|
r1009 | plot.plotLayout()->addElement(0, 0, colorScale.m_Scale); | ||
colorScale.m_Scale->setType(QCPAxis::atTop); | ||||
colorScale.m_Scale->setMinimumMargins(QMargins{0, 0, 0, 0}); | ||||
Alexandre Leroux
|
r920 | |||
// Aligns color scale with axes | ||||
auto marginGroups = plot.axisRect()->marginGroups(); | ||||
for (auto it = marginGroups.begin(), end = marginGroups.end(); it != end; ++it) { | ||||
Alexandre Leroux
|
r1009 | colorScale.m_Scale->setMarginGroup(it.key(), it.value()); | ||
Alexandre Leroux
|
r920 | } | ||
// Set color scale properties | ||||
Alexandre Leroux
|
r1009 | setAxisProperties(*colorScale.m_Scale->axis(), valuesUnit, QCPAxis::stLogarithmic); | ||
Alexandre Leroux
|
r1110 | colorScale.m_AutomaticThreshold = true; | ||
Alexandre Leroux
|
r920 | } | ||
}; | ||||
Alexandre Leroux
|
r915 | /** | ||
* Default implementation of IAxisHelper, which takes data series to set axes properties | ||||
* @tparam T the data series' type | ||||
*/ | ||||
template <typename T> | ||||
struct AxisHelper : public IAxisHelper { | ||||
explicit AxisHelper(T &dataSeries) : m_DataSeries{dataSeries} {} | ||||
Alexandre Leroux
|
r1009 | void setProperties(QCustomPlot &plot, SqpColorScale &colorScale) override | ||
Alexandre Leroux
|
r915 | { | ||
AxisSetter<T>::setProperties(m_DataSeries, plot, colorScale); | ||||
} | ||||
T &m_DataSeries; | ||||
}; | ||||
} // namespace | ||||
Alexandre Leroux
|
r916 | QString formatValue(double value, const QCPAxis &axis) | ||
{ | ||||
// If the axis is a time axis, formats the value as a date | ||||
if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) { | ||||
return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT); | ||||
} | ||||
else { | ||||
Alexandre Leroux
|
r1027 | return QString::number(value, NUMBER_FORMAT, NUMBER_PRECISION); | ||
Alexandre Leroux
|
r916 | } | ||
} | ||||
Alexandre Leroux
|
r915 | std::unique_ptr<IAxisHelper> | ||
IAxisHelperFactory::create(std::shared_ptr<IDataSeries> dataSeries) noexcept | ||||
{ | ||||
if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) { | ||||
return std::make_unique<AxisHelper<ScalarSeries> >(*scalarSeries); | ||||
} | ||||
Alexandre Leroux
|
r920 | else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) { | ||
return std::make_unique<AxisHelper<SpectrogramSeries> >(*spectrogramSeries); | ||||
} | ||||
Alexandre Leroux
|
r915 | else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) { | ||
return std::make_unique<AxisHelper<VectorSeries> >(*vectorSeries); | ||||
} | ||||
else { | ||||
return std::make_unique<AxisHelper<IDataSeries> >(*dataSeries); | ||||
} | ||||
} | ||||