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