AxisRenderingUtils.cpp
217 lines
| 6.8 KiB
| text/x-c
|
CppLexer
Alexandre Leroux
|
r915 | #include "Visualization/AxisRenderingUtils.h" | ||
r1420 | #include <Data/ScalarTimeSerie.h> | |||
#include <Data/SpectrogramTimeSerie.h> | ||||
#include <Data/VectorTimeSerie.h> | ||||
Alexandre Leroux
|
r915 | |||
r1420 | #include <Variable/Variable2.h> | |||
Alexandre Leroux
|
r1281 | |||
Alexandre Leroux
|
r1009 | #include <Visualization/SqpColorScale.h> | ||
Alexandre Leroux
|
r915 | #include <Visualization/qcustomplot.h> | ||
Alexandre Leroux
|
r928 | Q_LOGGING_CATEGORY(LOG_AxisRenderingUtils, "AxisRenderingUtils") | ||
r1420 | namespace | |||
{ | ||||
Alexandre Leroux
|
r915 | |||
Alexandre Leroux
|
r916 | /// 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 | { | ||
r1420 | if (isTimeAxis) | |||
{ | ||||
Alexandre Leroux
|
r916 | auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create(); | ||
dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT); | ||||
dateTicker->setDateTimeSpec(Qt::UTC); | ||||
return dateTicker; | ||||
} | ||||
r1420 | else if (scaleType == QCPAxis::stLogarithmic) | |||
{ | ||||
Alexandre Leroux
|
r929 | return QSharedPointer<QCPAxisTickerLog>::create(); | ||
} | ||||
r1420 | else | |||
{ | ||||
Alexandre Leroux
|
r916 | // 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 | ||||
*/ | ||||
r1420 | void setAxisProperties(QCPAxis& axis, const std::string& unit, bool isTime, | |||
QCPAxis::ScaleType scaleType = QCPAxis::stLinear) | ||||
Alexandre Leroux
|
r916 | { | ||
// label (unit name) | ||||
r1420 | axis.setLabel(QString::fromStdString(unit)); | |||
Alexandre Leroux
|
r916 | |||
// scale type | ||||
axis.setScaleType(scaleType); | ||||
r1420 | if (scaleType == QCPAxis::stLogarithmic) | |||
{ | ||||
Alexandre Leroux
|
r929 | // Scientific notation | ||
axis.setNumberPrecision(0); | ||||
axis.setNumberFormat("eb"); | ||||
} | ||||
Alexandre Leroux
|
r916 | |||
// ticker (depending on the type of unit) | ||||
r1420 | axis.setTicker(axisTicker(isTime, scaleType)); | |||
Alexandre Leroux
|
r916 | } | ||
Alexandre Leroux
|
r915 | /** | ||
* Delegate used to set axes properties | ||||
*/ | ||||
template <typename T, typename Enabled = void> | ||||
r1420 | struct AxisSetter | |||
{ | ||||
static void setProperties(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 | } | ||
Alexandre Leroux
|
r1281 | |||
r1420 | static void setUnits(T&, QCustomPlot&, SqpColorScale&) | |||
Alexandre Leroux
|
r1281 | { | ||
// Default implementation does nothing | ||||
qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis units: unmanaged type of data"; | ||||
} | ||||
Alexandre Leroux
|
r915 | }; | ||
/** | ||||
* Specialization of AxisSetter for scalars and vectors | ||||
* @sa ScalarSeries | ||||
* @sa VectorSeries | ||||
*/ | ||||
template <typename T> | ||||
r1420 | struct AxisSetter<T, | |||
typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value | ||||
r1432 | or std::is_base_of<VectorTimeSerie, T>::value | |||
or std::is_base_of<MultiComponentTimeSerie, T>::value>> | ||||
r1420 | { | |||
static void setProperties(QCustomPlot&, SqpColorScale&) | ||||
Alexandre Leroux
|
r1281 | { | ||
// Nothing to do | ||||
} | ||||
r1420 | static void setUnits(T& dataSeries, QCustomPlot& plot, SqpColorScale&) | |||
Alexandre Leroux
|
r915 | { | ||
r1420 | auto serie = dynamic_cast<TimeSeries::ITimeSerie*>(&dataSeries); | |||
setAxisProperties(*plot.xAxis, "s", true); | ||||
setAxisProperties(*plot.yAxis, serie->unit(1), false); | ||||
Alexandre Leroux
|
r915 | } | ||
}; | ||||
Alexandre Leroux
|
r920 | /** | ||
* Specialization of AxisSetter for spectrograms | ||||
* @sa SpectrogramSeries | ||||
*/ | ||||
template <typename T> | ||||
r1420 | struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>> | |||
{ | ||||
static void setProperties(QCustomPlot& plot, SqpColorScale& colorScale) | ||||
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); | ||||
r1420 | colorScale.m_Scale->setMinimumMargins(QMargins { 0, 0, 0, 0 }); | |||
Alexandre Leroux
|
r920 | |||
// Aligns color scale with axes | ||||
auto marginGroups = plot.axisRect()->marginGroups(); | ||||
r1420 | 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
|
r1110 | colorScale.m_AutomaticThreshold = true; | ||
Alexandre Leroux
|
r920 | } | ||
Alexandre Leroux
|
r1281 | |||
r1420 | static void setUnits(T& dataSeries, QCustomPlot& plot, SqpColorScale& colorScale) | |||
Alexandre Leroux
|
r1281 | { | ||
r1420 | auto serie = dynamic_cast<TimeSeries::ITimeSerie*>(&dataSeries); | |||
setAxisProperties(*plot.xAxis, "s", true); | ||||
setAxisProperties(*plot.yAxis, serie->unit(1), false, QCPAxis::stLogarithmic); | ||||
setAxisProperties( | ||||
*colorScale.m_Scale->axis(), serie->unit(2), false, QCPAxis::stLogarithmic); | ||||
Alexandre Leroux
|
r1281 | } | ||
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> | ||||
r1420 | struct AxisHelper : public IAxisHelper | |||
{ | ||||
r1421 | explicit AxisHelper(std::shared_ptr<T> dataSeries) : m_DataSeries { dataSeries } {} | |||
Alexandre Leroux
|
r915 | |||
r1420 | void setProperties(QCustomPlot& plot, SqpColorScale& colorScale) override | |||
Alexandre Leroux
|
r915 | { | ||
Alexandre Leroux
|
r1281 | AxisSetter<T>::setProperties(plot, colorScale); | ||
Alexandre Leroux
|
r915 | } | ||
r1420 | void setUnits(QCustomPlot& plot, SqpColorScale& colorScale) override | |||
Alexandre Leroux
|
r1281 | { | ||
r1420 | if (m_DataSeries) | |||
{ | ||||
Alexandre Leroux
|
r1281 | AxisSetter<T>::setUnits(*m_DataSeries, plot, colorScale); | ||
} | ||||
r1420 | else | |||
{ | ||||
Alexandre Leroux
|
r1281 | qCCritical(LOG_AxisRenderingUtils()) << "Can't set units: inconsistency between the " | ||
"type of data series and the type supposed"; | ||||
} | ||||
} | ||||
r1421 | std::shared_ptr<T> m_DataSeries; | |||
Alexandre Leroux
|
r915 | }; | ||
} // namespace | ||||
r1420 | QString formatValue(double value, const QCPAxis& axis) | |||
Alexandre Leroux
|
r916 | { | ||
// If the axis is a time axis, formats the value as a date | ||||
r1420 | if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) | |||
{ | ||||
Alexandre Leroux
|
r916 | return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT); | ||
} | ||||
r1420 | else | |||
{ | ||||
Alexandre Leroux
|
r1027 | return QString::number(value, NUMBER_FORMAT, NUMBER_PRECISION); | ||
Alexandre Leroux
|
r916 | } | ||
} | ||||
r1420 | std::unique_ptr<IAxisHelper> IAxisHelperFactory::create(Variable2& variable) noexcept | |||
Alexandre Leroux
|
r915 | { | ||
r1420 | switch (variable.type()) | |||
{ | ||||
Alexandre Leroux
|
r1281 | case DataSeriesType::SCALAR: | ||
r1420 | return std::make_unique<AxisHelper<ScalarTimeSerie>>( | |||
r1421 | std::dynamic_pointer_cast<ScalarTimeSerie>(variable.data())); | |||
Alexandre Leroux
|
r1281 | case DataSeriesType::SPECTROGRAM: | ||
r1420 | return std::make_unique<AxisHelper<SpectrogramTimeSerie>>( | |||
r1421 | std::dynamic_pointer_cast<SpectrogramTimeSerie>(variable.data())); | |||
Alexandre Leroux
|
r1281 | case DataSeriesType::VECTOR: | ||
r1420 | return std::make_unique<AxisHelper<VectorTimeSerie>>( | |||
r1421 | std::dynamic_pointer_cast<VectorTimeSerie>(variable.data())); | |||
r1432 | case DataSeriesType::MULTICOMPONENT: | |||
return std::make_unique<AxisHelper<MultiComponentTimeSerie>>( | ||||
std::dynamic_pointer_cast<MultiComponentTimeSerie>(variable.data())); | ||||
Alexandre Leroux
|
r1281 | default: | ||
// Creates default helper | ||||
break; | ||||
Alexandre Leroux
|
r915 | } | ||
Alexandre Leroux
|
r1281 | |||
r1420 | return std::make_unique<AxisHelper<TimeSeries::ITimeSerie>>(nullptr); | |||
Alexandre Leroux
|
r915 | } | ||