##// END OF EJS Templates
Implements spectrograms display (1)...
Alexandre Leroux -
r905:aa0c1de0bcf6
parent child
Show More
@@ -1,482 +1,483
1 #ifndef SCIQLOP_DATASERIES_H
1 #ifndef SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Common/SortUtils.h>
6 #include <Common/SortUtils.h>
7
7
8 #include <Data/ArrayData.h>
8 #include <Data/ArrayData.h>
9 #include <Data/DataSeriesMergeHelper.h>
9 #include <Data/DataSeriesMergeHelper.h>
10 #include <Data/IDataSeries.h>
10 #include <Data/IDataSeries.h>
11 #include <Data/OptionalAxis.h>
11 #include <Data/OptionalAxis.h>
12
12
13 #include <QLoggingCategory>
13 #include <QLoggingCategory>
14 #include <QReadLocker>
14 #include <QReadLocker>
15 #include <QReadWriteLock>
15 #include <QReadWriteLock>
16 #include <memory>
16 #include <memory>
17
17
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
19 // definitions with inheritance. Inline method is used instead
19 // definitions with inheritance. Inline method is used instead
20 inline const QLoggingCategory &LOG_DataSeries()
20 inline const QLoggingCategory &LOG_DataSeries()
21 {
21 {
22 static const QLoggingCategory category{"DataSeries"};
22 static const QLoggingCategory category{"DataSeries"};
23 return category;
23 return category;
24 }
24 }
25
25
26 template <int Dim>
26 template <int Dim>
27 class DataSeries;
27 class DataSeries;
28
28
29 namespace dataseries_detail {
29 namespace dataseries_detail {
30
30
31 template <int Dim, bool IsConst>
31 template <int Dim, bool IsConst>
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
33 public:
33 public:
34 friend class DataSeries<Dim>;
34 friend class DataSeries<Dim>;
35
35
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
40 {
40 {
41 }
41 }
42
42
43 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
43 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
44 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
44 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
45 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
45 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
46 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
46 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
47 : dataSeries.valuesData()->cend())
47 : dataSeries.valuesData()->cend())
48 {
48 {
49 }
49 }
50
50
51 IteratorValue(const IteratorValue &other) = default;
51 IteratorValue(const IteratorValue &other) = default;
52
52
53 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
53 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
54 {
54 {
55 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
55 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
56 }
56 }
57
57
58 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
58 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
59 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
60 return m_XIt->distance(*otherImpl.m_XIt);
60 return m_XIt->distance(*otherImpl.m_XIt);
61 }
61 }
62 catch (const std::bad_cast &) {
62 catch (const std::bad_cast &) {
63 return 0;
63 return 0;
64 }
64 }
65
65
66 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
66 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
67 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
67 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
68 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
68 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
69 }
69 }
70 catch (const std::bad_cast &) {
70 catch (const std::bad_cast &) {
71 return false;
71 return false;
72 }
72 }
73
73
74 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
74 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
75 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
75 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
76 return m_XIt->lowerThan(*otherImpl.m_XIt);
76 return m_XIt->lowerThan(*otherImpl.m_XIt);
77 }
77 }
78 catch (const std::bad_cast &) {
78 catch (const std::bad_cast &) {
79 return false;
79 return false;
80 }
80 }
81
81
82 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
82 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
83 {
83 {
84 auto result = clone();
84 auto result = clone();
85 result->next(offset);
85 result->next(offset);
86 return result;
86 return result;
87 }
87 }
88
88
89 void next(int offset) override
89 void next(int offset) override
90 {
90 {
91 m_XIt->next(offset);
91 m_XIt->next(offset);
92 m_ValuesIt->next(offset);
92 m_ValuesIt->next(offset);
93 }
93 }
94
94
95 void prev() override
95 void prev() override
96 {
96 {
97 --m_XIt;
97 --m_XIt;
98 --m_ValuesIt;
98 --m_ValuesIt;
99 }
99 }
100
100
101 double x() const override { return m_XIt->at(0); }
101 double x() const override { return m_XIt->at(0); }
102 double value() const override { return m_ValuesIt->at(0); }
102 double value() const override { return m_ValuesIt->at(0); }
103 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
103 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
104 double minValue() const override { return m_ValuesIt->min(); }
104 double minValue() const override { return m_ValuesIt->min(); }
105 double maxValue() const override { return m_ValuesIt->max(); }
105 double maxValue() const override { return m_ValuesIt->max(); }
106 QVector<double> values() const override { return m_ValuesIt->values(); }
106 QVector<double> values() const override { return m_ValuesIt->values(); }
107
107
108 void swap(DataSeriesIteratorValue::Impl &other) override
108 void swap(DataSeriesIteratorValue::Impl &other) override
109 {
109 {
110 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
110 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
111 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
111 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
112 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
112 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
113 }
113 }
114
114
115 private:
115 private:
116 ArrayDataIterator m_XIt;
116 ArrayDataIterator m_XIt;
117 ArrayDataIterator m_ValuesIt;
117 ArrayDataIterator m_ValuesIt;
118 };
118 };
119 } // namespace dataseries_detail
119 } // namespace dataseries_detail
120
120
121 /**
121 /**
122 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
122 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
123 *
123 *
124 * The DataSeries represents values on one or two axes, according to these rules:
124 * The DataSeries represents values on one or two axes, according to these rules:
125 * - the x-axis is always defined
125 * - the x-axis is always defined
126 * - an y-axis can be defined or not. If set, additional consistency checks apply to the values (see
126 * - an y-axis can be defined or not. If set, additional consistency checks apply to the values (see
127 * below)
127 * below)
128 * - the values are defined on one or two dimensions. In the case of 2-dim values, the data is
128 * - the values are defined on one or two dimensions. In the case of 2-dim values, the data is
129 * distributed into components (for example, a vector defines three components)
129 * distributed into components (for example, a vector defines three components)
130 * - New values can be added to the series, on the x-axis.
130 * - New values can be added to the series, on the x-axis.
131 * - Once initialized to the series creation, the y-axis (if defined) is no longer modifiable
131 * - Once initialized to the series creation, the y-axis (if defined) is no longer modifiable
132 * - Data representing values and axes are associated with a unit
132 * - Data representing values and axes are associated with a unit
133 * - The data series is always sorted in ascending order on the x-axis.
133 * - The data series is always sorted in ascending order on the x-axis.
134 *
134 *
135 * Consistency checks are carried out between the axes and the values. These controls are provided
135 * Consistency checks are carried out between the axes and the values. These controls are provided
136 * throughout the DataSeries lifecycle:
136 * throughout the DataSeries lifecycle:
137 * - the number of data on the x-axis must be equal to the number of values (in the case of
137 * - the number of data on the x-axis must be equal to the number of values (in the case of
138 * 2-dim ArrayData for values, the test is performed on the number of values per component)
138 * 2-dim ArrayData for values, the test is performed on the number of values per component)
139 * - if the y-axis is defined, the number of components of the ArrayData for values must equal the
139 * - if the y-axis is defined, the number of components of the ArrayData for values must equal the
140 * number of data on the y-axis.
140 * number of data on the y-axis.
141 *
141 *
142 * Examples:
142 * Examples:
143 * 1)
143 * 1)
144 * - x-axis: [1 ; 2 ; 3]
144 * - x-axis: [1 ; 2 ; 3]
145 * - y-axis: not defined
145 * - y-axis: not defined
146 * - values: [10 ; 20 ; 30] (1-dim ArrayData)
146 * - values: [10 ; 20 ; 30] (1-dim ArrayData)
147 * => the DataSeries is valid, as x-axis and values have the same number of data
147 * => the DataSeries is valid, as x-axis and values have the same number of data
148 *
148 *
149 * 2)
149 * 2)
150 * - x-axis: [1 ; 2 ; 3]
150 * - x-axis: [1 ; 2 ; 3]
151 * - y-axis: not defined
151 * - y-axis: not defined
152 * - values: [10 ; 20 ; 30 ; 40] (1-dim ArrayData)
152 * - values: [10 ; 20 ; 30 ; 40] (1-dim ArrayData)
153 * => the DataSeries is invalid, as x-axis and values haven't the same number of data
153 * => the DataSeries is invalid, as x-axis and values haven't the same number of data
154 *
154 *
155 * 3)
155 * 3)
156 * - x-axis: [1 ; 2 ; 3]
156 * - x-axis: [1 ; 2 ; 3]
157 * - y-axis: not defined
157 * - y-axis: not defined
158 * - values: [10 ; 20 ; 30
158 * - values: [10 ; 20 ; 30
159 * 40 ; 50 ; 60] (2-dim ArrayData)
159 * 40 ; 50 ; 60] (2-dim ArrayData)
160 * => the DataSeries is valid, as x-axis has 3 data and values contains 2 components with 3
160 * => the DataSeries is valid, as x-axis has 3 data and values contains 2 components with 3
161 * data each
161 * data each
162 *
162 *
163 * 4)
163 * 4)
164 * - x-axis: [1 ; 2 ; 3]
164 * - x-axis: [1 ; 2 ; 3]
165 * - y-axis: [1 ; 2]
165 * - y-axis: [1 ; 2]
166 * - values: [10 ; 20 ; 30
166 * - values: [10 ; 20 ; 30
167 * 40 ; 50 ; 60] (2-dim ArrayData)
167 * 40 ; 50 ; 60] (2-dim ArrayData)
168 * => the DataSeries is valid, as:
168 * => the DataSeries is valid, as:
169 * - x-axis has 3 data and values contains 2 components with 3 data each AND
169 * - x-axis has 3 data and values contains 2 components with 3 data each AND
170 * - y-axis has 2 data and values contains 2 components
170 * - y-axis has 2 data and values contains 2 components
171 *
171 *
172 * 5)
172 * 5)
173 * - x-axis: [1 ; 2 ; 3]
173 * - x-axis: [1 ; 2 ; 3]
174 * - y-axis: [1 ; 2 ; 3]
174 * - y-axis: [1 ; 2 ; 3]
175 * - values: [10 ; 20 ; 30
175 * - values: [10 ; 20 ; 30
176 * 40 ; 50 ; 60] (2-dim ArrayData)
176 * 40 ; 50 ; 60] (2-dim ArrayData)
177 * => the DataSeries is invalid, as:
177 * => the DataSeries is invalid, as:
178 * - x-axis has 3 data and values contains 2 components with 3 data each BUT
178 * - x-axis has 3 data and values contains 2 components with 3 data each BUT
179 * - y-axis has 3 data and values contains only 2 components
179 * - y-axis has 3 data and values contains only 2 components
180 *
180 *
181 * @tparam Dim The dimension of the values data
181 * @tparam Dim The dimension of the values data
182 *
182 *
183 */
183 */
184 template <int Dim>
184 template <int Dim>
185 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
185 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
186 friend class DataSeriesMergeHelper;
186 friend class DataSeriesMergeHelper;
187
187
188 public:
188 public:
189 /// @sa IDataSeries::xAxisData()
189 /// @sa IDataSeries::xAxisData()
190 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
190 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
191 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
191 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
192
192
193 /// @sa IDataSeries::xAxisUnit()
193 /// @sa IDataSeries::xAxisUnit()
194 Unit xAxisUnit() const override { return m_XAxisUnit; }
194 Unit xAxisUnit() const override { return m_XAxisUnit; }
195
195
196 /// @return the values dataset
196 /// @return the values dataset
197 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
197 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
198 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
198 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
199
199
200 /// @sa IDataSeries::valuesUnit()
200 /// @sa IDataSeries::valuesUnit()
201 Unit valuesUnit() const override { return m_ValuesUnit; }
201 Unit valuesUnit() const override { return m_ValuesUnit; }
202
202
203 int nbPoints() const override { return m_ValuesData->totalSize(); }
203 int nbPoints() const override { return m_ValuesData->totalSize(); }
204
204
205 void clear()
205 void clear()
206 {
206 {
207 m_XAxisData->clear();
207 m_XAxisData->clear();
208 m_ValuesData->clear();
208 m_ValuesData->clear();
209 }
209 }
210
210
211 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
211 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
212
212
213 /// Merges into the data series an other data series.
213 /// Merges into the data series an other data series.
214 ///
214 ///
215 /// The two dataseries:
215 /// The two dataseries:
216 /// - must be of the same dimension
216 /// - must be of the same dimension
217 /// - must have the same y-axis (if defined)
217 /// - must have the same y-axis (if defined)
218 ///
218 ///
219 /// If the prerequisites are not valid, the method does nothing
219 /// If the prerequisites are not valid, the method does nothing
220 ///
220 ///
221 /// @remarks the data series to merge with is cleared after the operation
221 /// @remarks the data series to merge with is cleared after the operation
222 void merge(IDataSeries *dataSeries) override
222 void merge(IDataSeries *dataSeries) override
223 {
223 {
224 dataSeries->lockWrite();
224 dataSeries->lockWrite();
225 lockWrite();
225 lockWrite();
226
226
227 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
227 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
228 if (m_YAxis == other->m_YAxis) {
228 if (m_YAxis == other->m_YAxis) {
229 DataSeriesMergeHelper::merge(*other, *this);
229 DataSeriesMergeHelper::merge(*other, *this);
230 }
230 }
231 else {
231 else {
232 qCWarning(LOG_DataSeries())
232 qCWarning(LOG_DataSeries())
233 << QObject::tr("Can't merge data series that have not the same y-axis");
233 << QObject::tr("Can't merge data series that have not the same y-axis");
234 }
234 }
235 }
235 }
236 else {
236 else {
237 qCWarning(LOG_DataSeries())
237 qCWarning(LOG_DataSeries())
238 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
238 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
239 }
239 }
240 unlock();
240 unlock();
241 dataSeries->unlock();
241 dataSeries->unlock();
242 }
242 }
243
243
244 void purge(double min, double max) override
244 void purge(double min, double max) override
245 {
245 {
246 // Nothing to purge if series is empty
246 // Nothing to purge if series is empty
247 if (isEmpty()) {
247 if (isEmpty()) {
248 return;
248 return;
249 }
249 }
250
250
251 if (min > max) {
251 if (min > max) {
252 std::swap(min, max);
252 std::swap(min, max);
253 }
253 }
254
254
255 // Nothing to purge if series min/max are inside purge range
255 // Nothing to purge if series min/max are inside purge range
256 auto xMin = cbegin()->x();
256 auto xMin = cbegin()->x();
257 auto xMax = (--cend())->x();
257 auto xMax = (--cend())->x();
258 if (xMin >= min && xMax <= max) {
258 if (xMin >= min && xMax <= max) {
259 return;
259 return;
260 }
260 }
261
261
262 auto lowerIt = std::lower_bound(
262 auto lowerIt = std::lower_bound(
263 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
263 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
264 erase(begin(), lowerIt);
264 erase(begin(), lowerIt);
265 auto upperIt = std::upper_bound(
265 auto upperIt = std::upper_bound(
266 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
266 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
267 erase(upperIt, end());
267 erase(upperIt, end());
268 }
268 }
269
269
270 // ///////// //
270 // ///////// //
271 // Iterators //
271 // Iterators //
272 // ///////// //
272 // ///////// //
273
273
274 DataSeriesIterator begin() override
274 DataSeriesIterator begin() override
275 {
275 {
276 return DataSeriesIterator{DataSeriesIteratorValue{
276 return DataSeriesIterator{DataSeriesIteratorValue{
277 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
277 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
278 }
278 }
279
279
280 DataSeriesIterator end() override
280 DataSeriesIterator end() override
281 {
281 {
282 return DataSeriesIterator{DataSeriesIteratorValue{
282 return DataSeriesIterator{DataSeriesIteratorValue{
283 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
283 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
284 }
284 }
285
285
286 DataSeriesIterator cbegin() const override
286 DataSeriesIterator cbegin() const override
287 {
287 {
288 return DataSeriesIterator{DataSeriesIteratorValue{
288 return DataSeriesIterator{DataSeriesIteratorValue{
289 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
289 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
290 }
290 }
291
291
292 DataSeriesIterator cend() const override
292 DataSeriesIterator cend() const override
293 {
293 {
294 return DataSeriesIterator{DataSeriesIteratorValue{
294 return DataSeriesIterator{DataSeriesIteratorValue{
295 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
295 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
296 }
296 }
297
297
298 void erase(DataSeriesIterator first, DataSeriesIterator last)
298 void erase(DataSeriesIterator first, DataSeriesIterator last)
299 {
299 {
300 auto firstImpl
300 auto firstImpl
301 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
301 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
302 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
302 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
303
303
304 if (firstImpl && lastImpl) {
304 if (firstImpl && lastImpl) {
305 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
305 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
306 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
306 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
307 }
307 }
308 }
308 }
309
309
310 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
310 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
311 {
311 {
312 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
312 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
313 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
313 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
314
314
315 if (firstImpl && lastImpl) {
315 if (firstImpl && lastImpl) {
316 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
316 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
317 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
317 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
318 }
318 }
319 }
319 }
320
320
321 /// @sa IDataSeries::minXAxisData()
321 /// @sa IDataSeries::minXAxisData()
322 DataSeriesIterator minXAxisData(double minXAxisData) const override
322 DataSeriesIterator minXAxisData(double minXAxisData) const override
323 {
323 {
324 return std::lower_bound(
324 return std::lower_bound(
325 cbegin(), cend(), minXAxisData,
325 cbegin(), cend(), minXAxisData,
326 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
326 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
327 }
327 }
328
328
329 /// @sa IDataSeries::maxXAxisData()
329 /// @sa IDataSeries::maxXAxisData()
330 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
330 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
331 {
331 {
332 // Gets the first element that greater than max value
332 // Gets the first element that greater than max value
333 auto it = std::upper_bound(
333 auto it = std::upper_bound(
334 cbegin(), cend(), maxXAxisData,
334 cbegin(), cend(), maxXAxisData,
335 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
335 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
336
336
337 return it == cbegin() ? cend() : --it;
337 return it == cbegin() ? cend() : --it;
338 }
338 }
339
339
340 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
340 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
341 double maxXAxisData) const override
341 double maxXAxisData) const override
342 {
342 {
343 if (minXAxisData > maxXAxisData) {
343 if (minXAxisData > maxXAxisData) {
344 std::swap(minXAxisData, maxXAxisData);
344 std::swap(minXAxisData, maxXAxisData);
345 }
345 }
346
346
347 auto begin = cbegin();
347 auto begin = cbegin();
348 auto end = cend();
348 auto end = cend();
349
349
350 auto lowerIt = std::lower_bound(
350 auto lowerIt = std::lower_bound(
351 begin, end, minXAxisData,
351 begin, end, minXAxisData,
352 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
352 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
353 auto upperIt = std::upper_bound(
353 auto upperIt = std::upper_bound(
354 lowerIt, end, maxXAxisData,
354 lowerIt, end, maxXAxisData,
355 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
355 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
356
356
357 return std::make_pair(lowerIt, upperIt);
357 return std::make_pair(lowerIt, upperIt);
358 }
358 }
359
359
360 std::pair<DataSeriesIterator, DataSeriesIterator>
360 std::pair<DataSeriesIterator, DataSeriesIterator>
361 valuesBounds(double minXAxisData, double maxXAxisData) const override
361 valuesBounds(double minXAxisData, double maxXAxisData) const override
362 {
362 {
363 // Places iterators to the correct x-axis range
363 // Places iterators to the correct x-axis range
364 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
364 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
365
365
366 // Returns end iterators if the range is empty
366 // Returns end iterators if the range is empty
367 if (xAxisRangeIts.first == xAxisRangeIts.second) {
367 if (xAxisRangeIts.first == xAxisRangeIts.second) {
368 return std::make_pair(cend(), cend());
368 return std::make_pair(cend(), cend());
369 }
369 }
370
370
371 // Gets the iterator on the min of all values data
371 // Gets the iterator on the min of all values data
372 auto minIt = std::min_element(
372 auto minIt = std::min_element(
373 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
373 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
374 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
374 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
375 });
375 });
376
376
377 // Gets the iterator on the max of all values data
377 // Gets the iterator on the max of all values data
378 auto maxIt = std::max_element(
378 auto maxIt = std::max_element(
379 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
379 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
380 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
380 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
381 });
381 });
382
382
383 return std::make_pair(minIt, maxIt);
383 return std::make_pair(minIt, maxIt);
384 }
384 }
385
385
386 /// @return the y-axis associated to the data series
387 /// @todo pass getter as protected and use iterators to access the y-axis data
388 OptionalAxis yAxis() const { return m_YAxis; }
389
386 // /////// //
390 // /////// //
387 // Mutexes //
391 // Mutexes //
388 // /////// //
392 // /////// //
389
393
390 virtual void lockRead() { m_Lock.lockForRead(); }
394 virtual void lockRead() { m_Lock.lockForRead(); }
391 virtual void lockWrite() { m_Lock.lockForWrite(); }
395 virtual void lockWrite() { m_Lock.lockForWrite(); }
392 virtual void unlock() { m_Lock.unlock(); }
396 virtual void unlock() { m_Lock.unlock(); }
393
397
394 protected:
398 protected:
395 /// Protected ctor (DataSeries is abstract).
399 /// Protected ctor (DataSeries is abstract).
396 ///
400 ///
397 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
401 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
398 /// class description for consistent rules)
402 /// class description for consistent rules)
399 /// @remarks data series is automatically sorted on its x-axis data
403 /// @remarks data series is automatically sorted on its x-axis data
400 /// @throws std::invalid_argument if the data are inconsistent with each other
404 /// @throws std::invalid_argument if the data are inconsistent with each other
401 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
405 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
402 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
406 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
403 OptionalAxis yAxis = OptionalAxis{})
407 OptionalAxis yAxis = OptionalAxis{})
404 : m_XAxisData{xAxisData},
408 : m_XAxisData{xAxisData},
405 m_XAxisUnit{xAxisUnit},
409 m_XAxisUnit{xAxisUnit},
406 m_ValuesData{valuesData},
410 m_ValuesData{valuesData},
407 m_ValuesUnit{valuesUnit},
411 m_ValuesUnit{valuesUnit},
408 m_YAxis{std::move(yAxis)}
412 m_YAxis{std::move(yAxis)}
409 {
413 {
410 if (m_XAxisData->size() != m_ValuesData->size()) {
414 if (m_XAxisData->size() != m_ValuesData->size()) {
411 throw std::invalid_argument{
415 throw std::invalid_argument{
412 "The number of values by component must be equal to the number of x-axis data"};
416 "The number of values by component must be equal to the number of x-axis data"};
413 }
417 }
414
418
415 // Validates y-axis (if defined)
419 // Validates y-axis (if defined)
416 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
420 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
417 throw std::invalid_argument{
421 throw std::invalid_argument{
418 "As the y-axis is defined, the number of value components must be equal to the "
422 "As the y-axis is defined, the number of value components must be equal to the "
419 "number of y-axis data"};
423 "number of y-axis data"};
420 }
424 }
421
425
422 // Sorts data if it's not the case
426 // Sorts data if it's not the case
423 const auto &xAxisCData = m_XAxisData->cdata();
427 const auto &xAxisCData = m_XAxisData->cdata();
424 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
428 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
425 sort();
429 sort();
426 }
430 }
427 }
431 }
428
432
429 /// Copy ctor
433 /// Copy ctor
430 explicit DataSeries(const DataSeries<Dim> &other)
434 explicit DataSeries(const DataSeries<Dim> &other)
431 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
435 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
432 m_XAxisUnit{other.m_XAxisUnit},
436 m_XAxisUnit{other.m_XAxisUnit},
433 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
437 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
434 m_ValuesUnit{other.m_ValuesUnit},
438 m_ValuesUnit{other.m_ValuesUnit},
435 m_YAxis{other.m_YAxis}
439 m_YAxis{other.m_YAxis}
436 {
440 {
437 // Since a series is ordered from its construction and is always ordered, it is not
441 // Since a series is ordered from its construction and is always ordered, it is not
438 // necessary to call the sort method here ('other' is sorted)
442 // necessary to call the sort method here ('other' is sorted)
439 }
443 }
440
444
441 /// @return the y-axis associated to the data series
442 OptionalAxis yAxis() const { return m_YAxis; }
443
444 /// Assignment operator
445 /// Assignment operator
445 template <int D>
446 template <int D>
446 DataSeries &operator=(DataSeries<D> other)
447 DataSeries &operator=(DataSeries<D> other)
447 {
448 {
448 std::swap(m_XAxisData, other.m_XAxisData);
449 std::swap(m_XAxisData, other.m_XAxisData);
449 std::swap(m_XAxisUnit, other.m_XAxisUnit);
450 std::swap(m_XAxisUnit, other.m_XAxisUnit);
450 std::swap(m_ValuesData, other.m_ValuesData);
451 std::swap(m_ValuesData, other.m_ValuesData);
451 std::swap(m_ValuesUnit, other.m_ValuesUnit);
452 std::swap(m_ValuesUnit, other.m_ValuesUnit);
452 std::swap(m_YAxis, other.m_YAxis);
453 std::swap(m_YAxis, other.m_YAxis);
453
454
454 return *this;
455 return *this;
455 }
456 }
456
457
457 private:
458 private:
458 /**
459 /**
459 * Sorts data series on its x-axis data
460 * Sorts data series on its x-axis data
460 */
461 */
461 void sort() noexcept
462 void sort() noexcept
462 {
463 {
463 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
464 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
464 m_XAxisData = m_XAxisData->sort(permutation);
465 m_XAxisData = m_XAxisData->sort(permutation);
465 m_ValuesData = m_ValuesData->sort(permutation);
466 m_ValuesData = m_ValuesData->sort(permutation);
466 }
467 }
467
468
468 // x-axis
469 // x-axis
469 std::shared_ptr<ArrayData<1> > m_XAxisData;
470 std::shared_ptr<ArrayData<1> > m_XAxisData;
470 Unit m_XAxisUnit;
471 Unit m_XAxisUnit;
471
472
472 // values
473 // values
473 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
474 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
474 Unit m_ValuesUnit;
475 Unit m_ValuesUnit;
475
476
476 // y-axis (optional)
477 // y-axis (optional)
477 OptionalAxis m_YAxis;
478 OptionalAxis m_YAxis;
478
479
479 QReadWriteLock m_Lock;
480 QReadWriteLock m_Lock;
480 };
481 };
481
482
482 #endif // SCIQLOP_DATASERIES_H
483 #endif // SCIQLOP_DATASERIES_H
@@ -1,247 +1,269
1 #include "Visualization/VisualizationGraphHelper.h"
1 #include "Visualization/VisualizationGraphHelper.h"
2 #include "Visualization/qcustomplot.h"
2 #include "Visualization/qcustomplot.h"
3
3
4 #include <Common/ColorUtils.h>
4 #include <Common/ColorUtils.h>
5
5
6 #include <Data/ScalarSeries.h>
6 #include <Data/ScalarSeries.h>
7 #include <Data/SpectrogramSeries.h>
7 #include <Data/VectorSeries.h>
8 #include <Data/VectorSeries.h>
8
9
9 #include <Variable/Variable.h>
10 #include <Variable/Variable.h>
10
11
11 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
12 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
12
13
13 namespace {
14 namespace {
14
15
15 class SqpDataContainer : public QCPGraphDataContainer {
16 class SqpDataContainer : public QCPGraphDataContainer {
16 public:
17 public:
17 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
18 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
18 };
19 };
19
20
20 /**
21 /**
21 * Struct used to create plottables, depending on the type of the data series from which to create
22 * Struct used to create plottables, depending on the type of the data series from which to create
22 * them
23 * them
23 * @tparam T the data series' type
24 * @tparam T the data series' type
24 * @remarks Default implementation can't create plottables
25 * @remarks Default implementation can't create plottables
25 */
26 */
26 template <typename T, typename Enabled = void>
27 template <typename T, typename Enabled = void>
27 struct PlottablesCreator {
28 struct PlottablesCreator {
28 static PlottablesMap createPlottables(T &, QCustomPlot &)
29 static PlottablesMap createPlottables(T &, QCustomPlot &)
29 {
30 {
30 qCCritical(LOG_DataSeries())
31 qCCritical(LOG_DataSeries())
31 << QObject::tr("Can't create plottables: unmanaged data series type");
32 << QObject::tr("Can't create plottables: unmanaged data series type");
32 return {};
33 return {};
33 }
34 }
34 };
35 };
35
36
36 /**
37 /**
37 * Specialization of PlottablesCreator for scalars and vectors
38 * Specialization of PlottablesCreator for scalars and vectors
38 * @sa ScalarSeries
39 * @sa ScalarSeries
39 * @sa VectorSeries
40 * @sa VectorSeries
40 */
41 */
41 template <typename T>
42 template <typename T>
42 struct PlottablesCreator<T,
43 struct PlottablesCreator<T,
43 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
44 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
44 or std::is_base_of<VectorSeries, T>::value> > {
45 or std::is_base_of<VectorSeries, T>::value> > {
45 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
46 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
46 {
47 {
47 PlottablesMap result{};
48 PlottablesMap result{};
48
49
49 // Gets the number of components of the data series
50 // Gets the number of components of the data series
50 dataSeries.lockRead();
51 dataSeries.lockRead();
51 auto componentCount = dataSeries.valuesData()->componentCount();
52 auto componentCount = dataSeries.valuesData()->componentCount();
52 dataSeries.unlock();
53 dataSeries.unlock();
53
54
54 auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount);
55 auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount);
55
56
56 // For each component of the data series, creates a QCPGraph to add to the plot
57 // For each component of the data series, creates a QCPGraph to add to the plot
57 for (auto i = 0; i < componentCount; ++i) {
58 for (auto i = 0; i < componentCount; ++i) {
58 auto graph = plot.addGraph();
59 auto graph = plot.addGraph();
59 graph->setPen(QPen{colors.at(i)});
60 graph->setPen(QPen{colors.at(i)});
60
61
61 result.insert({i, graph});
62 result.insert({i, graph});
62 }
63 }
63
64
64 plot.replot();
65 plot.replot();
65
66
66 return result;
67 return result;
67 }
68 }
68 };
69 };
69
70
70 /**
71 /**
72 * Specialization of PlottablesCreator for spectrograms
73 * @sa SpectrogramSeries
74 */
75 template <typename T>
76 struct PlottablesCreator<T,
77 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
78 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
79 {
80 PlottablesMap result{};
81 result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}});
82
83 plot.replot();
84
85 return result;
86 }
87 };
88
89 /**
71 * Struct used to update plottables, depending on the type of the data series from which to update
90 * Struct used to update plottables, depending on the type of the data series from which to update
72 * them
91 * them
73 * @tparam T the data series' type
92 * @tparam T the data series' type
74 * @remarks Default implementation can't update plottables
93 * @remarks Default implementation can't update plottables
75 */
94 */
76 template <typename T, typename Enabled = void>
95 template <typename T, typename Enabled = void>
77 struct PlottablesUpdater {
96 struct PlottablesUpdater {
78 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
97 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
79 {
98 {
80 qCCritical(LOG_DataSeries())
99 qCCritical(LOG_DataSeries())
81 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
100 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
82 }
101 }
83
102
84 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
103 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
85 {
104 {
86 qCCritical(LOG_DataSeries())
105 qCCritical(LOG_DataSeries())
87 << QObject::tr("Can't update plottables: unmanaged data series type");
106 << QObject::tr("Can't update plottables: unmanaged data series type");
88 }
107 }
89 };
108 };
90
109
91 /**
110 /**
92 * Specialization of PlottablesUpdater for scalars and vectors
111 * Specialization of PlottablesUpdater for scalars and vectors
93 * @sa ScalarSeries
112 * @sa ScalarSeries
94 * @sa VectorSeries
113 * @sa VectorSeries
95 */
114 */
96 template <typename T>
115 template <typename T>
97 struct PlottablesUpdater<T,
116 struct PlottablesUpdater<T,
98 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
117 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
99 or std::is_base_of<VectorSeries, T>::value> > {
118 or std::is_base_of<VectorSeries, T>::value> > {
100 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
119 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
101 {
120 {
102 auto minValue = 0., maxValue = 0.;
121 auto minValue = 0., maxValue = 0.;
103
122
104 dataSeries.lockRead();
123 dataSeries.lockRead();
105 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
124 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
106 auto end = dataSeries.cend();
125 auto end = dataSeries.cend();
107 if (valuesBounds.first != end && valuesBounds.second != end) {
126 if (valuesBounds.first != end && valuesBounds.second != end) {
108 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
127 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
109
128
110 minValue = rangeValue(valuesBounds.first->minValue());
129 minValue = rangeValue(valuesBounds.first->minValue());
111 maxValue = rangeValue(valuesBounds.second->maxValue());
130 maxValue = rangeValue(valuesBounds.second->maxValue());
112 }
131 }
113 dataSeries.unlock();
132 dataSeries.unlock();
114
133
115 plot.yAxis->setRange(QCPRange{minValue, maxValue});
134 plot.yAxis->setRange(QCPRange{minValue, maxValue});
116 }
135 }
117
136
118 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
137 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
119 bool rescaleAxes)
138 bool rescaleAxes)
120 {
139 {
121
140
122 // For each plottable to update, resets its data
141 // For each plottable to update, resets its data
123 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
142 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
124 for (const auto &plottable : plottables) {
143 for (const auto &plottable : plottables) {
125 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
144 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
126 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
145 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
127 graph->setData(dataContainer);
146 graph->setData(dataContainer);
128
147
129 dataContainers.insert({plottable.first, dataContainer});
148 dataContainers.insert({plottable.first, dataContainer});
130 }
149 }
131 }
150 }
132 dataSeries.lockRead();
151 dataSeries.lockRead();
133
152
134 // - Gets the data of the series included in the current range
153 // - Gets the data of the series included in the current range
135 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
154 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
136 // and value data. The correct value is retrieved according to the index of the component
155 // and value data. The correct value is retrieved according to the index of the component
137 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
156 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
138 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
157 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
139 for (const auto &dataContainer : dataContainers) {
158 for (const auto &dataContainer : dataContainers) {
140 auto componentIndex = dataContainer.first;
159 auto componentIndex = dataContainer.first;
141 dataContainer.second->appendGraphData(
160 dataContainer.second->appendGraphData(
142 QCPGraphData(it->x(), it->value(componentIndex)));
161 QCPGraphData(it->x(), it->value(componentIndex)));
143 }
162 }
144 }
163 }
145
164
146 dataSeries.unlock();
165 dataSeries.unlock();
147
166
148 if (!plottables.empty()) {
167 if (!plottables.empty()) {
149 auto plot = plottables.begin()->second->parentPlot();
168 auto plot = plottables.begin()->second->parentPlot();
150
169
151 if (rescaleAxes) {
170 if (rescaleAxes) {
152 plot->rescaleAxes();
171 plot->rescaleAxes();
153 }
172 }
154
173
155 plot->replot();
174 plot->replot();
156 }
175 }
157 }
176 }
158 };
177 };
159
178
160 /**
179 /**
161 * Helper used to create/update plottables
180 * Helper used to create/update plottables
162 */
181 */
163 struct IPlottablesHelper {
182 struct IPlottablesHelper {
164 virtual ~IPlottablesHelper() noexcept = default;
183 virtual ~IPlottablesHelper() noexcept = default;
165 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
184 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
166 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
185 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
167 virtual void update(PlottablesMap &plottables, const SqpRange &range,
186 virtual void update(PlottablesMap &plottables, const SqpRange &range,
168 bool rescaleAxes = false) const = 0;
187 bool rescaleAxes = false) const = 0;
169 };
188 };
170
189
171 /**
190 /**
172 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
191 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
173 * @tparam T the data series' type
192 * @tparam T the data series' type
174 */
193 */
175 template <typename T>
194 template <typename T>
176 struct PlottablesHelper : public IPlottablesHelper {
195 struct PlottablesHelper : public IPlottablesHelper {
177 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
196 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
178
197
179 PlottablesMap create(QCustomPlot &plot) const override
198 PlottablesMap create(QCustomPlot &plot) const override
180 {
199 {
181 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
200 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
182 }
201 }
183
202
184 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
203 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
185 {
204 {
186 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
205 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
187 }
206 }
188
207
189 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
208 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
190 {
209 {
191 return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot);
210 return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot);
192 }
211 }
193
212
194 T &m_DataSeries;
213 T &m_DataSeries;
195 };
214 };
196
215
197 /// Creates IPlottablesHelper according to a data series
216 /// Creates IPlottablesHelper according to a data series
198 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
217 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
199 {
218 {
200 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
219 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
201 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
220 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
202 }
221 }
222 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
223 return std::make_unique<PlottablesHelper<SpectrogramSeries> >(*spectrogramSeries);
224 }
203 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
225 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
204 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
226 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
205 }
227 }
206 else {
228 else {
207 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
229 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
208 }
230 }
209 }
231 }
210
232
211 } // namespace
233 } // namespace
212
234
213 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
235 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
214 QCustomPlot &plot) noexcept
236 QCustomPlot &plot) noexcept
215 {
237 {
216 if (variable) {
238 if (variable) {
217 auto helper = createHelper(variable->dataSeries());
239 auto helper = createHelper(variable->dataSeries());
218 auto plottables = helper->create(plot);
240 auto plottables = helper->create(plot);
219 return plottables;
241 return plottables;
220 }
242 }
221 else {
243 else {
222 qCDebug(LOG_VisualizationGraphHelper())
244 qCDebug(LOG_VisualizationGraphHelper())
223 << QObject::tr("Can't create graph plottables : the variable is null");
245 << QObject::tr("Can't create graph plottables : the variable is null");
224 return PlottablesMap{};
246 return PlottablesMap{};
225 }
247 }
226 }
248 }
227
249
228 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
250 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
229 QCustomPlot &plot) noexcept
251 QCustomPlot &plot) noexcept
230 {
252 {
231 if (variable) {
253 if (variable) {
232 auto helper = createHelper(variable->dataSeries());
254 auto helper = createHelper(variable->dataSeries());
233 helper->setYAxisRange(variable->range(), plot);
255 helper->setYAxisRange(variable->range(), plot);
234 }
256 }
235 else {
257 else {
236 qCDebug(LOG_VisualizationGraphHelper())
258 qCDebug(LOG_VisualizationGraphHelper())
237 << QObject::tr("Can't set y-axis range of plot: the variable is null");
259 << QObject::tr("Can't set y-axis range of plot: the variable is null");
238 }
260 }
239 }
261 }
240
262
241 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
263 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
242 std::shared_ptr<IDataSeries> dataSeries,
264 std::shared_ptr<IDataSeries> dataSeries,
243 const SqpRange &dateTime)
265 const SqpRange &dateTime)
244 {
266 {
245 auto helper = createHelper(dataSeries);
267 auto helper = createHelper(dataSeries);
246 helper->update(plottables, dateTime);
268 helper->update(plottables, dateTime);
247 }
269 }
General Comments 0
You need to be logged in to leave comments. Login now