VisualizationGraphHelper.cpp
603 lines
| 21.4 KiB
| text/x-c
|
CppLexer
r243 | #include "Visualization/VisualizationGraphHelper.h" | |||
Alexandre Leroux
|
r181 | #include "Visualization/qcustomplot.h" | ||
r1420 | #include <Data/ScalarTimeSerie.h> | |||
#include <Data/SpectrogramTimeSerie.h> | ||||
r1465 | #include <Data/TimeSeriesUtils.h> | |||
r1420 | #include <Data/VectorTimeSerie.h> | |||
Alexandre Leroux
|
r182 | |||
r1465 | #include <Common/cpp_utils.h> | |||
r1420 | #include <Variable/Variable2.h> | |||
r1465 | #include <algorithm> | |||
r1467 | #include <cmath> | |||
Alexandre Leroux
|
r181 | |||
r243 | Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper") | |||
Alexandre Leroux
|
r181 | |||
r1420 | namespace | |||
{ | ||||
Alexandre Leroux
|
r182 | |||
r1420 | class SqpDataContainer : public QCPGraphDataContainer | |||
{ | ||||
r364 | public: | |||
r1420 | void appendGraphData(const QCPGraphData& data) { mData.append(data); } | |||
r364 | }; | |||
Alexandre Leroux
|
r583 | /** | ||
r591 | * Struct used to create plottables, depending on the type of the data series from which to create | |||
* them | ||||
Alexandre Leroux
|
r583 | * @tparam T the data series' type | ||
* @remarks Default implementation can't create plottables | ||||
*/ | ||||
template <typename T, typename Enabled = void> | ||||
r1420 | struct PlottablesCreator | |||
{ | ||||
r1431 | static PlottablesMap createPlottables(QCustomPlot&, const std::shared_ptr<T>& dataSeries) | |||
{ | ||||
return {}; | ||||
} | ||||
Alexandre Leroux
|
r583 | }; | ||
r235 | ||||
r1420 | PlottablesMap createGraphs(QCustomPlot& plot, int nbGraphs) | |||
Alexandre Leroux
|
r1280 | { | ||
r1420 | PlottablesMap result {}; | |||
Alexandre Leroux
|
r1280 | |||
// Creates {nbGraphs} QCPGraph to add to the plot | ||||
r1420 | for (auto i = 0; i < nbGraphs; ++i) | |||
{ | ||||
Alexandre Leroux
|
r1280 | auto graph = plot.addGraph(); | ||
r1420 | result.insert({ i, graph }); | |||
Alexandre Leroux
|
r1280 | } | ||
plot.replot(); | ||||
return result; | ||||
} | ||||
Alexandre Leroux
|
r583 | /** | ||
Alexandre Leroux
|
r1280 | * Specialization of PlottablesCreator for scalars | ||
Alexandre Leroux
|
r583 | * @sa ScalarSeries | ||
*/ | ||||
template <typename T> | ||||
r1420 | struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>> | |||
{ | ||||
r1431 | static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries) | |||
{ | ||||
return createGraphs(plot, 1); | ||||
} | ||||
Alexandre Leroux
|
r1280 | }; | ||
Alexandre Leroux
|
r583 | |||
Alexandre Leroux
|
r1280 | /** | ||
* Specialization of PlottablesCreator for vectors | ||||
* @sa VectorSeries | ||||
*/ | ||||
template <typename T> | ||||
r1420 | struct PlottablesCreator<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>> | |||
{ | ||||
r1431 | static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries) | |||
{ | ||||
return createGraphs(plot, 3); | ||||
} | ||||
}; | ||||
/** | ||||
* Specialization of PlottablesCreator for MultiComponentTimeSeries | ||||
* @sa VectorSeries | ||||
*/ | ||||
template <typename T> | ||||
struct PlottablesCreator<T, | ||||
typename std::enable_if_t<std::is_base_of<MultiComponentTimeSerie, T>::value>> | ||||
{ | ||||
static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries) | ||||
{ | ||||
return createGraphs(plot, dataSeries->size(1)); | ||||
} | ||||
Alexandre Leroux
|
r583 | }; | ||
r548 | ||||
Alexandre Leroux
|
r902 | /** | ||
* Specialization of PlottablesCreator for spectrograms | ||||
* @sa SpectrogramSeries | ||||
*/ | ||||
template <typename T> | ||||
struct PlottablesCreator<T, | ||||
r1420 | typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>> | |||
{ | ||||
r1431 | static PlottablesMap createPlottables(QCustomPlot& plot, const std::shared_ptr<T>& dataSeries) | |||
Alexandre Leroux
|
r902 | { | ||
r1420 | PlottablesMap result {}; | |||
result.insert({ 0, new QCPColorMap { plot.xAxis, plot.yAxis } }); | ||||
Alexandre Leroux
|
r902 | |||
plot.replot(); | ||||
return result; | ||||
} | ||||
}; | ||||
Alexandre Leroux
|
r583 | /** | ||
r591 | * Struct used to update plottables, depending on the type of the data series from which to update | |||
* them | ||||
Alexandre Leroux
|
r583 | * @tparam T the data series' type | ||
* @remarks Default implementation can't update plottables | ||||
*/ | ||||
template <typename T, typename Enabled = void> | ||||
r1420 | struct PlottablesUpdater | |||
{ | ||||
static void setPlotYAxisRange(T&, const DateTimeRange&, QCustomPlot&) | ||||
Alexandre Leroux
|
r900 | { | ||
Alexandre Leroux
|
r905 | qCCritical(LOG_VisualizationGraphHelper()) | ||
Alexandre Leroux
|
r900 | << QObject::tr("Can't set plot y-axis range: unmanaged data series type"); | ||
} | ||||
r1420 | static void updatePlottables(T&, PlottablesMap&, const DateTimeRange&, bool) | |||
Alexandre Leroux
|
r583 | { | ||
Alexandre Leroux
|
r905 | qCCritical(LOG_VisualizationGraphHelper()) | ||
Alexandre Leroux
|
r583 | << QObject::tr("Can't update plottables: unmanaged data series type"); | ||
} | ||||
}; | ||||
Alexandre Leroux
|
r182 | |||
Alexandre Leroux
|
r583 | /** | ||
* Specialization of PlottablesUpdater for scalars and vectors | ||||
* @sa ScalarSeries | ||||
* @sa VectorSeries | ||||
*/ | ||||
template <typename T> | ||||
r1420 | struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<ScalarTimeSerie, T>::value>> | |||
{ | ||||
static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot) | ||||
Alexandre Leroux
|
r900 | { | ||
auto minValue = 0., maxValue = 0.; | ||||
r1420 | if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries)) | |||
{ | ||||
r1423 | if (serie->size()) | |||
{ | ||||
maxValue = (*std::max_element(std::begin(*serie), std::end(*serie))).v(); | ||||
minValue = (*std::min_element(std::begin(*serie), std::end(*serie))).v(); | ||||
} | ||||
Alexandre Leroux
|
r900 | } | ||
r1420 | plot.yAxis->setRange(QCPRange { minValue, maxValue }); | |||
Alexandre Leroux
|
r900 | } | ||
r1420 | static void updatePlottables( | |||
T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) | ||||
Alexandre Leroux
|
r583 | { | ||
// For each plottable to update, resets its data | ||||
r1420 | for (const auto& plottable : plottables) | |||
{ | ||||
if (auto graph = dynamic_cast<QCPGraph*>(plottable.second)) | ||||
{ | ||||
Alexandre Leroux
|
r583 | auto dataContainer = QSharedPointer<SqpDataContainer>::create(); | ||
r1420 | if (auto serie = dynamic_cast<ScalarTimeSerie*>(&dataSeries)) | |||
{ | ||||
std::for_each( | ||||
std::begin(*serie), std::end(*serie), [&dataContainer](const auto& value) { | ||||
dataContainer->appendGraphData(QCPGraphData(value.t(), value.v())); | ||||
}); | ||||
} | ||||
Alexandre Leroux
|
r583 | graph->setData(dataContainer); | ||
} | ||||
} | ||||
r235 | ||||
r1420 | if (!plottables.empty()) | |||
{ | ||||
Alexandre Leroux
|
r583 | auto plot = plottables.begin()->second->parentPlot(); | ||
Alexandre Leroux
|
r183 | |||
r1420 | if (rescaleAxes) | |||
{ | ||||
Alexandre Leroux
|
r583 | plot->rescaleAxes(); | ||
} | ||||
} | ||||
Alexandre Leroux
|
r182 | } | ||
Alexandre Leroux
|
r583 | }; | ||
r1420 | ||||
Alexandre Leroux
|
r903 | template <typename T> | ||
r1420 | struct PlottablesUpdater<T, typename std::enable_if_t<std::is_base_of<VectorTimeSerie, T>::value>> | |||
{ | ||||
static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot) | ||||
Alexandre Leroux
|
r904 | { | ||
r1420 | double minValue = 0., maxValue = 0.; | |||
if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries)) | ||||
{ | ||||
std::for_each( | ||||
std::begin(*serie), std::end(*serie), [&minValue, &maxValue](const auto& v) { | ||||
minValue = std::min({ minValue, v.v().x, v.v().y, v.v().z }); | ||||
maxValue = std::max({ maxValue, v.v().x, v.v().y, v.v().z }); | ||||
}); | ||||
Alexandre Leroux
|
r904 | } | ||
r1420 | ||||
plot.yAxis->setRange(QCPRange { minValue, maxValue }); | ||||
Alexandre Leroux
|
r904 | } | ||
r1420 | static void updatePlottables( | |||
T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) | ||||
Alexandre Leroux
|
r903 | { | ||
r1420 | // For each plottable to update, resets its data | |||
for (const auto& plottable : plottables) | ||||
{ | ||||
if (auto graph = dynamic_cast<QCPGraph*>(plottable.second)) | ||||
{ | ||||
auto dataContainer = QSharedPointer<SqpDataContainer>::create(); | ||||
if (auto serie = dynamic_cast<VectorTimeSerie*>(&dataSeries)) | ||||
{ | ||||
switch (plottable.first) | ||||
{ | ||||
case 0: | ||||
std::for_each(std::begin(*serie), std::end(*serie), | ||||
[&dataContainer](const auto& value) { | ||||
dataContainer->appendGraphData( | ||||
QCPGraphData(value.t(), value.v().x)); | ||||
}); | ||||
break; | ||||
case 1: | ||||
std::for_each(std::begin(*serie), std::end(*serie), | ||||
[&dataContainer](const auto& value) { | ||||
dataContainer->appendGraphData( | ||||
QCPGraphData(value.t(), value.v().y)); | ||||
}); | ||||
break; | ||||
case 2: | ||||
std::for_each(std::begin(*serie), std::end(*serie), | ||||
[&dataContainer](const auto& value) { | ||||
dataContainer->appendGraphData( | ||||
QCPGraphData(value.t(), value.v().z)); | ||||
}); | ||||
break; | ||||
default: | ||||
break; | ||||
} | ||||
Alexandre Leroux
|
r922 | } | ||
r1420 | graph->setData(dataContainer); | |||
Alexandre Leroux
|
r903 | } | ||
} | ||||
r1420 | if (!plottables.empty()) | |||
{ | ||||
auto plot = plottables.begin()->second->parentPlot(); | ||||
Alexandre Leroux
|
r903 | |||
r1420 | if (rescaleAxes) | |||
{ | ||||
plot->rescaleAxes(); | ||||
} | ||||
Alexandre Leroux
|
r903 | } | ||
} | ||||
}; | ||||
r1431 | ||||
template <typename T> | ||||
r1432 | struct PlottablesUpdater<T, | |||
typename std::enable_if_t<std::is_base_of<MultiComponentTimeSerie, T>::value>> | ||||
r1431 | { | |||
static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot) | ||||
{ | ||||
double minValue = 0., maxValue = 0.; | ||||
if (auto serie = dynamic_cast<MultiComponentTimeSerie*>(&dataSeries)) | ||||
{ | ||||
r1467 | std::for_each(std::begin(*serie), std::end(*serie), [&minValue, &maxValue](auto& v) { | |||
minValue = std::min(minValue, std::min_element(v.begin(), v.end())->v()); | ||||
maxValue = std::max(maxValue, std::max_element(v.begin(), v.end())->v()); | ||||
}); | ||||
r1431 | } | |||
plot.yAxis->setRange(QCPRange { minValue, maxValue }); | ||||
} | ||||
static void updatePlottables( | ||||
T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) | ||||
{ | ||||
for (const auto& plottable : plottables) | ||||
{ | ||||
if (auto graph = dynamic_cast<QCPGraph*>(plottable.second)) | ||||
{ | ||||
auto dataContainer = QSharedPointer<SqpDataContainer>::create(); | ||||
if (auto serie = dynamic_cast<MultiComponentTimeSerie*>(&dataSeries)) | ||||
{ | ||||
r1432 | // TODO | |||
std::for_each(std::begin(*serie), std::end(*serie), | ||||
[&dataContainer, component = plottable.first](const auto& value) { | ||||
dataContainer->appendGraphData( | ||||
QCPGraphData(value.t(), value[component])); | ||||
}); | ||||
r1431 | } | |||
graph->setData(dataContainer); | ||||
} | ||||
} | ||||
if (!plottables.empty()) | ||||
{ | ||||
auto plot = plottables.begin()->second->parentPlot(); | ||||
if (rescaleAxes) | ||||
{ | ||||
plot->rescaleAxes(); | ||||
} | ||||
} | ||||
} | ||||
}; | ||||
r1467 | /*=============================================================*/ | |||
// TODO move this to dedicated srcs | ||||
/*=============================================================*/ | ||||
struct ColomapProperties | ||||
{ | ||||
int h_size_px; | ||||
int v_size_px; | ||||
double h_resolutuon; | ||||
double v_resolutuon; | ||||
}; | ||||
inline ColomapProperties CMAxisAnalysis(const TimeSeriesUtils::axis_properties& xAxisProperties, | ||||
const TimeSeriesUtils::axis_properties& yAxisProperties) | ||||
{ | ||||
int colormap_h_size | ||||
= std::min(32000, static_cast<int>(xAxisProperties.range / xAxisProperties.max_resolution)); | ||||
int colormap_v_size = static_cast<int>(yAxisProperties.range / yAxisProperties.max_resolution); | ||||
double colormap_h_resolution = xAxisProperties.range / static_cast<double>(colormap_h_size); | ||||
double colormap_v_resolution = yAxisProperties.range / static_cast<double>(colormap_v_size); | ||||
return ColomapProperties { colormap_h_size, colormap_v_size, colormap_h_resolution, | ||||
colormap_v_resolution }; | ||||
} | ||||
inline std::vector<std::pair<int, int>> build_access_pattern(const std::vector<double>& axis, | ||||
const TimeSeriesUtils::axis_properties& axisProperties, | ||||
const ColomapProperties& colormap_properties) | ||||
{ | ||||
std::vector<std::pair<int, int>> access_pattern; | ||||
for (int index = 0, cel_index = 0; index < colormap_properties.v_size_px; index++) | ||||
{ | ||||
double current_y = pow(10., (axisProperties.max_resolution * index) + axisProperties.min); | ||||
if (current_y > axis[cel_index]) | ||||
cel_index++; | ||||
access_pattern.push_back({ index, axis.size() - 1 - cel_index }); | ||||
} | ||||
return access_pattern; | ||||
} | ||||
/*=============================================================*/ | ||||
r1420 | /** | |||
* Specialization of PlottablesUpdater for spectrograms | ||||
* @sa SpectrogramSeries | ||||
*/ | ||||
template <typename T> | ||||
struct PlottablesUpdater<T, | ||||
typename std::enable_if_t<std::is_base_of<SpectrogramTimeSerie, T>::value>> | ||||
{ | ||||
static void setPlotYAxisRange(T& dataSeries, const DateTimeRange& xAxisRange, QCustomPlot& plot) | ||||
{ | ||||
r1465 | auto [minValue, maxValue] = dataSeries.axis_range(1); | |||
std::cout << "min=" << minValue << " max=" << maxValue << std::endl; | ||||
r1464 | plot.yAxis->setRange(QCPRange { minValue, maxValue }); | |||
r1420 | } | |||
static void updatePlottables( | ||||
T& dataSeries, PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) | ||||
{ | ||||
r1464 | if (plottables.empty()) | |||
{ | ||||
qCDebug(LOG_VisualizationGraphHelper()) | ||||
<< QObject::tr("Can't update spectrogram: no colormap has been associated"); | ||||
return; | ||||
} | ||||
r1420 | ||||
r1465 | // Gets the colormap to update (normally there is only one colormap) | |||
r1464 | Q_ASSERT(plottables.size() == 1); | |||
auto colormap = dynamic_cast<QCPColorMap*>(plottables.at(0)); | ||||
Q_ASSERT(colormap != nullptr); | ||||
r1465 | auto plot = colormap->parentPlot(); | |||
auto [minValue, maxValue] = dataSeries.axis_range(1); | ||||
plot->yAxis->setRange(QCPRange { minValue, maxValue }); | ||||
r1464 | if (auto serie = dynamic_cast<SpectrogramTimeSerie*>(&dataSeries)) | |||
{ | ||||
r1465 | if (serie->size(0) > 2) | |||
r1464 | { | |||
r1465 | const auto& xAxis = serie->axis(0); | |||
auto yAxis = serie->axis(1); // copy for in place reverse order | ||||
std::reverse(std::begin(yAxis), std::end(yAxis)); | ||||
auto xAxisProperties = TimeSeriesUtils::axis_analysis<TimeSeriesUtils::IsLinear, | ||||
r1467 | TimeSeriesUtils::CheckMedian>(xAxis, serie->min_sampling); | |||
r1465 | auto yAxisProperties = TimeSeriesUtils::axis_analysis<TimeSeriesUtils::IsLog, | |||
TimeSeriesUtils::DontCheckMedian>(yAxis); | ||||
r1467 | auto colormap_properties = CMAxisAnalysis(xAxisProperties, yAxisProperties); | |||
r1465 | ||||
r1467 | colormap->data()->setSize( | |||
colormap_properties.h_size_px, colormap_properties.v_size_px); | ||||
r1464 | colormap->data()->setRange( | |||
r1467 | QCPRange { xAxisProperties.min, xAxisProperties.max }, { minValue, maxValue }); | |||
r1465 | ||||
r1467 | auto y_access_pattern | |||
= build_access_pattern(yAxis, yAxisProperties, colormap_properties); | ||||
r1465 | ||||
auto line = serie->begin(); | ||||
r1467 | auto next_line = line + 1; | |||
double current_time = xAxisProperties.min; | ||||
r1465 | int x_index = 0; | |||
r1467 | auto x_min_resolution | |||
= std::fmin(2. * serie->max_sampling, xAxisProperties.max_resolution * 100.); | ||||
std::vector<double> line_values(serie->size(1)); | ||||
double avg_coef = 0.; | ||||
bool has_nan = false; | ||||
while (x_index < colormap_properties.h_size_px) | ||||
r1465 | { | |||
r1467 | if (next_line != std::end(*serie) and current_time >= next_line->t()) | |||
r1464 | { | |||
r1467 | line = next_line; | |||
next_line++; | ||||
r1465 | } | |||
r1467 | if ((current_time - xAxisProperties.min) | |||
> (static_cast<double>(x_index + 1) * colormap_properties.h_resolutuon)) | ||||
r1465 | { | |||
r1467 | std::for_each(std::cbegin(y_access_pattern), std::cend(y_access_pattern), | |||
[&colormap, &line_values, x_index, avg_coef](const auto& acc) { | ||||
colormap->data()->setCell( | ||||
x_index, acc.first, line_values[acc.second] / avg_coef); | ||||
}); | ||||
std::fill(std::begin(line_values), std::end(line_values), 0.); | ||||
r1465 | x_index++; | |||
r1467 | avg_coef = 0.; | |||
has_nan = false; | ||||
r1465 | } | |||
r1467 | if (line->t() + x_min_resolution > current_time) | |||
r1465 | { | |||
r1467 | if (has_nan) | |||
{ | ||||
std::transform(std::begin(*line), std::end(*line), | ||||
std::begin(line_values), | ||||
[](const auto& input) { return input.v(); }); | ||||
has_nan = false; | ||||
} | ||||
else | ||||
{ | ||||
std::transform(std::begin(*line), std::end(*line), | ||||
std::cbegin(line_values), std::begin(line_values), | ||||
[](const auto& input, auto output) { return input.v() + output; }); | ||||
} | ||||
avg_coef += 1.; | ||||
r1465 | } | |||
else | ||||
{ | ||||
r1467 | for (int y_index = 0; y_index < colormap_properties.v_size_px; y_index++) | |||
r1464 | { | |||
r1467 | if (avg_coef > 0.) | |||
{ | ||||
has_nan = true; | ||||
std::fill( | ||||
std::begin(line_values), std::end(line_values), std::nan("")); | ||||
} | ||||
r1464 | } | |||
} | ||||
r1465 | current_time += xAxisProperties.max_resolution; | |||
r1464 | } | |||
} | ||||
r1466 | colormap->rescaleDataRange(true); | |||
r1465 | if (rescaleAxes) | |||
{ | ||||
plot->rescaleAxes(); | ||||
} | ||||
r1464 | } | |||
r1420 | } | |||
}; | ||||
Alexandre Leroux
|
r583 | /** | ||
* Helper used to create/update plottables | ||||
*/ | ||||
r1420 | struct IPlottablesHelper | |||
{ | ||||
Alexandre Leroux
|
r583 | virtual ~IPlottablesHelper() noexcept = default; | ||
r1420 | virtual PlottablesMap create(QCustomPlot& plot) const = 0; | |||
virtual void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const = 0; | ||||
virtual void update( | ||||
PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes = false) const = 0; | ||||
Alexandre Leroux
|
r583 | }; | ||
/** | ||||
r1465 | * Default implementation of IPlottablesHelper, which takes data series to create/update | |||
* plottables | ||||
Alexandre Leroux
|
r583 | * @tparam T the data series' type | ||
*/ | ||||
template <typename T> | ||||
r1420 | struct PlottablesHelper : public IPlottablesHelper | |||
{ | ||||
r1421 | explicit PlottablesHelper(std::shared_ptr<T> dataSeries) : m_DataSeries { dataSeries } {} | |||
Alexandre Leroux
|
r583 | |||
r1420 | PlottablesMap create(QCustomPlot& plot) const override | |||
Alexandre Leroux
|
r583 | { | ||
r1431 | return PlottablesCreator<T>::createPlottables(plot, m_DataSeries); | |||
Alexandre Leroux
|
r182 | } | ||
r1420 | void update( | |||
PlottablesMap& plottables, const DateTimeRange& range, bool rescaleAxes) const override | ||||
Alexandre Leroux
|
r583 | { | ||
r1420 | if (m_DataSeries) | |||
{ | ||||
Alexandre Leroux
|
r1280 | PlottablesUpdater<T>::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes); | ||
} | ||||
r1420 | else | |||
{ | ||||
Alexandre Leroux
|
r1280 | qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency " | ||
"between the type of data series and the " | ||||
"type supposed"; | ||||
} | ||||
Alexandre Leroux
|
r583 | } | ||
Alexandre Leroux
|
r182 | |||
r1420 | void setYAxisRange(const DateTimeRange& xAxisRange, QCustomPlot& plot) const override | |||
Alexandre Leroux
|
r900 | { | ||
r1420 | if (m_DataSeries) | |||
{ | ||||
Alexandre Leroux
|
r1280 | PlottablesUpdater<T>::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot); | ||
} | ||||
r1420 | else | |||
{ | ||||
Alexandre Leroux
|
r1280 | qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency " | ||
"between the type of data series and the " | ||||
"type supposed"; | ||||
} | ||||
Alexandre Leroux
|
r900 | } | ||
r1421 | std::shared_ptr<T> m_DataSeries; | |||
Alexandre Leroux
|
r583 | }; | ||
Alexandre Leroux
|
r182 | |||
Alexandre Leroux
|
r1280 | /// Creates IPlottablesHelper according to the type of data series a variable holds | ||
r1420 | std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<Variable2> variable) noexcept | |||
r548 | { | |||
r1420 | switch (variable->type()) | |||
{ | ||||
Alexandre Leroux
|
r1280 | case DataSeriesType::SCALAR: | ||
r1420 | return std::make_unique<PlottablesHelper<ScalarTimeSerie>>( | |||
r1421 | std::dynamic_pointer_cast<ScalarTimeSerie>(variable->data())); | |||
Alexandre Leroux
|
r1280 | case DataSeriesType::SPECTROGRAM: | ||
r1420 | return std::make_unique<PlottablesHelper<SpectrogramTimeSerie>>( | |||
r1421 | std::dynamic_pointer_cast<SpectrogramTimeSerie>(variable->data())); | |||
Alexandre Leroux
|
r1280 | case DataSeriesType::VECTOR: | ||
r1420 | return std::make_unique<PlottablesHelper<VectorTimeSerie>>( | |||
r1421 | std::dynamic_pointer_cast<VectorTimeSerie>(variable->data())); | |||
r1431 | case DataSeriesType::MULTICOMPONENT: | |||
return std::make_unique<PlottablesHelper<MultiComponentTimeSerie>>( | ||||
std::dynamic_pointer_cast<MultiComponentTimeSerie>(variable->data())); | ||||
Alexandre Leroux
|
r1280 | default: | ||
// Creates default helper | ||||
break; | ||||
r548 | } | |||
Alexandre Leroux
|
r1280 | |||
r1420 | return std::make_unique<PlottablesHelper<TimeSeries::ITimeSerie>>(nullptr); | |||
r548 | } | |||
Alexandre Leroux
|
r583 | } // namespace | ||
Alexandre Leroux
|
r181 | |||
r1420 | PlottablesMap VisualizationGraphHelper::create( | |||
std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept | ||||
Alexandre Leroux
|
r583 | { | ||
r1420 | if (variable) | |||
{ | ||||
Alexandre Leroux
|
r1280 | auto helper = createHelper(variable); | ||
Alexandre Leroux
|
r583 | auto plottables = helper->create(plot); | ||
return plottables; | ||||
Alexandre Leroux
|
r181 | } | ||
r1420 | else | |||
{ | ||||
r243 | qCDebug(LOG_VisualizationGraphHelper()) | |||
Alexandre Leroux
|
r181 | << QObject::tr("Can't create graph plottables : the variable is null"); | ||
r1420 | return PlottablesMap {}; | |||
Alexandre Leroux
|
r181 | } | ||
} | ||||
r235 | ||||
r1420 | void VisualizationGraphHelper::setYAxisRange( | |||
std::shared_ptr<Variable2> variable, QCustomPlot& plot) noexcept | ||||
Alexandre Leroux
|
r900 | { | ||
r1420 | if (variable) | |||
{ | ||||
Alexandre Leroux
|
r1280 | auto helper = createHelper(variable); | ||
Alexandre Leroux
|
r900 | helper->setYAxisRange(variable->range(), plot); | ||
} | ||||
r1420 | else | |||
{ | ||||
Alexandre Leroux
|
r900 | qCDebug(LOG_VisualizationGraphHelper()) | ||
<< QObject::tr("Can't set y-axis range of plot: the variable is null"); | ||||
} | ||||
} | ||||
r1420 | void VisualizationGraphHelper::updateData( | |||
PlottablesMap& plottables, std::shared_ptr<Variable2> variable, const DateTimeRange& dateTime) | ||||
r235 | { | |||
Alexandre Leroux
|
r1280 | auto helper = createHelper(variable); | ||
Alexandre Leroux
|
r583 | helper->update(plottables, dateTime); | ||
r235 | } | |||