##// END OF EJS Templates
Updates access to y-axis properties of the data series (1)...
Alexandre Leroux -
r988:99f1718ec669
parent child
Show More
@@ -1,483 +1,488
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 /// @sa IDataSeries::yAxisUnit()
197 Unit yAxisUnit() const override { return m_YAxis.unit(); }
198
196 /// @return the values dataset
199 /// @return the values dataset
197 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
200 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
198 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
201 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
199
202
200 /// @sa IDataSeries::valuesUnit()
203 /// @sa IDataSeries::valuesUnit()
201 Unit valuesUnit() const override { return m_ValuesUnit; }
204 Unit valuesUnit() const override { return m_ValuesUnit; }
202
205
203 int nbPoints() const override { return m_ValuesData->totalSize(); }
206 int nbPoints() const override { return m_ValuesData->totalSize(); }
204
207
208 std::pair<double, double> yBounds() const override { return m_YAxis.bounds(); }
209
205 void clear()
210 void clear()
206 {
211 {
207 m_XAxisData->clear();
212 m_XAxisData->clear();
208 m_ValuesData->clear();
213 m_ValuesData->clear();
209 }
214 }
210
215
211 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
216 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
212
217
213 /// Merges into the data series an other data series.
218 /// Merges into the data series an other data series.
214 ///
219 ///
215 /// The two dataseries:
220 /// The two dataseries:
216 /// - must be of the same dimension
221 /// - must be of the same dimension
217 /// - must have the same y-axis (if defined)
222 /// - must have the same y-axis (if defined)
218 ///
223 ///
219 /// If the prerequisites are not valid, the method does nothing
224 /// If the prerequisites are not valid, the method does nothing
220 ///
225 ///
221 /// @remarks the data series to merge with is cleared after the operation
226 /// @remarks the data series to merge with is cleared after the operation
222 void merge(IDataSeries *dataSeries) override
227 void merge(IDataSeries *dataSeries) override
223 {
228 {
224 dataSeries->lockWrite();
229 dataSeries->lockWrite();
225 lockWrite();
230 lockWrite();
226
231
227 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
232 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
228 if (m_YAxis == other->m_YAxis) {
233 if (m_YAxis == other->m_YAxis) {
229 DataSeriesMergeHelper::merge(*other, *this);
234 DataSeriesMergeHelper::merge(*other, *this);
230 }
235 }
231 else {
236 else {
232 qCWarning(LOG_DataSeries())
237 qCWarning(LOG_DataSeries())
233 << QObject::tr("Can't merge data series that have not the same y-axis");
238 << QObject::tr("Can't merge data series that have not the same y-axis");
234 }
239 }
235 }
240 }
236 else {
241 else {
237 qCWarning(LOG_DataSeries())
242 qCWarning(LOG_DataSeries())
238 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
243 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
239 }
244 }
240 unlock();
245 unlock();
241 dataSeries->unlock();
246 dataSeries->unlock();
242 }
247 }
243
248
244 void purge(double min, double max) override
249 void purge(double min, double max) override
245 {
250 {
246 // Nothing to purge if series is empty
251 // Nothing to purge if series is empty
247 if (isEmpty()) {
252 if (isEmpty()) {
248 return;
253 return;
249 }
254 }
250
255
251 if (min > max) {
256 if (min > max) {
252 std::swap(min, max);
257 std::swap(min, max);
253 }
258 }
254
259
255 // Nothing to purge if series min/max are inside purge range
260 // Nothing to purge if series min/max are inside purge range
256 auto xMin = cbegin()->x();
261 auto xMin = cbegin()->x();
257 auto xMax = (--cend())->x();
262 auto xMax = (--cend())->x();
258 if (xMin >= min && xMax <= max) {
263 if (xMin >= min && xMax <= max) {
259 return;
264 return;
260 }
265 }
261
266
262 auto lowerIt = std::lower_bound(
267 auto lowerIt = std::lower_bound(
263 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
268 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
264 erase(begin(), lowerIt);
269 erase(begin(), lowerIt);
265 auto upperIt = std::upper_bound(
270 auto upperIt = std::upper_bound(
266 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
271 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
267 erase(upperIt, end());
272 erase(upperIt, end());
268 }
273 }
269
274
270 // ///////// //
275 // ///////// //
271 // Iterators //
276 // Iterators //
272 // ///////// //
277 // ///////// //
273
278
274 DataSeriesIterator begin() override
279 DataSeriesIterator begin() override
275 {
280 {
276 return DataSeriesIterator{DataSeriesIteratorValue{
281 return DataSeriesIterator{DataSeriesIteratorValue{
277 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
282 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
278 }
283 }
279
284
280 DataSeriesIterator end() override
285 DataSeriesIterator end() override
281 {
286 {
282 return DataSeriesIterator{DataSeriesIteratorValue{
287 return DataSeriesIterator{DataSeriesIteratorValue{
283 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
288 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
284 }
289 }
285
290
286 DataSeriesIterator cbegin() const override
291 DataSeriesIterator cbegin() const override
287 {
292 {
288 return DataSeriesIterator{DataSeriesIteratorValue{
293 return DataSeriesIterator{DataSeriesIteratorValue{
289 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
294 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
290 }
295 }
291
296
292 DataSeriesIterator cend() const override
297 DataSeriesIterator cend() const override
293 {
298 {
294 return DataSeriesIterator{DataSeriesIteratorValue{
299 return DataSeriesIterator{DataSeriesIteratorValue{
295 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
300 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
296 }
301 }
297
302
298 void erase(DataSeriesIterator first, DataSeriesIterator last)
303 void erase(DataSeriesIterator first, DataSeriesIterator last)
299 {
304 {
300 auto firstImpl
305 auto firstImpl
301 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
306 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
302 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
307 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
303
308
304 if (firstImpl && lastImpl) {
309 if (firstImpl && lastImpl) {
305 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
310 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
306 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
311 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
307 }
312 }
308 }
313 }
309
314
310 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
315 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
311 {
316 {
312 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
317 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
313 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
318 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
314
319
315 if (firstImpl && lastImpl) {
320 if (firstImpl && lastImpl) {
316 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
321 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
317 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
322 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
318 }
323 }
319 }
324 }
320
325
321 /// @sa IDataSeries::minXAxisData()
326 /// @sa IDataSeries::minXAxisData()
322 DataSeriesIterator minXAxisData(double minXAxisData) const override
327 DataSeriesIterator minXAxisData(double minXAxisData) const override
323 {
328 {
324 return std::lower_bound(
329 return std::lower_bound(
325 cbegin(), cend(), minXAxisData,
330 cbegin(), cend(), minXAxisData,
326 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
331 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
327 }
332 }
328
333
329 /// @sa IDataSeries::maxXAxisData()
334 /// @sa IDataSeries::maxXAxisData()
330 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
335 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
331 {
336 {
332 // Gets the first element that greater than max value
337 // Gets the first element that greater than max value
333 auto it = std::upper_bound(
338 auto it = std::upper_bound(
334 cbegin(), cend(), maxXAxisData,
339 cbegin(), cend(), maxXAxisData,
335 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
340 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
336
341
337 return it == cbegin() ? cend() : --it;
342 return it == cbegin() ? cend() : --it;
338 }
343 }
339
344
340 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
345 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
341 double maxXAxisData) const override
346 double maxXAxisData) const override
342 {
347 {
343 if (minXAxisData > maxXAxisData) {
348 if (minXAxisData > maxXAxisData) {
344 std::swap(minXAxisData, maxXAxisData);
349 std::swap(minXAxisData, maxXAxisData);
345 }
350 }
346
351
347 auto begin = cbegin();
352 auto begin = cbegin();
348 auto end = cend();
353 auto end = cend();
349
354
350 auto lowerIt = std::lower_bound(
355 auto lowerIt = std::lower_bound(
351 begin, end, minXAxisData,
356 begin, end, minXAxisData,
352 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
357 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
353 auto upperIt = std::upper_bound(
358 auto upperIt = std::upper_bound(
354 lowerIt, end, maxXAxisData,
359 lowerIt, end, maxXAxisData,
355 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
360 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
356
361
357 return std::make_pair(lowerIt, upperIt);
362 return std::make_pair(lowerIt, upperIt);
358 }
363 }
359
364
360 std::pair<DataSeriesIterator, DataSeriesIterator>
365 std::pair<DataSeriesIterator, DataSeriesIterator>
361 valuesBounds(double minXAxisData, double maxXAxisData) const override
366 valuesBounds(double minXAxisData, double maxXAxisData) const override
362 {
367 {
363 // Places iterators to the correct x-axis range
368 // Places iterators to the correct x-axis range
364 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
369 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
365
370
366 // Returns end iterators if the range is empty
371 // Returns end iterators if the range is empty
367 if (xAxisRangeIts.first == xAxisRangeIts.second) {
372 if (xAxisRangeIts.first == xAxisRangeIts.second) {
368 return std::make_pair(cend(), cend());
373 return std::make_pair(cend(), cend());
369 }
374 }
370
375
371 // Gets the iterator on the min of all values data
376 // Gets the iterator on the min of all values data
372 auto minIt = std::min_element(
377 auto minIt = std::min_element(
373 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
378 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
374 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
379 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
375 });
380 });
376
381
377 // Gets the iterator on the max of all values data
382 // Gets the iterator on the max of all values data
378 auto maxIt = std::max_element(
383 auto maxIt = std::max_element(
379 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
384 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
380 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
385 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
381 });
386 });
382
387
383 return std::make_pair(minIt, maxIt);
388 return std::make_pair(minIt, maxIt);
384 }
389 }
385
390
386 /// @return the y-axis associated to the data series
391 /// @return the y-axis associated to the data series
387 /// @todo pass getter as protected and use iterators to access the y-axis data
392 const OptionalAxis &yAxis() const { return m_YAxis; }
388 OptionalAxis yAxis() const { return m_YAxis; }
393 OptionalAxis &yAxis() { return m_YAxis; }
389
394
390 // /////// //
395 // /////// //
391 // Mutexes //
396 // Mutexes //
392 // /////// //
397 // /////// //
393
398
394 virtual void lockRead() { m_Lock.lockForRead(); }
399 virtual void lockRead() { m_Lock.lockForRead(); }
395 virtual void lockWrite() { m_Lock.lockForWrite(); }
400 virtual void lockWrite() { m_Lock.lockForWrite(); }
396 virtual void unlock() { m_Lock.unlock(); }
401 virtual void unlock() { m_Lock.unlock(); }
397
402
398 protected:
403 protected:
399 /// Protected ctor (DataSeries is abstract).
404 /// Protected ctor (DataSeries is abstract).
400 ///
405 ///
401 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
406 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
402 /// class description for consistent rules)
407 /// class description for consistent rules)
403 /// @remarks data series is automatically sorted on its x-axis data
408 /// @remarks data series is automatically sorted on its x-axis data
404 /// @throws std::invalid_argument if the data are inconsistent with each other
409 /// @throws std::invalid_argument if the data are inconsistent with each other
405 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
410 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
406 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
411 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
407 OptionalAxis yAxis = OptionalAxis{})
412 OptionalAxis yAxis = OptionalAxis{})
408 : m_XAxisData{xAxisData},
413 : m_XAxisData{xAxisData},
409 m_XAxisUnit{xAxisUnit},
414 m_XAxisUnit{xAxisUnit},
410 m_ValuesData{valuesData},
415 m_ValuesData{valuesData},
411 m_ValuesUnit{valuesUnit},
416 m_ValuesUnit{valuesUnit},
412 m_YAxis{std::move(yAxis)}
417 m_YAxis{std::move(yAxis)}
413 {
418 {
414 if (m_XAxisData->size() != m_ValuesData->size()) {
419 if (m_XAxisData->size() != m_ValuesData->size()) {
415 throw std::invalid_argument{
420 throw std::invalid_argument{
416 "The number of values by component must be equal to the number of x-axis data"};
421 "The number of values by component must be equal to the number of x-axis data"};
417 }
422 }
418
423
419 // Validates y-axis (if defined)
424 // Validates y-axis (if defined)
420 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
425 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
421 throw std::invalid_argument{
426 throw std::invalid_argument{
422 "As the y-axis is defined, the number of value components must be equal to the "
427 "As the y-axis is defined, the number of value components must be equal to the "
423 "number of y-axis data"};
428 "number of y-axis data"};
424 }
429 }
425
430
426 // Sorts data if it's not the case
431 // Sorts data if it's not the case
427 const auto &xAxisCData = m_XAxisData->cdata();
432 const auto &xAxisCData = m_XAxisData->cdata();
428 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
433 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
429 sort();
434 sort();
430 }
435 }
431 }
436 }
432
437
433 /// Copy ctor
438 /// Copy ctor
434 explicit DataSeries(const DataSeries<Dim> &other)
439 explicit DataSeries(const DataSeries<Dim> &other)
435 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
440 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
436 m_XAxisUnit{other.m_XAxisUnit},
441 m_XAxisUnit{other.m_XAxisUnit},
437 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
442 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
438 m_ValuesUnit{other.m_ValuesUnit},
443 m_ValuesUnit{other.m_ValuesUnit},
439 m_YAxis{other.m_YAxis}
444 m_YAxis{other.m_YAxis}
440 {
445 {
441 // Since a series is ordered from its construction and is always ordered, it is not
446 // Since a series is ordered from its construction and is always ordered, it is not
442 // necessary to call the sort method here ('other' is sorted)
447 // necessary to call the sort method here ('other' is sorted)
443 }
448 }
444
449
445 /// Assignment operator
450 /// Assignment operator
446 template <int D>
451 template <int D>
447 DataSeries &operator=(DataSeries<D> other)
452 DataSeries &operator=(DataSeries<D> other)
448 {
453 {
449 std::swap(m_XAxisData, other.m_XAxisData);
454 std::swap(m_XAxisData, other.m_XAxisData);
450 std::swap(m_XAxisUnit, other.m_XAxisUnit);
455 std::swap(m_XAxisUnit, other.m_XAxisUnit);
451 std::swap(m_ValuesData, other.m_ValuesData);
456 std::swap(m_ValuesData, other.m_ValuesData);
452 std::swap(m_ValuesUnit, other.m_ValuesUnit);
457 std::swap(m_ValuesUnit, other.m_ValuesUnit);
453 std::swap(m_YAxis, other.m_YAxis);
458 std::swap(m_YAxis, other.m_YAxis);
454
459
455 return *this;
460 return *this;
456 }
461 }
457
462
458 private:
463 private:
459 /**
464 /**
460 * Sorts data series on its x-axis data
465 * Sorts data series on its x-axis data
461 */
466 */
462 void sort() noexcept
467 void sort() noexcept
463 {
468 {
464 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
469 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
465 m_XAxisData = m_XAxisData->sort(permutation);
470 m_XAxisData = m_XAxisData->sort(permutation);
466 m_ValuesData = m_ValuesData->sort(permutation);
471 m_ValuesData = m_ValuesData->sort(permutation);
467 }
472 }
468
473
469 // x-axis
474 // x-axis
470 std::shared_ptr<ArrayData<1> > m_XAxisData;
475 std::shared_ptr<ArrayData<1> > m_XAxisData;
471 Unit m_XAxisUnit;
476 Unit m_XAxisUnit;
472
477
473 // values
478 // values
474 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
479 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
475 Unit m_ValuesUnit;
480 Unit m_ValuesUnit;
476
481
477 // y-axis (optional)
482 // y-axis (optional)
478 OptionalAxis m_YAxis;
483 OptionalAxis m_YAxis;
479
484
480 QReadWriteLock m_Lock;
485 QReadWriteLock m_Lock;
481 };
486 };
482
487
483 #endif // SCIQLOP_DATASERIES_H
488 #endif // SCIQLOP_DATASERIES_H
@@ -1,96 +1,102
1 #ifndef SCIQLOP_IDATASERIES_H
1 #ifndef SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
2 #define SCIQLOP_IDATASERIES_H
3
3
4 #include <Common/MetaTypes.h>
4 #include <Common/MetaTypes.h>
5 #include <Data/DataSeriesIterator.h>
5 #include <Data/DataSeriesIterator.h>
6 #include <Data/SqpRange.h>
6 #include <Data/SqpRange.h>
7 #include <Data/Unit.h>
7 #include <Data/Unit.h>
8
8
9 #include <memory>
9 #include <memory>
10
10
11 #include <QString>
11 #include <QString>
12
12
13 template <int Dim>
13 template <int Dim>
14 class ArrayData;
14 class ArrayData;
15
15
16 /**
16 /**
17 * @brief The IDataSeries aims to declare a data series.
17 * @brief The IDataSeries aims to declare a data series.
18 *
18 *
19 * A data series is an entity that contains at least :
19 * A data series is an entity that contains at least :
20 * - one dataset representing the x-axis
20 * - one dataset representing the x-axis
21 * - one dataset representing the values
21 * - one dataset representing the values
22 *
22 *
23 * Each dataset is represented by an ArrayData, and is associated with a unit.
23 * Each dataset is represented by an ArrayData, and is associated with a unit.
24 *
24 *
25 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
25 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
26 * IDataSeries. The x-axis dataset is always unidimensional.
26 * IDataSeries. The x-axis dataset is always unidimensional.
27 *
27 *
28 * @sa ArrayData
28 * @sa ArrayData
29 */
29 */
30 class IDataSeries {
30 class IDataSeries {
31 public:
31 public:
32 virtual ~IDataSeries() noexcept = default;
32 virtual ~IDataSeries() noexcept = default;
33
33
34 /// Returns the x-axis dataset
34 /// Returns the x-axis dataset
35 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
35 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
36
36
37 /// Returns the x-axis dataset (as const)
37 /// Returns the x-axis dataset (as const)
38 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
38 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
39
39
40 virtual Unit xAxisUnit() const = 0;
40 virtual Unit xAxisUnit() const = 0;
41
41
42 /// @return the y-axis unit, if axis is defined, default unit otherwise
43 virtual Unit yAxisUnit() const = 0;
44
42 virtual Unit valuesUnit() const = 0;
45 virtual Unit valuesUnit() const = 0;
43
46
44 virtual void merge(IDataSeries *dataSeries) = 0;
47 virtual void merge(IDataSeries *dataSeries) = 0;
45 /// Removes from data series all entries whose value on the x-axis is not between min and max
48 /// Removes from data series all entries whose value on the x-axis is not between min and max
46 virtual void purge(double min, double max) = 0;
49 virtual void purge(double min, double max) = 0;
47
50
48 /// @todo Review the name and signature of this method
51 /// @todo Review the name and signature of this method
49 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
52 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
50
53
51 virtual std::unique_ptr<IDataSeries> clone() const = 0;
54 virtual std::unique_ptr<IDataSeries> clone() const = 0;
52
55
53 /// @return the total number of points contained in the data series
56 /// @return the total number of points contained in the data series
54 virtual int nbPoints() const = 0;
57 virtual int nbPoints() const = 0;
55
58
59 /// @return the bounds of the y-axis axis (if defined)
60 virtual std::pair<double, double> yBounds() const = 0;
61
56 // ///////// //
62 // ///////// //
57 // Iterators //
63 // Iterators //
58 // ///////// //
64 // ///////// //
59
65
60 virtual DataSeriesIterator cbegin() const = 0;
66 virtual DataSeriesIterator cbegin() const = 0;
61 virtual DataSeriesIterator cend() const = 0;
67 virtual DataSeriesIterator cend() const = 0;
62 virtual DataSeriesIterator begin() = 0;
68 virtual DataSeriesIterator begin() = 0;
63 virtual DataSeriesIterator end() = 0;
69 virtual DataSeriesIterator end() = 0;
64
70
65 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
71 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
66 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
72 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
67 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
73 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
68
74
69 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
75 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
70 /// equal to the value passed in parameter, or the end iterator if there is no matching value
76 /// equal to the value passed in parameter, or the end iterator if there is no matching value
71 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
77 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
72
78
73 /// @return the iterators pointing to the range of data whose x-axis values are between min and
79 /// @return the iterators pointing to the range of data whose x-axis values are between min and
74 /// max passed in parameters
80 /// max passed in parameters
75 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
81 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
76 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
82 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
77
83
78 /// @return two iterators pointing to the data that have respectively the min and the max value
84 /// @return two iterators pointing to the data that have respectively the min and the max value
79 /// data of a data series' range. The search is performed for a given x-axis range.
85 /// data of a data series' range. The search is performed for a given x-axis range.
80 /// @sa xAxisRange()
86 /// @sa xAxisRange()
81 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
87 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
82 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
88 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
83
89
84 // /////// //
90 // /////// //
85 // Mutexes //
91 // Mutexes //
86 // /////// //
92 // /////// //
87
93
88 virtual void lockRead() = 0;
94 virtual void lockRead() = 0;
89 virtual void lockWrite() = 0;
95 virtual void lockWrite() = 0;
90 virtual void unlock() = 0;
96 virtual void unlock() = 0;
91 };
97 };
92
98
93 // Required for using shared_ptr in signals/slots
99 // Required for using shared_ptr in signals/slots
94 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
100 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
95
101
96 #endif // SCIQLOP_IDATASERIES_H
102 #endif // SCIQLOP_IDATASERIES_H
@@ -1,173 +1,172
1 #include "Visualization/AxisRenderingUtils.h"
1 #include "Visualization/AxisRenderingUtils.h"
2
2
3 #include <Data/ScalarSeries.h>
3 #include <Data/ScalarSeries.h>
4 #include <Data/SpectrogramSeries.h>
4 #include <Data/SpectrogramSeries.h>
5 #include <Data/VectorSeries.h>
5 #include <Data/VectorSeries.h>
6
6
7 #include <Visualization/qcustomplot.h>
7 #include <Visualization/qcustomplot.h>
8
8
9 Q_LOGGING_CATEGORY(LOG_AxisRenderingUtils, "AxisRenderingUtils")
9 Q_LOGGING_CATEGORY(LOG_AxisRenderingUtils, "AxisRenderingUtils")
10
10
11 namespace {
11 namespace {
12
12
13 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
13 const auto DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd hh:mm:ss:zzz");
14
14
15 /// Format for datetimes on a axis
15 /// Format for datetimes on a axis
16 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
16 const auto DATETIME_TICKER_FORMAT = QStringLiteral("yyyy/MM/dd \nhh:mm:ss");
17
17
18 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
18 /// Generates the appropriate ticker for an axis, depending on whether the axis displays time or
19 /// non-time data
19 /// non-time data
20 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis, QCPAxis::ScaleType scaleType)
20 QSharedPointer<QCPAxisTicker> axisTicker(bool isTimeAxis, QCPAxis::ScaleType scaleType)
21 {
21 {
22 if (isTimeAxis) {
22 if (isTimeAxis) {
23 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
23 auto dateTicker = QSharedPointer<QCPAxisTickerDateTime>::create();
24 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
24 dateTicker->setDateTimeFormat(DATETIME_TICKER_FORMAT);
25 dateTicker->setDateTimeSpec(Qt::UTC);
25 dateTicker->setDateTimeSpec(Qt::UTC);
26
26
27 return dateTicker;
27 return dateTicker;
28 }
28 }
29 else if (scaleType == QCPAxis::stLogarithmic) {
29 else if (scaleType == QCPAxis::stLogarithmic) {
30 return QSharedPointer<QCPAxisTickerLog>::create();
30 return QSharedPointer<QCPAxisTickerLog>::create();
31 }
31 }
32 else {
32 else {
33 // default ticker
33 // default ticker
34 return QSharedPointer<QCPAxisTicker>::create();
34 return QSharedPointer<QCPAxisTicker>::create();
35 }
35 }
36 }
36 }
37
37
38 /**
38 /**
39 * Sets properties of the axis passed as parameter
39 * Sets properties of the axis passed as parameter
40 * @param axis the axis to set
40 * @param axis the axis to set
41 * @param unit the unit to set for the axis
41 * @param unit the unit to set for the axis
42 * @param scaleType the scale type to set for the axis
42 * @param scaleType the scale type to set for the axis
43 */
43 */
44 void setAxisProperties(QCPAxis &axis, const Unit &unit,
44 void setAxisProperties(QCPAxis &axis, const Unit &unit,
45 QCPAxis::ScaleType scaleType = QCPAxis::stLinear)
45 QCPAxis::ScaleType scaleType = QCPAxis::stLinear)
46 {
46 {
47 // label (unit name)
47 // label (unit name)
48 axis.setLabel(unit.m_Name);
48 axis.setLabel(unit.m_Name);
49
49
50 // scale type
50 // scale type
51 axis.setScaleType(scaleType);
51 axis.setScaleType(scaleType);
52 if (scaleType == QCPAxis::stLogarithmic) {
52 if (scaleType == QCPAxis::stLogarithmic) {
53 // Scientific notation
53 // Scientific notation
54 axis.setNumberPrecision(0);
54 axis.setNumberPrecision(0);
55 axis.setNumberFormat("eb");
55 axis.setNumberFormat("eb");
56 }
56 }
57
57
58 // ticker (depending on the type of unit)
58 // ticker (depending on the type of unit)
59 axis.setTicker(axisTicker(unit.m_TimeUnit, scaleType));
59 axis.setTicker(axisTicker(unit.m_TimeUnit, scaleType));
60 }
60 }
61
61
62 /**
62 /**
63 * Delegate used to set axes properties
63 * Delegate used to set axes properties
64 */
64 */
65 template <typename T, typename Enabled = void>
65 template <typename T, typename Enabled = void>
66 struct AxisSetter {
66 struct AxisSetter {
67 static void setProperties(T &, QCustomPlot &, QCPColorScale &)
67 static void setProperties(T &, QCustomPlot &, QCPColorScale &)
68 {
68 {
69 // Default implementation does nothing
69 // Default implementation does nothing
70 qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis properties: unmanaged type of data";
70 qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis properties: unmanaged type of data";
71 }
71 }
72 };
72 };
73
73
74 /**
74 /**
75 * Specialization of AxisSetter for scalars and vectors
75 * Specialization of AxisSetter for scalars and vectors
76 * @sa ScalarSeries
76 * @sa ScalarSeries
77 * @sa VectorSeries
77 * @sa VectorSeries
78 */
78 */
79 template <typename T>
79 template <typename T>
80 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
80 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
81 or std::is_base_of<VectorSeries, T>::value> > {
81 or std::is_base_of<VectorSeries, T>::value> > {
82 static void setProperties(T &dataSeries, QCustomPlot &plot, QCPColorScale &)
82 static void setProperties(T &dataSeries, QCustomPlot &plot, QCPColorScale &)
83 {
83 {
84 dataSeries.lockRead();
84 dataSeries.lockRead();
85 auto xAxisUnit = dataSeries.xAxisUnit();
85 auto xAxisUnit = dataSeries.xAxisUnit();
86 auto valuesUnit = dataSeries.valuesUnit();
86 auto valuesUnit = dataSeries.valuesUnit();
87 dataSeries.unlock();
87 dataSeries.unlock();
88
88
89 setAxisProperties(*plot.xAxis, xAxisUnit);
89 setAxisProperties(*plot.xAxis, xAxisUnit);
90 setAxisProperties(*plot.yAxis, valuesUnit);
90 setAxisProperties(*plot.yAxis, valuesUnit);
91 }
91 }
92 };
92 };
93
93
94 /**
94 /**
95 * Specialization of AxisSetter for spectrograms
95 * Specialization of AxisSetter for spectrograms
96 * @sa SpectrogramSeries
96 * @sa SpectrogramSeries
97 */
97 */
98 template <typename T>
98 template <typename T>
99 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
99 struct AxisSetter<T, typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
100 static void setProperties(T &dataSeries, QCustomPlot &plot, QCPColorScale &colorScale)
100 static void setProperties(T &dataSeries, QCustomPlot &plot, QCPColorScale &colorScale)
101 {
101 {
102 dataSeries.lockRead();
102 dataSeries.lockRead();
103 auto xAxisUnit = dataSeries.xAxisUnit();
103 auto xAxisUnit = dataSeries.xAxisUnit();
104 /// @todo ALX: use iterators here
104 auto yAxisUnit = dataSeries.yAxisUnit();
105 auto yAxisUnit = dataSeries.yAxis().unit();
106 auto valuesUnit = dataSeries.valuesUnit();
105 auto valuesUnit = dataSeries.valuesUnit();
107 dataSeries.unlock();
106 dataSeries.unlock();
108
107
109 setAxisProperties(*plot.xAxis, xAxisUnit);
108 setAxisProperties(*plot.xAxis, xAxisUnit);
110 setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic);
109 setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic);
111
110
112 // Displays color scale in plot
111 // Displays color scale in plot
113 plot.plotLayout()->insertRow(0);
112 plot.plotLayout()->insertRow(0);
114 plot.plotLayout()->addElement(0, 0, &colorScale);
113 plot.plotLayout()->addElement(0, 0, &colorScale);
115 colorScale.setType(QCPAxis::atTop);
114 colorScale.setType(QCPAxis::atTop);
116 colorScale.setMinimumMargins(QMargins{0, 0, 0, 0});
115 colorScale.setMinimumMargins(QMargins{0, 0, 0, 0});
117
116
118 // Aligns color scale with axes
117 // Aligns color scale with axes
119 auto marginGroups = plot.axisRect()->marginGroups();
118 auto marginGroups = plot.axisRect()->marginGroups();
120 for (auto it = marginGroups.begin(), end = marginGroups.end(); it != end; ++it) {
119 for (auto it = marginGroups.begin(), end = marginGroups.end(); it != end; ++it) {
121 colorScale.setMarginGroup(it.key(), it.value());
120 colorScale.setMarginGroup(it.key(), it.value());
122 }
121 }
123
122
124 // Set color scale properties
123 // Set color scale properties
125 setAxisProperties(*colorScale.axis(), valuesUnit, QCPAxis::stLogarithmic);
124 setAxisProperties(*colorScale.axis(), valuesUnit, QCPAxis::stLogarithmic);
126 }
125 }
127 };
126 };
128
127
129 /**
128 /**
130 * Default implementation of IAxisHelper, which takes data series to set axes properties
129 * Default implementation of IAxisHelper, which takes data series to set axes properties
131 * @tparam T the data series' type
130 * @tparam T the data series' type
132 */
131 */
133 template <typename T>
132 template <typename T>
134 struct AxisHelper : public IAxisHelper {
133 struct AxisHelper : public IAxisHelper {
135 explicit AxisHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
134 explicit AxisHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
136
135
137 void setProperties(QCustomPlot &plot, QCPColorScale &colorScale) override
136 void setProperties(QCustomPlot &plot, QCPColorScale &colorScale) override
138 {
137 {
139 AxisSetter<T>::setProperties(m_DataSeries, plot, colorScale);
138 AxisSetter<T>::setProperties(m_DataSeries, plot, colorScale);
140 }
139 }
141
140
142 T &m_DataSeries;
141 T &m_DataSeries;
143 };
142 };
144
143
145 } // namespace
144 } // namespace
146
145
147 QString formatValue(double value, const QCPAxis &axis)
146 QString formatValue(double value, const QCPAxis &axis)
148 {
147 {
149 // If the axis is a time axis, formats the value as a date
148 // If the axis is a time axis, formats the value as a date
150 if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) {
149 if (auto axisTicker = qSharedPointerDynamicCast<QCPAxisTickerDateTime>(axis.ticker())) {
151 return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT);
150 return DateUtils::dateTime(value, axisTicker->dateTimeSpec()).toString(DATETIME_FORMAT);
152 }
151 }
153 else {
152 else {
154 return QString::number(value);
153 return QString::number(value);
155 }
154 }
156 }
155 }
157
156
158 std::unique_ptr<IAxisHelper>
157 std::unique_ptr<IAxisHelper>
159 IAxisHelperFactory::create(std::shared_ptr<IDataSeries> dataSeries) noexcept
158 IAxisHelperFactory::create(std::shared_ptr<IDataSeries> dataSeries) noexcept
160 {
159 {
161 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
160 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
162 return std::make_unique<AxisHelper<ScalarSeries> >(*scalarSeries);
161 return std::make_unique<AxisHelper<ScalarSeries> >(*scalarSeries);
163 }
162 }
164 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
163 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
165 return std::make_unique<AxisHelper<SpectrogramSeries> >(*spectrogramSeries);
164 return std::make_unique<AxisHelper<SpectrogramSeries> >(*spectrogramSeries);
166 }
165 }
167 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
166 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
168 return std::make_unique<AxisHelper<VectorSeries> >(*vectorSeries);
167 return std::make_unique<AxisHelper<VectorSeries> >(*vectorSeries);
169 }
168 }
170 else {
169 else {
171 return std::make_unique<AxisHelper<IDataSeries> >(*dataSeries);
170 return std::make_unique<AxisHelper<IDataSeries> >(*dataSeries);
172 }
171 }
173 }
172 }
@@ -1,345 +1,344
1 #include "Visualization/VisualizationGraphHelper.h"
1 #include "Visualization/VisualizationGraphHelper.h"
2 #include "Visualization/qcustomplot.h"
2 #include "Visualization/qcustomplot.h"
3
3
4 #include <Data/ScalarSeries.h>
4 #include <Data/ScalarSeries.h>
5 #include <Data/SpectrogramSeries.h>
5 #include <Data/SpectrogramSeries.h>
6 #include <Data/VectorSeries.h>
6 #include <Data/VectorSeries.h>
7
7
8 #include <Variable/Variable.h>
8 #include <Variable/Variable.h>
9
9
10 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
10 Q_LOGGING_CATEGORY(LOG_VisualizationGraphHelper, "VisualizationGraphHelper")
11
11
12 namespace {
12 namespace {
13
13
14 class SqpDataContainer : public QCPGraphDataContainer {
14 class SqpDataContainer : public QCPGraphDataContainer {
15 public:
15 public:
16 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
16 void appendGraphData(const QCPGraphData &data) { mData.append(data); }
17 };
17 };
18
18
19 /**
19 /**
20 * Struct used to create plottables, depending on the type of the data series from which to create
20 * Struct used to create plottables, depending on the type of the data series from which to create
21 * them
21 * them
22 * @tparam T the data series' type
22 * @tparam T the data series' type
23 * @remarks Default implementation can't create plottables
23 * @remarks Default implementation can't create plottables
24 */
24 */
25 template <typename T, typename Enabled = void>
25 template <typename T, typename Enabled = void>
26 struct PlottablesCreator {
26 struct PlottablesCreator {
27 static PlottablesMap createPlottables(T &, QCustomPlot &)
27 static PlottablesMap createPlottables(T &, QCustomPlot &)
28 {
28 {
29 qCCritical(LOG_DataSeries())
29 qCCritical(LOG_DataSeries())
30 << QObject::tr("Can't create plottables: unmanaged data series type");
30 << QObject::tr("Can't create plottables: unmanaged data series type");
31 return {};
31 return {};
32 }
32 }
33 };
33 };
34
34
35 /**
35 /**
36 * Specialization of PlottablesCreator for scalars and vectors
36 * Specialization of PlottablesCreator for scalars and vectors
37 * @sa ScalarSeries
37 * @sa ScalarSeries
38 * @sa VectorSeries
38 * @sa VectorSeries
39 */
39 */
40 template <typename T>
40 template <typename T>
41 struct PlottablesCreator<T,
41 struct PlottablesCreator<T,
42 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
42 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
43 or std::is_base_of<VectorSeries, T>::value> > {
43 or std::is_base_of<VectorSeries, T>::value> > {
44 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
44 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
45 {
45 {
46 PlottablesMap result{};
46 PlottablesMap result{};
47
47
48 // Gets the number of components of the data series
48 // Gets the number of components of the data series
49 dataSeries.lockRead();
49 dataSeries.lockRead();
50 auto componentCount = dataSeries.valuesData()->componentCount();
50 auto componentCount = dataSeries.valuesData()->componentCount();
51 dataSeries.unlock();
51 dataSeries.unlock();
52
52
53 // For each component of the data series, creates a QCPGraph to add to the plot
53 // For each component of the data series, creates a QCPGraph to add to the plot
54 for (auto i = 0; i < componentCount; ++i) {
54 for (auto i = 0; i < componentCount; ++i) {
55 auto graph = plot.addGraph();
55 auto graph = plot.addGraph();
56 result.insert({i, graph});
56 result.insert({i, graph});
57 }
57 }
58
58
59 plot.replot();
59 plot.replot();
60
60
61 return result;
61 return result;
62 }
62 }
63 };
63 };
64
64
65 /**
65 /**
66 * Specialization of PlottablesCreator for spectrograms
66 * Specialization of PlottablesCreator for spectrograms
67 * @sa SpectrogramSeries
67 * @sa SpectrogramSeries
68 */
68 */
69 template <typename T>
69 template <typename T>
70 struct PlottablesCreator<T,
70 struct PlottablesCreator<T,
71 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
71 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
72 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
72 static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot)
73 {
73 {
74 PlottablesMap result{};
74 PlottablesMap result{};
75 result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}});
75 result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}});
76
76
77 plot.replot();
77 plot.replot();
78
78
79 return result;
79 return result;
80 }
80 }
81 };
81 };
82
82
83 /**
83 /**
84 * Struct used to update plottables, depending on the type of the data series from which to update
84 * Struct used to update plottables, depending on the type of the data series from which to update
85 * them
85 * them
86 * @tparam T the data series' type
86 * @tparam T the data series' type
87 * @remarks Default implementation can't update plottables
87 * @remarks Default implementation can't update plottables
88 */
88 */
89 template <typename T, typename Enabled = void>
89 template <typename T, typename Enabled = void>
90 struct PlottablesUpdater {
90 struct PlottablesUpdater {
91 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
91 static void setPlotYAxisRange(T &, const SqpRange &, QCustomPlot &)
92 {
92 {
93 qCCritical(LOG_VisualizationGraphHelper())
93 qCCritical(LOG_VisualizationGraphHelper())
94 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
94 << QObject::tr("Can't set plot y-axis range: unmanaged data series type");
95 }
95 }
96
96
97 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
97 static void updatePlottables(T &, PlottablesMap &, const SqpRange &, bool)
98 {
98 {
99 qCCritical(LOG_VisualizationGraphHelper())
99 qCCritical(LOG_VisualizationGraphHelper())
100 << QObject::tr("Can't update plottables: unmanaged data series type");
100 << QObject::tr("Can't update plottables: unmanaged data series type");
101 }
101 }
102 };
102 };
103
103
104 /**
104 /**
105 * Specialization of PlottablesUpdater for scalars and vectors
105 * Specialization of PlottablesUpdater for scalars and vectors
106 * @sa ScalarSeries
106 * @sa ScalarSeries
107 * @sa VectorSeries
107 * @sa VectorSeries
108 */
108 */
109 template <typename T>
109 template <typename T>
110 struct PlottablesUpdater<T,
110 struct PlottablesUpdater<T,
111 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
111 typename std::enable_if_t<std::is_base_of<ScalarSeries, T>::value
112 or std::is_base_of<VectorSeries, T>::value> > {
112 or std::is_base_of<VectorSeries, T>::value> > {
113 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
113 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
114 {
114 {
115 auto minValue = 0., maxValue = 0.;
115 auto minValue = 0., maxValue = 0.;
116
116
117 dataSeries.lockRead();
117 dataSeries.lockRead();
118 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
118 auto valuesBounds = dataSeries.valuesBounds(xAxisRange.m_TStart, xAxisRange.m_TEnd);
119 auto end = dataSeries.cend();
119 auto end = dataSeries.cend();
120 if (valuesBounds.first != end && valuesBounds.second != end) {
120 if (valuesBounds.first != end && valuesBounds.second != end) {
121 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
121 auto rangeValue = [](const auto &value) { return std::isnan(value) ? 0. : value; };
122
122
123 minValue = rangeValue(valuesBounds.first->minValue());
123 minValue = rangeValue(valuesBounds.first->minValue());
124 maxValue = rangeValue(valuesBounds.second->maxValue());
124 maxValue = rangeValue(valuesBounds.second->maxValue());
125 }
125 }
126 dataSeries.unlock();
126 dataSeries.unlock();
127
127
128 plot.yAxis->setRange(QCPRange{minValue, maxValue});
128 plot.yAxis->setRange(QCPRange{minValue, maxValue});
129 }
129 }
130
130
131 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
131 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
132 bool rescaleAxes)
132 bool rescaleAxes)
133 {
133 {
134
134
135 // For each plottable to update, resets its data
135 // For each plottable to update, resets its data
136 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
136 std::map<int, QSharedPointer<SqpDataContainer> > dataContainers{};
137 for (const auto &plottable : plottables) {
137 for (const auto &plottable : plottables) {
138 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
138 if (auto graph = dynamic_cast<QCPGraph *>(plottable.second)) {
139 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
139 auto dataContainer = QSharedPointer<SqpDataContainer>::create();
140 graph->setData(dataContainer);
140 graph->setData(dataContainer);
141
141
142 dataContainers.insert({plottable.first, dataContainer});
142 dataContainers.insert({plottable.first, dataContainer});
143 }
143 }
144 }
144 }
145 dataSeries.lockRead();
145 dataSeries.lockRead();
146
146
147 // - Gets the data of the series included in the current range
147 // - Gets the data of the series included in the current range
148 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
148 // - Updates each plottable by adding, for each data item, a point that takes x-axis data
149 // and value data. The correct value is retrieved according to the index of the component
149 // and value data. The correct value is retrieved according to the index of the component
150 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
150 auto subDataIts = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
151 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
151 for (auto it = subDataIts.first; it != subDataIts.second; ++it) {
152 for (const auto &dataContainer : dataContainers) {
152 for (const auto &dataContainer : dataContainers) {
153 auto componentIndex = dataContainer.first;
153 auto componentIndex = dataContainer.first;
154 dataContainer.second->appendGraphData(
154 dataContainer.second->appendGraphData(
155 QCPGraphData(it->x(), it->value(componentIndex)));
155 QCPGraphData(it->x(), it->value(componentIndex)));
156 }
156 }
157 }
157 }
158
158
159 dataSeries.unlock();
159 dataSeries.unlock();
160
160
161 if (!plottables.empty()) {
161 if (!plottables.empty()) {
162 auto plot = plottables.begin()->second->parentPlot();
162 auto plot = plottables.begin()->second->parentPlot();
163
163
164 if (rescaleAxes) {
164 if (rescaleAxes) {
165 plot->rescaleAxes();
165 plot->rescaleAxes();
166 }
166 }
167
167
168 plot->replot();
168 plot->replot();
169 }
169 }
170 }
170 }
171 };
171 };
172
172
173 /**
173 /**
174 * Specialization of PlottablesUpdater for spectrograms
174 * Specialization of PlottablesUpdater for spectrograms
175 * @sa SpectrogramSeries
175 * @sa SpectrogramSeries
176 */
176 */
177 template <typename T>
177 template <typename T>
178 struct PlottablesUpdater<T,
178 struct PlottablesUpdater<T,
179 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
179 typename std::enable_if_t<std::is_base_of<SpectrogramSeries, T>::value> > {
180 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
180 static void setPlotYAxisRange(T &dataSeries, const SqpRange &xAxisRange, QCustomPlot &plot)
181 {
181 {
182 double min, max;
182 double min, max;
183 /// @todo ALX: use iterators here
183 std::tie(min, max) = dataSeries.yBounds();
184 std::tie(min, max) = dataSeries.yAxis().bounds();
185
184
186 if (!std::isnan(min) && !std::isnan(max)) {
185 if (!std::isnan(min) && !std::isnan(max)) {
187 plot.yAxis->setRange(QCPRange{min, max});
186 plot.yAxis->setRange(QCPRange{min, max});
188 }
187 }
189 }
188 }
190
189
191 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
190 static void updatePlottables(T &dataSeries, PlottablesMap &plottables, const SqpRange &range,
192 bool rescaleAxes)
191 bool rescaleAxes)
193 {
192 {
194 if (plottables.empty()) {
193 if (plottables.empty()) {
195 qCDebug(LOG_VisualizationGraphHelper())
194 qCDebug(LOG_VisualizationGraphHelper())
196 << QObject::tr("Can't update spectrogram: no colormap has been associated");
195 << QObject::tr("Can't update spectrogram: no colormap has been associated");
197 return;
196 return;
198 }
197 }
199
198
200 // Gets the colormap to update (normally there is only one colormap)
199 // Gets the colormap to update (normally there is only one colormap)
201 Q_ASSERT(plottables.size() == 1);
200 Q_ASSERT(plottables.size() == 1);
202 auto colormap = dynamic_cast<QCPColorMap *>(plottables.at(0));
201 auto colormap = dynamic_cast<QCPColorMap *>(plottables.at(0));
203 Q_ASSERT(colormap != nullptr);
202 Q_ASSERT(colormap != nullptr);
204
203
205 dataSeries.lockRead();
204 dataSeries.lockRead();
206
205
207 auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
206 auto its = dataSeries.xAxisRange(range.m_TStart, range.m_TEnd);
208 /// @todo ALX: use iterators here
207 /// @todo ALX: use iterators here
209 auto yAxis = dataSeries.yAxis();
208 auto yAxis = dataSeries.yAxis();
210
209
211 // Gets properties of x-axis and y-axis to set size and range of the colormap
210 // Gets properties of x-axis and y-axis to set size and range of the colormap
212 auto nbX = std::distance(its.first, its.second);
211 auto nbX = std::distance(its.first, its.second);
213 auto xMin = nbX != 0 ? its.first->x() : 0.;
212 auto xMin = nbX != 0 ? its.first->x() : 0.;
214 auto xMax = nbX != 0 ? (its.second - 1)->x() : 0.;
213 auto xMax = nbX != 0 ? (its.second - 1)->x() : 0.;
215
214
216 auto nbY = yAxis.size();
215 auto nbY = yAxis.size();
217 auto yMin = 0., yMax = 0.;
216 auto yMin = 0., yMax = 0.;
218 if (nbY != 0) {
217 if (nbY != 0) {
219 std::tie(yMin, yMax) = yAxis.bounds();
218 std::tie(yMin, yMax) = yAxis.bounds();
220 }
219 }
221
220
222 colormap->data()->setSize(nbX, nbY);
221 colormap->data()->setSize(nbX, nbY);
223 colormap->data()->setRange(QCPRange{xMin, xMax}, QCPRange{yMin, yMax});
222 colormap->data()->setRange(QCPRange{xMin, xMax}, QCPRange{yMin, yMax});
224
223
225 // Sets values
224 // Sets values
226 auto xIndex = 0;
225 auto xIndex = 0;
227 for (auto it = its.first; it != its.second; ++it, ++xIndex) {
226 for (auto it = its.first; it != its.second; ++it, ++xIndex) {
228 for (auto yIndex = 0; yIndex < nbY; ++yIndex) {
227 for (auto yIndex = 0; yIndex < nbY; ++yIndex) {
229 auto value = it->value(yIndex);
228 auto value = it->value(yIndex);
230
229
231 colormap->data()->setCell(xIndex, yIndex, value);
230 colormap->data()->setCell(xIndex, yIndex, value);
232
231
233 // Processing spectrogram data for display in QCustomPlot
232 // Processing spectrogram data for display in QCustomPlot
234 /// For the moment, we just make the NaN values to be transparent in the colormap
233 /// For the moment, we just make the NaN values to be transparent in the colormap
235 /// @todo ALX: complete treatments (mesh generation, etc.)
234 /// @todo ALX: complete treatments (mesh generation, etc.)
236 if (std::isnan(value)) {
235 if (std::isnan(value)) {
237 colormap->data()->setAlpha(xIndex, yIndex, 0);
236 colormap->data()->setAlpha(xIndex, yIndex, 0);
238 }
237 }
239 }
238 }
240 }
239 }
241
240
242 dataSeries.unlock();
241 dataSeries.unlock();
243
242
244 // Rescales axes
243 // Rescales axes
245 auto plot = colormap->parentPlot();
244 auto plot = colormap->parentPlot();
246
245
247 if (rescaleAxes) {
246 if (rescaleAxes) {
248 plot->rescaleAxes();
247 plot->rescaleAxes();
249 }
248 }
250
249
251 plot->replot();
250 plot->replot();
252 }
251 }
253 };
252 };
254
253
255 /**
254 /**
256 * Helper used to create/update plottables
255 * Helper used to create/update plottables
257 */
256 */
258 struct IPlottablesHelper {
257 struct IPlottablesHelper {
259 virtual ~IPlottablesHelper() noexcept = default;
258 virtual ~IPlottablesHelper() noexcept = default;
260 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
259 virtual PlottablesMap create(QCustomPlot &plot) const = 0;
261 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
260 virtual void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const = 0;
262 virtual void update(PlottablesMap &plottables, const SqpRange &range,
261 virtual void update(PlottablesMap &plottables, const SqpRange &range,
263 bool rescaleAxes = false) const = 0;
262 bool rescaleAxes = false) const = 0;
264 };
263 };
265
264
266 /**
265 /**
267 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
266 * Default implementation of IPlottablesHelper, which takes data series to create/update plottables
268 * @tparam T the data series' type
267 * @tparam T the data series' type
269 */
268 */
270 template <typename T>
269 template <typename T>
271 struct PlottablesHelper : public IPlottablesHelper {
270 struct PlottablesHelper : public IPlottablesHelper {
272 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
271 explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {}
273
272
274 PlottablesMap create(QCustomPlot &plot) const override
273 PlottablesMap create(QCustomPlot &plot) const override
275 {
274 {
276 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
275 return PlottablesCreator<T>::createPlottables(m_DataSeries, plot);
277 }
276 }
278
277
279 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
278 void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override
280 {
279 {
281 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
280 PlottablesUpdater<T>::updatePlottables(m_DataSeries, plottables, range, rescaleAxes);
282 }
281 }
283
282
284 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
283 void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override
285 {
284 {
286 return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot);
285 return PlottablesUpdater<T>::setPlotYAxisRange(m_DataSeries, xAxisRange, plot);
287 }
286 }
288
287
289 T &m_DataSeries;
288 T &m_DataSeries;
290 };
289 };
291
290
292 /// Creates IPlottablesHelper according to a data series
291 /// Creates IPlottablesHelper according to a data series
293 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
292 std::unique_ptr<IPlottablesHelper> createHelper(std::shared_ptr<IDataSeries> dataSeries) noexcept
294 {
293 {
295 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
294 if (auto scalarSeries = std::dynamic_pointer_cast<ScalarSeries>(dataSeries)) {
296 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
295 return std::make_unique<PlottablesHelper<ScalarSeries> >(*scalarSeries);
297 }
296 }
298 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
297 else if (auto spectrogramSeries = std::dynamic_pointer_cast<SpectrogramSeries>(dataSeries)) {
299 return std::make_unique<PlottablesHelper<SpectrogramSeries> >(*spectrogramSeries);
298 return std::make_unique<PlottablesHelper<SpectrogramSeries> >(*spectrogramSeries);
300 }
299 }
301 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
300 else if (auto vectorSeries = std::dynamic_pointer_cast<VectorSeries>(dataSeries)) {
302 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
301 return std::make_unique<PlottablesHelper<VectorSeries> >(*vectorSeries);
303 }
302 }
304 else {
303 else {
305 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
304 return std::make_unique<PlottablesHelper<IDataSeries> >(*dataSeries);
306 }
305 }
307 }
306 }
308
307
309 } // namespace
308 } // namespace
310
309
311 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
310 PlottablesMap VisualizationGraphHelper::create(std::shared_ptr<Variable> variable,
312 QCustomPlot &plot) noexcept
311 QCustomPlot &plot) noexcept
313 {
312 {
314 if (variable) {
313 if (variable) {
315 auto helper = createHelper(variable->dataSeries());
314 auto helper = createHelper(variable->dataSeries());
316 auto plottables = helper->create(plot);
315 auto plottables = helper->create(plot);
317 return plottables;
316 return plottables;
318 }
317 }
319 else {
318 else {
320 qCDebug(LOG_VisualizationGraphHelper())
319 qCDebug(LOG_VisualizationGraphHelper())
321 << QObject::tr("Can't create graph plottables : the variable is null");
320 << QObject::tr("Can't create graph plottables : the variable is null");
322 return PlottablesMap{};
321 return PlottablesMap{};
323 }
322 }
324 }
323 }
325
324
326 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
325 void VisualizationGraphHelper::setYAxisRange(std::shared_ptr<Variable> variable,
327 QCustomPlot &plot) noexcept
326 QCustomPlot &plot) noexcept
328 {
327 {
329 if (variable) {
328 if (variable) {
330 auto helper = createHelper(variable->dataSeries());
329 auto helper = createHelper(variable->dataSeries());
331 helper->setYAxisRange(variable->range(), plot);
330 helper->setYAxisRange(variable->range(), plot);
332 }
331 }
333 else {
332 else {
334 qCDebug(LOG_VisualizationGraphHelper())
333 qCDebug(LOG_VisualizationGraphHelper())
335 << QObject::tr("Can't set y-axis range of plot: the variable is null");
334 << QObject::tr("Can't set y-axis range of plot: the variable is null");
336 }
335 }
337 }
336 }
338
337
339 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
338 void VisualizationGraphHelper::updateData(PlottablesMap &plottables,
340 std::shared_ptr<IDataSeries> dataSeries,
339 std::shared_ptr<IDataSeries> dataSeries,
341 const SqpRange &dateTime)
340 const SqpRange &dateTime)
342 {
341 {
343 auto helper = createHelper(dataSeries);
342 auto helper = createHelper(dataSeries);
344 helper->update(plottables, dateTime);
343 helper->update(plottables, dateTime);
345 }
344 }
General Comments 0
You need to be logged in to leave comments. Login now