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