#include "Visualization/AxisRenderingUtils.h" #include #include #include #include #include Q_LOGGING_CATEGORY(LOG_AxisRenderingUtils, "AxisRenderingUtils") namespace { 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"); const auto NUMBER_FORMAT = 'g'; const auto NUMBER_PRECISION = 9; /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or /// non-time data QSharedPointer axisTicker(bool isTimeAxis, QCPAxis::ScaleType scaleType) { if (isTimeAxis) { auto dateTicker = QSharedPointer::create(); dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT); dateTicker->setDateTimeSpec(Qt::UTC); return dateTicker; } else if (scaleType == QCPAxis::stLogarithmic) { return QSharedPointer::create(); } else { // default ticker return QSharedPointer::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); if (scaleType == QCPAxis::stLogarithmic) { // Scientific notation axis.setNumberPrecision(0); axis.setNumberFormat("eb"); } // ticker (depending on the type of unit) axis.setTicker(axisTicker(unit.m_TimeUnit, scaleType)); } /** * Delegate used to set axes properties */ template struct AxisSetter { static void setProperties(T &, QCustomPlot &, SqpColorScale &) { // Default implementation does nothing qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis properties: unmanaged type of data"; } }; /** * Specialization of AxisSetter for scalars and vectors * @sa ScalarSeries * @sa VectorSeries */ template struct AxisSetter::value or std::is_base_of::value> > { static void setProperties(T &dataSeries, QCustomPlot &plot, SqpColorScale &) { dataSeries.lockRead(); auto xAxisUnit = dataSeries.xAxisUnit(); auto valuesUnit = dataSeries.valuesUnit(); dataSeries.unlock(); setAxisProperties(*plot.xAxis, xAxisUnit); setAxisProperties(*plot.yAxis, valuesUnit); } }; /** * Specialization of AxisSetter for spectrograms * @sa SpectrogramSeries */ template struct AxisSetter::value> > { static void setProperties(T &dataSeries, QCustomPlot &plot, SqpColorScale &colorScale) { dataSeries.lockRead(); auto xAxisUnit = dataSeries.xAxisUnit(); auto yAxisUnit = dataSeries.yAxisUnit(); auto valuesUnit = dataSeries.valuesUnit(); dataSeries.unlock(); setAxisProperties(*plot.xAxis, xAxisUnit); setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic); // Displays color scale in plot plot.plotLayout()->insertRow(0); plot.plotLayout()->addElement(0, 0, colorScale.m_Scale); colorScale.m_Scale->setType(QCPAxis::atTop); colorScale.m_Scale->setMinimumMargins(QMargins{0, 0, 0, 0}); // Aligns color scale with axes auto marginGroups = plot.axisRect()->marginGroups(); for (auto it = marginGroups.begin(), end = marginGroups.end(); it != end; ++it) { colorScale.m_Scale->setMarginGroup(it.key(), it.value()); } // Set color scale properties setAxisProperties(*colorScale.m_Scale->axis(), valuesUnit, QCPAxis::stLogarithmic); colorScale.m_AutomaticThreshold = true; } }; /** * Default implementation of IAxisHelper, which takes data series to set axes properties * @tparam T the data series' type */ template struct AxisHelper : public IAxisHelper { explicit AxisHelper(T &dataSeries) : m_DataSeries{dataSeries} {} void setProperties(QCustomPlot &plot, SqpColorScale &colorScale) override { AxisSetter::setProperties(m_DataSeries, plot, colorScale); } T &m_DataSeries; }; } // namespace QString formatValue(double value, const QCPAxis &axis) { // If the axis is a time axis, formats the value as a date if (auto axisTicker = qSharedPointerDynamicCast(axis.ticker())) { return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT); } else { return QString::number(value, NUMBER_FORMAT, NUMBER_PRECISION); } } std::unique_ptr IAxisHelperFactory::create(std::shared_ptr dataSeries) noexcept { if (auto scalarSeries = std::dynamic_pointer_cast(dataSeries)) { return std::make_unique >(*scalarSeries); } else if (auto spectrogramSeries = std::dynamic_pointer_cast(dataSeries)) { return std::make_unique >(*spectrogramSeries); } else if (auto vectorSeries = std::dynamic_pointer_cast(dataSeries)) { return std::make_unique >(*vectorSeries); } else { return std::make_unique >(*dataSeries); } }