##// END OF EJS Templates
Implements spectrograms display (3)...
Alexandre Leroux -
r907:b4b8a9876f21
parent child
Show More
@@ -1,331 +1,342
1 1 #include "Visualization/VisualizationGraphHelper.h"
2 2 #include "Visualization/qcustomplot.h"
3 3
4 4 #include <Common/ColorUtils.h>
5 5
6 6 #include <Data/ScalarSeries.h>
7 7 #include <Data/SpectrogramSeries.h>
8 8 #include <Data/VectorSeries.h>
9 9
10 10 #include <Variable/Variable.h>
11 11
12 12 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
13 13
14 14 namespace {
15 15
16 16 class SqpDataContainer : public QCPGraphDataContainer {
17 17 public:
18 18 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
19 19 };
20 20
21 21 /**
22 22 * Struct used to create plottables, depending on the type of the data series from which to create
23 23 * them
24 24 * @tparam T the data series' type
25 25 * @remarks Default implementation can't create plottables
26 26 */
27 27 template <typename T, typename Enabled = void>
28 28 struct PlottablesCreator {
29 29 static PlottablesMap createPlottables(T &, QCustomPlot &)
30 30 {
31 31 qCCritical(LOG_DataSeries())
32 32 << QObject::tr("Can't create plottables: unmanaged data series type");
33 33 return {};
34 34 }
35 35 };
36 36
37 37 /**
38 38 * Specialization of PlottablesCreator for scalars and vectors
39 39 * @sa ScalarSeries
40 40 * @sa VectorSeries
41 41 */
42 42 template <typename T>
43 43 struct PlottablesCreator<T,
44 44 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
45 45 or std::is_base_of<VectorSeries, T>::value> > {
46 46 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
47 47 {
48 48 PlottablesMap result{};
49 49
50 50 // Gets the number of components of the data series
51 51 dataSeries.lockRead();
52 52 auto componentCount = dataSeries.valuesData()->componentCount();
53 53 dataSeries.unlock();
54 54
55 55 auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount);
56 56
57 57 // For each component of the data series, creates a QCPGraph to add to the plot
58 58 for (auto i = 0; i < componentCount; ++i) {
59 59 auto graph = plot.addGraph();
60 60 graph->setPen(QPen{colors.at(i)});
61 61
62 62 result.insert({i, graph});
63 63 }
64 64
65 65 plot.replot();
66 66
67 67 return result;
68 68 }
69 69 };
70 70
71 71 /**
72 72 * Specialization of PlottablesCreator for spectrograms
73 73 * @sa SpectrogramSeries
74 74 */
75 75 template <typename T>
76 76 struct PlottablesCreator<T,
77 77 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
78 78 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
79 79 {
80 80 PlottablesMap result{};
81 81 result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}});
82 82
83 83 plot.replot();
84 84
85 85 return result;
86 86 }
87 87 };
88 88
89 89 /**
90 90 * Struct used to update plottables, depending on the type of the data series from which to update
91 91 * them
92 92 * @tparam T the data series' type
93 93 * @remarks Default implementation can't update plottables
94 94 */
95 95 template <typename T, typename Enabled = void>
96 96 struct PlottablesUpdater {
97 97 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
98 98 {
99 99 qCCritical(LOG_DataSeries())
100 100 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
101 101 }
102 102
103 103 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
104 104 {
105 105 qCCritical(LOG_DataSeries())
106 106 << QObject::tr("Can't update plottables: unmanaged data series type");
107 107 }
108 108 };
109 109
110 110 /**
111 111 * Specialization of PlottablesUpdater for scalars and vectors
112 112 * @sa ScalarSeries
113 113 * @sa VectorSeries
114 114 */
115 115 template <typename T>
116 116 struct PlottablesUpdater<T,
117 117 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
118 118 or std::is_base_of<VectorSeries, T>::value> > {
119 119 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
120 120 {
121 121 auto minValue = 0., maxValue = 0.;
122 122
123 123 dataSeries.lockRead();
124 124 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
125 125 auto end = dataSeries.cend();
126 126 if (valuesBounds.first != end && valuesBounds.second != end) {
127 127 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
128 128
129 129 minValue = rangeValue(valuesBounds.first->minValue());
130 130 maxValue = rangeValue(valuesBounds.second->maxValue());
131 131 }
132 132 dataSeries.unlock();
133 133
134 134 plot.yAxis->setRange(QCPRange{minValue, maxValue});
135 135 }
136 136
137 137 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
138 138 bool rescaleAxes)
139 139 {
140 140
141 141 // For each plottable to update, resets its data
142 142 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
143 143 for (const auto &plottable : plottables) {
144 144 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
145 145 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
146 146 graph->setData(dataContainer);
147 147
148 148 dataContainers.insert({plottable.first, dataContainer});
149 149 }
150 150 }
151 151 dataSeries.lockRead();
152 152
153 153 // - Gets the data of the series included in the current range
154 154 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
155 155 // and value data. The correct value is retrieved according to the index of the component
156 156 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
157 157 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
158 158 for (const auto &dataContainer : dataContainers) {
159 159 auto componentIndex = dataContainer.first;
160 160 dataContainer.second->appendGraphData(
161 161 QCPGraphData(it->x(), it->value(componentIndex)));
162 162 }
163 163 }
164 164
165 165 dataSeries.unlock();
166 166
167 167 if (!plottables.empty()) {
168 168 auto plot = plottables.begin()->second->parentPlot();
169 169
170 170 if (rescaleAxes) {
171 171 plot->rescaleAxes();
172 172 }
173 173
174 174 plot->replot();
175 175 }
176 176 }
177 177 };
178 178
179 179 /**
180 180 * Specialization of PlottablesUpdater for spectrograms
181 181 * @sa SpectrogramSeries
182 182 */
183 183 template <typename T>
184 184 struct PlottablesUpdater<T,
185 185 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
186 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
187 {
188 double min, max;
189 /// @todo ALX: use iterators here
190 std::tie(min, max) = dataSeries.yAxis().bounds();
191
192 if (!std::isnan(min) && !std::isnan(max)) {
193 plot.yAxis->setRange(QCPRange{min, max});
194 }
195 }
196
186 197 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
187 198 bool rescaleAxes)
188 199 {
189 200 if (plottables.empty()) {
190 201 qCDebug(LOG_VisualizationGraphHelper())
191 202 << QObject::tr("Can't update spectrogram: no colormap has been associated");
192 203 return;
193 204 }
194 205
195 206 // Gets the colormap to update (normally there is only one colormap)
196 207 Q_ASSERT(plottables.size() == 1);
197 208 auto colormap = dynamic_cast<QCPColorMap *>(plottables.at(0));
198 209 Q_ASSERT(colormap != nullptr);
199 210
200 211 dataSeries.lockRead();
201 212
202 213 auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
203 214 /// @todo ALX: use iterators here
204 215 auto yAxis = dataSeries.yAxis();
205 216
206 217 // Gets properties of x-axis and y-axis to set size and range of the colormap
207 218 auto nbX = std::distance(its.first, its.second);
208 219 auto xMin = nbX != 0 ? its.first->x() : 0.;
209 220 auto xMax = nbX != 0 ? (its.second - 1)->x() : 0.;
210 221
211 222 auto nbY = yAxis.size();
212 223 auto yMin = 0., yMax = 0.;
213 224 if (nbY != 0) {
214 225 std::tie(yMin, yMax) = yAxis.bounds();
215 226 }
216 227
217 228 colormap->data()->setSize(nbX, nbY);
218 229 colormap->data()->setRange(QCPRange{xMin, xMax}, QCPRange{yMin, yMax});
219 230
220 231 // Sets values
221 232 auto xIndex = 0;
222 233 for (auto it = its.first; it != its.second; ++it, ++xIndex) {
223 234 for (auto yIndex = 0; yIndex < nbY; ++yIndex) {
224 235 colormap->data()->setCell(xIndex, yIndex, it->value(yIndex));
225 236 }
226 237 }
227 238
228 239 dataSeries.unlock();
229 240
230 241 // Rescales axes
231 242 auto plot = colormap->parentPlot();
232 243
233 244 if (rescaleAxes) {
234 245 plot->rescaleAxes();
235 246 }
236 247
237 248 plot->replot();
238 249 }
239 250 };
240 251
241 252 /**
242 253 * Helper used to create/update plottables
243 254 */
244 255 struct IPlottablesHelper {
245 256 virtual ~IPlottablesHelper() noexcept = default;
246 257 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
247 258 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
248 259 virtual void update(PlottablesMap &plottables, const SqpRange &range,
249 260 bool rescaleAxes = false) const = 0;
250 261 };
251 262
252 263 /**
253 264 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
254 265 * @tparam T the data series' type
255 266 */
256 267 template <typename T>
257 268 struct PlottablesHelper : public IPlottablesHelper {
258 269 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
259 270
260 271 PlottablesMap create(QCustomPlot &plot) const override
261 272 {
262 273 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
263 274 }
264 275
265 276 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
266 277 {
267 278 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
268 279 }
269 280
270 281 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
271 282 {
272 283 return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot);
273 284 }
274 285
275 286 T &m_DataSeries;
276 287 };
277 288
278 289 /// Creates IPlottablesHelper according to a data series
279 290 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
280 291 {
281 292 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
282 293 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
283 294 }
284 295 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
285 296 return std::make_unique<PlottablesHelper<SpectrogramSeries> >(*spectrogramSeries);
286 297 }
287 298 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
288 299 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
289 300 }
290 301 else {
291 302 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
292 303 }
293 304 }
294 305
295 306 } // namespace
296 307
297 308 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
298 309 QCustomPlot &plot) noexcept
299 310 {
300 311 if (variable) {
301 312 auto helper = createHelper(variable->dataSeries());
302 313 auto plottables = helper->create(plot);
303 314 return plottables;
304 315 }
305 316 else {
306 317 qCDebug(LOG_VisualizationGraphHelper())
307 318 << QObject::tr("Can't create graph plottables : the variable is null");
308 319 return PlottablesMap{};
309 320 }
310 321 }
311 322
312 323 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
313 324 QCustomPlot &plot) noexcept
314 325 {
315 326 if (variable) {
316 327 auto helper = createHelper(variable->dataSeries());
317 328 helper->setYAxisRange(variable->range(), plot);
318 329 }
319 330 else {
320 331 qCDebug(LOG_VisualizationGraphHelper())
321 332 << QObject::tr("Can't set y-axis range of plot: the variable is null");
322 333 }
323 334 }
324 335
325 336 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
326 337 std::shared_ptr<IDataSeries> dataSeries,
327 338 const SqpRange &dateTime)
328 339 {
329 340 auto helper = createHelper(dataSeries);
330 341 helper->update(plottables, dateTime);
331 342 }
General Comments 0
You need to be logged in to leave comments. Login now