##// END OF EJS Templates
Moves cbegin() and cend() declarations in IDataSeries...
Alexandre Leroux -
r597:f66c81d8ced0
parent child
Show More
@@ -1,315 +1,328
1 #ifndef SCIQLOP_ARRAYDATA_H
1 #ifndef SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
2 #define SCIQLOP_ARRAYDATA_H
3
3
4 #include <Common/SortUtils.h>
4 #include <Common/SortUtils.h>
5
5
6 #include <QReadLocker>
6 #include <QReadLocker>
7 #include <QReadWriteLock>
7 #include <QReadWriteLock>
8 #include <QVector>
8 #include <QVector>
9
9
10 #include <memory>
10 #include <memory>
11
11
12 template <int Dim>
12 template <int Dim>
13 class ArrayData;
13 class ArrayData;
14
14
15 using DataContainer = QVector<QVector<double> >;
15 using DataContainer = QVector<QVector<double> >;
16
16
17 namespace arraydata_detail {
17 namespace arraydata_detail {
18
18
19 /// Struct used to sort ArrayData
19 /// Struct used to sort ArrayData
20 template <int Dim>
20 template <int Dim>
21 struct Sort {
21 struct Sort {
22 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data,
22 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data,
23 const std::vector<int> &sortPermutation)
23 const std::vector<int> &sortPermutation)
24 {
24 {
25 auto nbComponents = data.size();
25 auto nbComponents = data.size();
26 auto sortedData = DataContainer(nbComponents);
26 auto sortedData = DataContainer(nbComponents);
27
27
28 for (auto i = 0; i < nbComponents; ++i) {
28 for (auto i = 0; i < nbComponents; ++i) {
29 sortedData[i] = SortUtils::sort(data.at(i), sortPermutation);
29 sortedData[i] = SortUtils::sort(data.at(i), sortPermutation);
30 }
30 }
31
31
32 return std::make_shared<ArrayData<Dim> >(std::move(sortedData));
32 return std::make_shared<ArrayData<Dim> >(std::move(sortedData));
33 }
33 }
34 };
34 };
35
35
36 /// Specialization for uni-dimensional ArrayData
36 /// Specialization for uni-dimensional ArrayData
37 template <>
37 template <>
38 struct Sort<1> {
38 struct Sort<1> {
39 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data,
39 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data,
40 const std::vector<int> &sortPermutation)
40 const std::vector<int> &sortPermutation)
41 {
41 {
42 return std::make_shared<ArrayData<1> >(SortUtils::sort(data.at(0), sortPermutation));
42 return std::make_shared<ArrayData<1> >(SortUtils::sort(data.at(0), sortPermutation));
43 }
43 }
44 };
44 };
45
45
46 } // namespace arraydata_detail
46 } // namespace arraydata_detail
47
47
48 /**
48 /**
49 * @brief The ArrayData class represents a dataset for a data series.
49 * @brief The ArrayData class represents a dataset for a data series.
50 *
50 *
51 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
51 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
52 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
52 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
53 * number of values
53 * number of values
54 *
54 *
55 * @tparam Dim the dimension of the ArrayData (one or two)
55 * @tparam Dim the dimension of the ArrayData (one or two)
56 * @sa IDataSeries
56 * @sa IDataSeries
57 */
57 */
58 template <int Dim>
58 template <int Dim>
59 class ArrayData {
59 class ArrayData {
60 public:
60 public:
61 class IteratorValue {
61 class IteratorValue {
62 public:
62 public:
63 explicit IteratorValue(const DataContainer &container, bool begin) : m_Its{}
63 explicit IteratorValue(const DataContainer &container, bool begin) : m_Its{}
64 {
64 {
65 for (auto i = 0; i < container.size(); ++i) {
65 for (auto i = 0; i < container.size(); ++i) {
66 m_Its.push_back(begin ? container.at(i).cbegin() : container.at(i).cend());
66 m_Its.push_back(begin ? container.at(i).cbegin() : container.at(i).cend());
67 }
67 }
68 }
68 }
69
69
70 double at(int index) const { return *m_Its.at(index); }
70 double at(int index) const { return *m_Its.at(index); }
71 double first() const { return *m_Its.front(); }
71 double first() const { return *m_Its.front(); }
72
72
73 void next()
73 void next()
74 {
74 {
75 for (auto &it : m_Its) {
75 for (auto &it : m_Its) {
76 ++it;
76 ++it;
77 }
77 }
78 }
78 }
79
79
80 void prev()
81 {
82 for (auto &it : m_Its) {
83 --it;
84 }
85 }
86
80 bool operator==(const IteratorValue &other) const { return m_Its == other.m_Its; }
87 bool operator==(const IteratorValue &other) const { return m_Its == other.m_Its; }
81
88
82 private:
89 private:
83 std::vector<DataContainer::value_type::const_iterator> m_Its;
90 std::vector<DataContainer::value_type::const_iterator> m_Its;
84 };
91 };
85
92
86 class Iterator {
93 class Iterator {
87 public:
94 public:
88 using iterator_category = std::forward_iterator_tag;
95 using iterator_category = std::forward_iterator_tag;
89 using value_type = const IteratorValue;
96 using value_type = const IteratorValue;
90 using difference_type = std::ptrdiff_t;
97 using difference_type = std::ptrdiff_t;
91 using pointer = value_type *;
98 using pointer = value_type *;
92 using reference = value_type &;
99 using reference = value_type &;
93
100
94 Iterator(const DataContainer &container, bool begin) : m_CurrentValue{container, begin} {}
101 Iterator(const DataContainer &container, bool begin) : m_CurrentValue{container, begin} {}
95
102
96 virtual ~Iterator() noexcept = default;
103 virtual ~Iterator() noexcept = default;
97 Iterator(const Iterator &) = default;
104 Iterator(const Iterator &) = default;
98 Iterator(Iterator &&) = default;
105 Iterator(Iterator &&) = default;
99 Iterator &operator=(const Iterator &) = default;
106 Iterator &operator=(const Iterator &) = default;
100 Iterator &operator=(Iterator &&) = default;
107 Iterator &operator=(Iterator &&) = default;
101
108
102 Iterator &operator++()
109 Iterator &operator++()
103 {
110 {
104 m_CurrentValue.next();
111 m_CurrentValue.next();
105 return *this;
112 return *this;
106 }
113 }
107
114
115 Iterator &operator--()
116 {
117 m_CurrentValue.prev();
118 return *this;
119 }
120
108 pointer operator->() const { return &m_CurrentValue; }
121 pointer operator->() const { return &m_CurrentValue; }
109 reference operator*() const { return m_CurrentValue; }
122 reference operator*() const { return m_CurrentValue; }
110
123
111 bool operator==(const Iterator &other) const
124 bool operator==(const Iterator &other) const
112 {
125 {
113 return m_CurrentValue == other.m_CurrentValue;
126 return m_CurrentValue == other.m_CurrentValue;
114 }
127 }
115
128
116 bool operator!=(const Iterator &other) const { return !(*this == other); }
129 bool operator!=(const Iterator &other) const { return !(*this == other); }
117
130
118 private:
131 private:
119 IteratorValue m_CurrentValue;
132 IteratorValue m_CurrentValue;
120 };
133 };
121
134
122 // ///// //
135 // ///// //
123 // Ctors //
136 // Ctors //
124 // ///// //
137 // ///// //
125
138
126 /**
139 /**
127 * Ctor for a unidimensional ArrayData
140 * Ctor for a unidimensional ArrayData
128 * @param data the data the ArrayData will hold
141 * @param data the data the ArrayData will hold
129 */
142 */
130 template <int D = Dim, typename = std::enable_if_t<D == 1> >
143 template <int D = Dim, typename = std::enable_if_t<D == 1> >
131 explicit ArrayData(QVector<double> data) : m_Data{1, QVector<double>{}}
144 explicit ArrayData(QVector<double> data) : m_Data{1, QVector<double>{}}
132 {
145 {
133 m_Data[0] = std::move(data);
146 m_Data[0] = std::move(data);
134 }
147 }
135
148
136 /**
149 /**
137 * Ctor for a two-dimensional ArrayData. The number of components (number of vectors) must be
150 * Ctor for a two-dimensional ArrayData. The number of components (number of vectors) must be
138 * greater than 2 and each component must have the same number of values
151 * greater than 2 and each component must have the same number of values
139 * @param data the data the ArrayData will hold
152 * @param data the data the ArrayData will hold
140 * @throws std::invalid_argument if the number of components is less than 2
153 * @throws std::invalid_argument if the number of components is less than 2
141 * @remarks if the number of values is not the same for each component, no value is set
154 * @remarks if the number of values is not the same for each component, no value is set
142 */
155 */
143 template <int D = Dim, typename = std::enable_if_t<D == 2> >
156 template <int D = Dim, typename = std::enable_if_t<D == 2> >
144 explicit ArrayData(DataContainer data)
157 explicit ArrayData(DataContainer data)
145 {
158 {
146 auto nbComponents = data.size();
159 auto nbComponents = data.size();
147 if (nbComponents < 2) {
160 if (nbComponents < 2) {
148 throw std::invalid_argument{
161 throw std::invalid_argument{
149 QString{"A multidimensional ArrayData must have at least 2 components (found: %1"}
162 QString{"A multidimensional ArrayData must have at least 2 components (found: %1"}
150 .arg(data.size())
163 .arg(data.size())
151 .toStdString()};
164 .toStdString()};
152 }
165 }
153
166
154 auto nbValues = data.front().size();
167 auto nbValues = data.front().size();
155 if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) {
168 if (std::all_of(data.cbegin(), data.cend(), [nbValues](const auto &component) {
156 return component.size() == nbValues;
169 return component.size() == nbValues;
157 })) {
170 })) {
158 m_Data = std::move(data);
171 m_Data = std::move(data);
159 }
172 }
160 else {
173 else {
161 m_Data = DataContainer{nbComponents, QVector<double>{}};
174 m_Data = DataContainer{nbComponents, QVector<double>{}};
162 }
175 }
163 }
176 }
164
177
165 /// Copy ctor
178 /// Copy ctor
166 explicit ArrayData(const ArrayData &other)
179 explicit ArrayData(const ArrayData &other)
167 {
180 {
168 QReadLocker otherLocker{&other.m_Lock};
181 QReadLocker otherLocker{&other.m_Lock};
169 m_Data = other.m_Data;
182 m_Data = other.m_Data;
170 }
183 }
171
184
172 // /////////////// //
185 // /////////////// //
173 // General methods //
186 // General methods //
174 // /////////////// //
187 // /////////////// //
175
188
176 /**
189 /**
177 * Merges into the array data an other array data. The two array datas must have the same number
190 * Merges into the array data an other array data. The two array datas must have the same number
178 * of components so the merge can be done
191 * of components so the merge can be done
179 * @param other the array data to merge with
192 * @param other the array data to merge with
180 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
193 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
181 * inserted at the end
194 * inserted at the end
182 */
195 */
183 void add(const ArrayData<Dim> &other, bool prepend = false)
196 void add(const ArrayData<Dim> &other, bool prepend = false)
184 {
197 {
185 QWriteLocker locker{&m_Lock};
198 QWriteLocker locker{&m_Lock};
186 QReadLocker otherLocker{&other.m_Lock};
199 QReadLocker otherLocker{&other.m_Lock};
187
200
188 auto nbComponents = m_Data.size();
201 auto nbComponents = m_Data.size();
189 if (nbComponents != other.m_Data.size()) {
202 if (nbComponents != other.m_Data.size()) {
190 return;
203 return;
191 }
204 }
192
205
193 for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) {
206 for (auto componentIndex = 0; componentIndex < nbComponents; ++componentIndex) {
194 if (prepend) {
207 if (prepend) {
195 const auto &otherData = other.data(componentIndex);
208 const auto &otherData = other.data(componentIndex);
196 const auto otherDataSize = otherData.size();
209 const auto otherDataSize = otherData.size();
197
210
198 auto &data = m_Data[componentIndex];
211 auto &data = m_Data[componentIndex];
199 data.insert(data.begin(), otherDataSize, 0.);
212 data.insert(data.begin(), otherDataSize, 0.);
200
213
201 for (auto i = 0; i < otherDataSize; ++i) {
214 for (auto i = 0; i < otherDataSize; ++i) {
202 data.replace(i, otherData.at(i));
215 data.replace(i, otherData.at(i));
203 }
216 }
204 }
217 }
205 else {
218 else {
206 m_Data[componentIndex] += other.data(componentIndex);
219 m_Data[componentIndex] += other.data(componentIndex);
207 }
220 }
208 }
221 }
209 }
222 }
210
223
211 void clear()
224 void clear()
212 {
225 {
213 QWriteLocker locker{&m_Lock};
226 QWriteLocker locker{&m_Lock};
214
227
215 auto nbComponents = m_Data.size();
228 auto nbComponents = m_Data.size();
216 for (auto i = 0; i < nbComponents; ++i) {
229 for (auto i = 0; i < nbComponents; ++i) {
217 m_Data[i].clear();
230 m_Data[i].clear();
218 }
231 }
219 }
232 }
220
233
221 int componentCount() const noexcept { return m_Data.size(); }
234 int componentCount() const noexcept { return m_Data.size(); }
222
235
223 /**
236 /**
224 * @return the data of a component
237 * @return the data of a component
225 * @param componentIndex the index of the component to retrieve the data
238 * @param componentIndex the index of the component to retrieve the data
226 * @return the component's data, empty vector if the index is invalid
239 * @return the component's data, empty vector if the index is invalid
227 */
240 */
228 QVector<double> data(int componentIndex) const noexcept
241 QVector<double> data(int componentIndex) const noexcept
229 {
242 {
230 QReadLocker locker{&m_Lock};
243 QReadLocker locker{&m_Lock};
231
244
232 return (componentIndex >= 0 && componentIndex < m_Data.size()) ? m_Data.at(componentIndex)
245 return (componentIndex >= 0 && componentIndex < m_Data.size()) ? m_Data.at(componentIndex)
233 : QVector<double>{};
246 : QVector<double>{};
234 }
247 }
235
248
236 /// @return the size (i.e. number of values) of a single component
249 /// @return the size (i.e. number of values) of a single component
237 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
250 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
238 int size() const
251 int size() const
239 {
252 {
240 QReadLocker locker{&m_Lock};
253 QReadLocker locker{&m_Lock};
241 return m_Data[0].size();
254 return m_Data[0].size();
242 }
255 }
243
256
244 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
257 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
245 {
258 {
246 QReadLocker locker{&m_Lock};
259 QReadLocker locker{&m_Lock};
247 return arraydata_detail::Sort<Dim>::sort(m_Data, sortPermutation);
260 return arraydata_detail::Sort<Dim>::sort(m_Data, sortPermutation);
248 }
261 }
249
262
250 // ///////// //
263 // ///////// //
251 // Iterators //
264 // Iterators //
252 // ///////// //
265 // ///////// //
253
266
254 Iterator cbegin() const { return Iterator{m_Data, true}; }
267 Iterator cbegin() const { return Iterator{m_Data, true}; }
255 Iterator cend() const { return Iterator{m_Data, false}; }
268 Iterator cend() const { return Iterator{m_Data, false}; }
256
269
257 // ///////////// //
270 // ///////////// //
258 // 1-dim methods //
271 // 1-dim methods //
259 // ///////////// //
272 // ///////////// //
260
273
261 /**
274 /**
262 * @return the data at a specified index
275 * @return the data at a specified index
263 * @remarks index must be a valid position
276 * @remarks index must be a valid position
264 * @remarks this method is only available for a unidimensional ArrayData
277 * @remarks this method is only available for a unidimensional ArrayData
265 */
278 */
266 template <int D = Dim, typename = std::enable_if_t<D == 1> >
279 template <int D = Dim, typename = std::enable_if_t<D == 1> >
267 double at(int index) const noexcept
280 double at(int index) const noexcept
268 {
281 {
269 QReadLocker locker{&m_Lock};
282 QReadLocker locker{&m_Lock};
270 return m_Data[0].at(index);
283 return m_Data[0].at(index);
271 }
284 }
272
285
273 /**
286 /**
274 * @return the data as a vector, as a const reference
287 * @return the data as a vector, as a const reference
275 * @remarks this method is only available for a unidimensional ArrayData
288 * @remarks this method is only available for a unidimensional ArrayData
276 */
289 */
277 template <int D = Dim, typename = std::enable_if_t<D == 1> >
290 template <int D = Dim, typename = std::enable_if_t<D == 1> >
278 const QVector<double> &cdata() const noexcept
291 const QVector<double> &cdata() const noexcept
279 {
292 {
280 QReadLocker locker{&m_Lock};
293 QReadLocker locker{&m_Lock};
281 return m_Data.at(0);
294 return m_Data.at(0);
282 }
295 }
283
296
284 /**
297 /**
285 * @return the data as a vector
298 * @return the data as a vector
286 * @remarks this method is only available for a unidimensional ArrayData
299 * @remarks this method is only available for a unidimensional ArrayData
287 */
300 */
288 template <int D = Dim, typename = std::enable_if_t<D == 1> >
301 template <int D = Dim, typename = std::enable_if_t<D == 1> >
289 QVector<double> data() const noexcept
302 QVector<double> data() const noexcept
290 {
303 {
291 QReadLocker locker{&m_Lock};
304 QReadLocker locker{&m_Lock};
292 return m_Data[0];
305 return m_Data[0];
293 }
306 }
294
307
295 // ///////////// //
308 // ///////////// //
296 // 2-dim methods //
309 // 2-dim methods //
297 // ///////////// //
310 // ///////////// //
298
311
299 /**
312 /**
300 * @return the data
313 * @return the data
301 * @remarks this method is only available for a two-dimensional ArrayData
314 * @remarks this method is only available for a two-dimensional ArrayData
302 */
315 */
303 template <int D = Dim, typename = std::enable_if_t<D == 2> >
316 template <int D = Dim, typename = std::enable_if_t<D == 2> >
304 DataContainer data() const noexcept
317 DataContainer data() const noexcept
305 {
318 {
306 QReadLocker locker{&m_Lock};
319 QReadLocker locker{&m_Lock};
307 return m_Data;
320 return m_Data;
308 }
321 }
309
322
310 private:
323 private:
311 DataContainer m_Data;
324 DataContainer m_Data;
312 mutable QReadWriteLock m_Lock;
325 mutable QReadWriteLock m_Lock;
313 };
326 };
314
327
315 #endif // SCIQLOP_ARRAYDATA_H
328 #endif // SCIQLOP_ARRAYDATA_H
@@ -1,287 +1,287
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/IDataSeries.h>
9 #include <Data/IDataSeries.h>
10
10
11 #include <QLoggingCategory>
11 #include <QLoggingCategory>
12 #include <QReadLocker>
12 #include <QReadLocker>
13 #include <QReadWriteLock>
13 #include <QReadWriteLock>
14 #include <memory>
14 #include <memory>
15
15
16 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
16 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
17 // definitions with inheritance. Inline method is used instead
17 // definitions with inheritance. Inline method is used instead
18 inline const QLoggingCategory &LOG_DataSeries()
18 inline const QLoggingCategory &LOG_DataSeries()
19 {
19 {
20 static const QLoggingCategory category{"DataSeries"};
20 static const QLoggingCategory category{"DataSeries"};
21 return category;
21 return category;
22 }
22 }
23
23
24 template <int Dim>
24 template <int Dim>
25 class DataSeries;
25 class DataSeries;
26
26
27 namespace dataseries_detail {
27 namespace dataseries_detail {
28
28
29 template <int Dim>
29 template <int Dim>
30 class IteratorValue : public DataSeriesIteratorValue::Impl {
30 class IteratorValue : public DataSeriesIteratorValue::Impl {
31 public:
31 public:
32 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
32 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
33 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
33 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
34 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
34 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
35 : dataSeries.valuesData()->cend())
35 : dataSeries.valuesData()->cend())
36 {
36 {
37 }
37 }
38 IteratorValue(const IteratorValue &other) = default;
38 IteratorValue(const IteratorValue &other) = default;
39
39
40 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
40 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
41 {
41 {
42 return std::make_unique<IteratorValue<Dim> >(*this);
42 return std::make_unique<IteratorValue<Dim> >(*this);
43 }
43 }
44
44
45 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
45 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
46 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
46 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
47 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
47 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
48 }
48 }
49 catch (const std::bad_cast &) {
49 catch (const std::bad_cast &) {
50 return false;
50 return false;
51 }
51 }
52
52
53 void next() override
53 void next() override
54 {
54 {
55 ++m_XIt;
55 ++m_XIt;
56 ++m_ValuesIt;
56 ++m_ValuesIt;
57 }
57 }
58
58
59 void prev() override
59 void prev() override
60 {
60 {
61 --m_XIt;
61 --m_XIt;
62 --m_ValuesIt;
62 --m_ValuesIt;
63 }
63 }
64
64
65 double x() const override { return m_XIt->at(0); }
65 double x() const override { return m_XIt->at(0); }
66 double value() const override { return m_ValuesIt->at(0); }
66 double value() const override { return m_ValuesIt->at(0); }
67 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
67 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
68
68
69 private:
69 private:
70 ArrayData<1>::Iterator m_XIt;
70 ArrayData<1>::Iterator m_XIt;
71 typename ArrayData<Dim>::Iterator m_ValuesIt;
71 typename ArrayData<Dim>::Iterator m_ValuesIt;
72 };
72 };
73 } // namespace dataseries_detail
73 } // namespace dataseries_detail
74
74
75 /**
75 /**
76 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
76 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
77 *
77 *
78 * It proposes to set a dimension for the values ​​data.
78 * It proposes to set a dimension for the values ​​data.
79 *
79 *
80 * A DataSeries is always sorted on its x-axis data.
80 * A DataSeries is always sorted on its x-axis data.
81 *
81 *
82 * @tparam Dim The dimension of the values data
82 * @tparam Dim The dimension of the values data
83 *
83 *
84 */
84 */
85 template <int Dim>
85 template <int Dim>
86 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
86 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
87 public:
87 public:
88 /// @sa IDataSeries::xAxisData()
88 /// @sa IDataSeries::xAxisData()
89 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
89 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
90 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
90 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
91
91
92 /// @sa IDataSeries::xAxisUnit()
92 /// @sa IDataSeries::xAxisUnit()
93 Unit xAxisUnit() const override { return m_XAxisUnit; }
93 Unit xAxisUnit() const override { return m_XAxisUnit; }
94
94
95 /// @return the values dataset
95 /// @return the values dataset
96 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
96 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
97 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
97 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
98
98
99 /// @sa IDataSeries::valuesUnit()
99 /// @sa IDataSeries::valuesUnit()
100 Unit valuesUnit() const override { return m_ValuesUnit; }
100 Unit valuesUnit() const override { return m_ValuesUnit; }
101
101
102
102
103 SqpRange range() const override
103 SqpRange range() const override
104 {
104 {
105 if (!m_XAxisData->cdata().isEmpty()) {
105 if (!m_XAxisData->cdata().isEmpty()) {
106 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
106 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
107 }
107 }
108
108
109 return SqpRange{};
109 return SqpRange{};
110 }
110 }
111
111
112 void clear()
112 void clear()
113 {
113 {
114 m_XAxisData->clear();
114 m_XAxisData->clear();
115 m_ValuesData->clear();
115 m_ValuesData->clear();
116 }
116 }
117
117
118 /// Merges into the data series an other data series
118 /// Merges into the data series an other data series
119 /// @remarks the data series to merge with is cleared after the operation
119 /// @remarks the data series to merge with is cleared after the operation
120 void merge(IDataSeries *dataSeries) override
120 void merge(IDataSeries *dataSeries) override
121 {
121 {
122 dataSeries->lockWrite();
122 dataSeries->lockWrite();
123 lockWrite();
123 lockWrite();
124
124
125 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
125 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
126 const auto &otherXAxisData = other->xAxisData()->cdata();
126 const auto &otherXAxisData = other->xAxisData()->cdata();
127 const auto &xAxisData = m_XAxisData->cdata();
127 const auto &xAxisData = m_XAxisData->cdata();
128
128
129 // As data series are sorted, we can improve performances of merge, by call the sort
129 // As data series are sorted, we can improve performances of merge, by call the sort
130 // method only if the two data series overlap.
130 // method only if the two data series overlap.
131 if (!otherXAxisData.empty()) {
131 if (!otherXAxisData.empty()) {
132 auto firstValue = otherXAxisData.front();
132 auto firstValue = otherXAxisData.front();
133 auto lastValue = otherXAxisData.back();
133 auto lastValue = otherXAxisData.back();
134
134
135 auto xAxisDataBegin = xAxisData.cbegin();
135 auto xAxisDataBegin = xAxisData.cbegin();
136 auto xAxisDataEnd = xAxisData.cend();
136 auto xAxisDataEnd = xAxisData.cend();
137
137
138 bool prepend;
138 bool prepend;
139 bool sortNeeded;
139 bool sortNeeded;
140
140
141 if (std::lower_bound(xAxisDataBegin, xAxisDataEnd, firstValue) == xAxisDataEnd) {
141 if (std::lower_bound(xAxisDataBegin, xAxisDataEnd, firstValue) == xAxisDataEnd) {
142 // Other data series if after data series
142 // Other data series if after data series
143 prepend = false;
143 prepend = false;
144 sortNeeded = false;
144 sortNeeded = false;
145 }
145 }
146 else if (std::upper_bound(xAxisDataBegin, xAxisDataEnd, lastValue)
146 else if (std::upper_bound(xAxisDataBegin, xAxisDataEnd, lastValue)
147 == xAxisDataBegin) {
147 == xAxisDataBegin) {
148 // Other data series if before data series
148 // Other data series if before data series
149 prepend = true;
149 prepend = true;
150 sortNeeded = false;
150 sortNeeded = false;
151 }
151 }
152 else {
152 else {
153 // The two data series overlap
153 // The two data series overlap
154 prepend = false;
154 prepend = false;
155 sortNeeded = true;
155 sortNeeded = true;
156 }
156 }
157
157
158 // Makes the merge
158 // Makes the merge
159 m_XAxisData->add(*other->xAxisData(), prepend);
159 m_XAxisData->add(*other->xAxisData(), prepend);
160 m_ValuesData->add(*other->valuesData(), prepend);
160 m_ValuesData->add(*other->valuesData(), prepend);
161
161
162 if (sortNeeded) {
162 if (sortNeeded) {
163 sort();
163 sort();
164 }
164 }
165 }
165 }
166
166
167 // Clears the other data series
167 // Clears the other data series
168 other->clear();
168 other->clear();
169 }
169 }
170 else {
170 else {
171 qCWarning(LOG_DataSeries())
171 qCWarning(LOG_DataSeries())
172 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
172 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
173 }
173 }
174 unlock();
174 unlock();
175 dataSeries->unlock();
175 dataSeries->unlock();
176 }
176 }
177
177
178 // ///////// //
178 // ///////// //
179 // Iterators //
179 // Iterators //
180 // ///////// //
180 // ///////// //
181
181
182 DataSeriesIterator cbegin() const override
182 DataSeriesIterator cbegin() const override
183 {
183 {
184 return DataSeriesIterator{DataSeriesIteratorValue{
184 return DataSeriesIterator{DataSeriesIteratorValue{
185 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, true)}};
185 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, true)}};
186 }
186 }
187
187
188 DataSeriesIterator cend() const override
188 DataSeriesIterator cend() const override
189 {
189 {
190 return DataSeriesIterator{DataSeriesIteratorValue{
190 return DataSeriesIterator{DataSeriesIteratorValue{
191 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, false)}};
191 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, false)}};
192 }
192 }
193
193
194 std::pair<Iterator, Iterator> subData(double min, double max) const
194 std::pair<DataSeriesIterator, DataSeriesIterator> subData(double min, double max) const override
195 {
195 {
196 if (min > max) {
196 if (min > max) {
197 std::swap(min, max);
197 std::swap(min, max);
198 }
198 }
199
199
200 auto begin = cbegin();
200 auto begin = cbegin();
201 auto end = cend();
201 auto end = cend();
202
202
203 auto lowerIt
203 auto lowerIt
204 = std::lower_bound(begin, end, min, [](const auto &itValue, const auto &value) {
204 = std::lower_bound(begin, end, min, [](const auto &itValue, const auto &value) {
205 return itValue.x() < value;
205 return itValue.x() < value;
206 });
206 });
207 auto upperIt
207 auto upperIt
208 = std::upper_bound(begin, end, max, [](const auto &value, const auto &itValue) {
208 = std::upper_bound(begin, end, max, [](const auto &value, const auto &itValue) {
209 return value < itValue.x();
209 return value < itValue.x();
210 });
210 });
211
211
212 return std::make_pair(lowerIt, upperIt);
212 return std::make_pair(lowerIt, upperIt);
213 }
213 }
214
214
215 // /////// //
215 // /////// //
216 // Mutexes //
216 // Mutexes //
217 // /////// //
217 // /////// //
218
218
219 virtual void lockRead() { m_Lock.lockForRead(); }
219 virtual void lockRead() { m_Lock.lockForRead(); }
220 virtual void lockWrite() { m_Lock.lockForWrite(); }
220 virtual void lockWrite() { m_Lock.lockForWrite(); }
221 virtual void unlock() { m_Lock.unlock(); }
221 virtual void unlock() { m_Lock.unlock(); }
222
222
223 protected:
223 protected:
224 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
224 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
225 /// DataSeries with no values will be created.
225 /// DataSeries with no values will be created.
226 /// @remarks data series is automatically sorted on its x-axis data
226 /// @remarks data series is automatically sorted on its x-axis data
227 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
227 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
228 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
228 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
229 : m_XAxisData{xAxisData},
229 : m_XAxisData{xAxisData},
230 m_XAxisUnit{xAxisUnit},
230 m_XAxisUnit{xAxisUnit},
231 m_ValuesData{valuesData},
231 m_ValuesData{valuesData},
232 m_ValuesUnit{valuesUnit}
232 m_ValuesUnit{valuesUnit}
233 {
233 {
234 if (m_XAxisData->size() != m_ValuesData->size()) {
234 if (m_XAxisData->size() != m_ValuesData->size()) {
235 clear();
235 clear();
236 }
236 }
237
237
238 // Sorts data if it's not the case
238 // Sorts data if it's not the case
239 const auto &xAxisCData = m_XAxisData->cdata();
239 const auto &xAxisCData = m_XAxisData->cdata();
240 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
240 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
241 sort();
241 sort();
242 }
242 }
243 }
243 }
244
244
245 /// Copy ctor
245 /// Copy ctor
246 explicit DataSeries(const DataSeries<Dim> &other)
246 explicit DataSeries(const DataSeries<Dim> &other)
247 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
247 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
248 m_XAxisUnit{other.m_XAxisUnit},
248 m_XAxisUnit{other.m_XAxisUnit},
249 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
249 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
250 m_ValuesUnit{other.m_ValuesUnit}
250 m_ValuesUnit{other.m_ValuesUnit}
251 {
251 {
252 // Since a series is ordered from its construction and is always ordered, it is not
252 // Since a series is ordered from its construction and is always ordered, it is not
253 // necessary to call the sort method here ('other' is sorted)
253 // necessary to call the sort method here ('other' is sorted)
254 }
254 }
255
255
256 /// Assignment operator
256 /// Assignment operator
257 template <int D>
257 template <int D>
258 DataSeries &operator=(DataSeries<D> other)
258 DataSeries &operator=(DataSeries<D> other)
259 {
259 {
260 std::swap(m_XAxisData, other.m_XAxisData);
260 std::swap(m_XAxisData, other.m_XAxisData);
261 std::swap(m_XAxisUnit, other.m_XAxisUnit);
261 std::swap(m_XAxisUnit, other.m_XAxisUnit);
262 std::swap(m_ValuesData, other.m_ValuesData);
262 std::swap(m_ValuesData, other.m_ValuesData);
263 std::swap(m_ValuesUnit, other.m_ValuesUnit);
263 std::swap(m_ValuesUnit, other.m_ValuesUnit);
264
264
265 return *this;
265 return *this;
266 }
266 }
267
267
268 private:
268 private:
269 /**
269 /**
270 * Sorts data series on its x-axis data
270 * Sorts data series on its x-axis data
271 */
271 */
272 void sort() noexcept
272 void sort() noexcept
273 {
273 {
274 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
274 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
275 m_XAxisData = m_XAxisData->sort(permutation);
275 m_XAxisData = m_XAxisData->sort(permutation);
276 m_ValuesData = m_ValuesData->sort(permutation);
276 m_ValuesData = m_ValuesData->sort(permutation);
277 }
277 }
278
278
279 std::shared_ptr<ArrayData<1> > m_XAxisData;
279 std::shared_ptr<ArrayData<1> > m_XAxisData;
280 Unit m_XAxisUnit;
280 Unit m_XAxisUnit;
281 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
281 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
282 Unit m_ValuesUnit;
282 Unit m_ValuesUnit;
283
283
284 QReadWriteLock m_Lock;
284 QReadWriteLock m_Lock;
285 };
285 };
286
286
287 #endif // SCIQLOP_DATASERIES_H
287 #endif // SCIQLOP_DATASERIES_H
@@ -1,73 +1,88
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/SqpRange.h>
6 #include <Data/SqpRange.h>
6
7
7 #include <memory>
8 #include <memory>
8
9
9 #include <QString>
10 #include <QString>
10
11
11 template <int Dim>
12 template <int Dim>
12 class ArrayData;
13 class ArrayData;
13
14
14 struct Unit {
15 struct Unit {
15 explicit Unit(const QString &name = {}, bool timeUnit = false)
16 explicit Unit(const QString &name = {}, bool timeUnit = false)
16 : m_Name{name}, m_TimeUnit{timeUnit}
17 : m_Name{name}, m_TimeUnit{timeUnit}
17 {
18 {
18 }
19 }
19
20
20 inline bool operator==(const Unit &other) const
21 inline bool operator==(const Unit &other) const
21 {
22 {
22 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
23 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
23 }
24 }
24 inline bool operator!=(const Unit &other) const { return !(*this == other); }
25 inline bool operator!=(const Unit &other) const { return !(*this == other); }
25
26
26 QString m_Name; ///< Unit name
27 QString m_Name; ///< Unit name
27 bool m_TimeUnit; ///< The unit is a unit of time (UTC)
28 bool m_TimeUnit; ///< The unit is a unit of time (UTC)
28 };
29 };
29
30
30 /**
31 /**
31 * @brief The IDataSeries aims to declare a data series.
32 * @brief The IDataSeries aims to declare a data series.
32 *
33 *
33 * A data series is an entity that contains at least :
34 * A data series is an entity that contains at least :
34 * - one dataset representing the x-axis
35 * - one dataset representing the x-axis
35 * - one dataset representing the values
36 * - one dataset representing the values
36 *
37 *
37 * Each dataset is represented by an ArrayData, and is associated with a unit.
38 * Each dataset is represented by an ArrayData, and is associated with a unit.
38 *
39 *
39 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
40 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
40 * IDataSeries. The x-axis dataset is always unidimensional.
41 * IDataSeries. The x-axis dataset is always unidimensional.
41 *
42 *
42 * @sa ArrayData
43 * @sa ArrayData
43 */
44 */
44 class IDataSeries {
45 class IDataSeries {
45 public:
46 public:
46 virtual ~IDataSeries() noexcept = default;
47 virtual ~IDataSeries() noexcept = default;
47
48
48 /// Returns the x-axis dataset
49 /// Returns the x-axis dataset
49 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
50 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
50
51
51 /// Returns the x-axis dataset (as const)
52 /// Returns the x-axis dataset (as const)
52 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
53 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
53
54
54 virtual Unit xAxisUnit() const = 0;
55 virtual Unit xAxisUnit() const = 0;
55
56
56 virtual Unit valuesUnit() const = 0;
57 virtual Unit valuesUnit() const = 0;
57
58
58 virtual void merge(IDataSeries *dataSeries) = 0;
59 virtual void merge(IDataSeries *dataSeries) = 0;
59 /// @todo Review the name and signature of this method
60 /// @todo Review the name and signature of this method
60 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
61 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
61
62
62 virtual std::unique_ptr<IDataSeries> clone() const = 0;
63 virtual std::unique_ptr<IDataSeries> clone() const = 0;
63 virtual SqpRange range() const = 0;
64 virtual SqpRange range() const = 0;
64
65
66 // ///////// //
67 // Iterators //
68 // ///////// //
69
70 virtual DataSeriesIterator cbegin() const = 0;
71 virtual DataSeriesIterator cend() const = 0;
72
73 virtual std::pair<DataSeriesIterator, DataSeriesIterator> subData(double min,
74 double max) const = 0;
75
76 // /////// //
77 // Mutexes //
78 // /////// //
79
65 virtual void lockRead() = 0;
80 virtual void lockRead() = 0;
66 virtual void lockWrite() = 0;
81 virtual void lockWrite() = 0;
67 virtual void unlock() = 0;
82 virtual void unlock() = 0;
68 };
83 };
69
84
70 // Required for using shared_ptr in signals/slots
85 // Required for using shared_ptr in signals/slots
71 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
86 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
72
87
73 #endif // SCIQLOP_IDATASERIES_H
88 #endif // SCIQLOP_IDATASERIES_H
General Comments 0
You need to be logged in to leave comments. Login now