##// END OF EJS Templates
Updates merge method...
Alexandre Leroux -
r646:cb05b478d08e
parent child
Show More
@@ -1,378 +1,369
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 "Data/ArrayDataIterator.h"
5 #include <Common/SortUtils.h>
5 #include <Common/SortUtils.h>
6
6
7 #include <QReadLocker>
7 #include <QReadLocker>
8 #include <QReadWriteLock>
8 #include <QReadWriteLock>
9 #include <QVector>
9 #include <QVector>
10
10
11 #include <memory>
11 #include <memory>
12
12
13 template <int Dim>
13 template <int Dim>
14 class ArrayData;
14 class ArrayData;
15
15
16 using DataContainer = std::vector<double>;
16 using DataContainer = std::vector<double>;
17
17
18 namespace arraydata_detail {
18 namespace arraydata_detail {
19
19
20 /// Struct used to sort ArrayData
20 /// Struct used to sort ArrayData
21 template <int Dim>
21 template <int Dim>
22 struct Sort {
22 struct Sort {
23 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data, int nbComponents,
23 static std::shared_ptr<ArrayData<Dim> > sort(const DataContainer &data, int nbComponents,
24 const std::vector<int> &sortPermutation)
24 const std::vector<int> &sortPermutation)
25 {
25 {
26 return std::make_shared<ArrayData<Dim> >(
26 return std::make_shared<ArrayData<Dim> >(
27 SortUtils::sort(data, nbComponents, sortPermutation), nbComponents);
27 SortUtils::sort(data, nbComponents, sortPermutation), nbComponents);
28 }
28 }
29 };
29 };
30
30
31 /// Specialization for uni-dimensional ArrayData
31 /// Specialization for uni-dimensional ArrayData
32 template <>
32 template <>
33 struct Sort<1> {
33 struct Sort<1> {
34 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data, int nbComponents,
34 static std::shared_ptr<ArrayData<1> > sort(const DataContainer &data, int nbComponents,
35 const std::vector<int> &sortPermutation)
35 const std::vector<int> &sortPermutation)
36 {
36 {
37 Q_UNUSED(nbComponents)
37 Q_UNUSED(nbComponents)
38 return std::make_shared<ArrayData<1> >(SortUtils::sort(data, 1, sortPermutation));
38 return std::make_shared<ArrayData<1> >(SortUtils::sort(data, 1, sortPermutation));
39 }
39 }
40 };
40 };
41
41
42 template <int Dim, bool IsConst>
42 template <int Dim, bool IsConst>
43 class IteratorValue;
43 class IteratorValue;
44
44
45 template <int Dim, bool IsConst>
45 template <int Dim, bool IsConst>
46 struct IteratorValueBuilder {
46 struct IteratorValueBuilder {
47 };
47 };
48
48
49 template <int Dim>
49 template <int Dim>
50 struct IteratorValueBuilder<Dim, true> {
50 struct IteratorValueBuilder<Dim, true> {
51 using DataContainerIterator = DataContainer::const_iterator;
51 using DataContainerIterator = DataContainer::const_iterator;
52
52
53 static void swap(IteratorValue<Dim, true> &o1, IteratorValue<Dim, true> &o2) {}
53 static void swap(IteratorValue<Dim, true> &o1, IteratorValue<Dim, true> &o2) {}
54 };
54 };
55
55
56 template <int Dim>
56 template <int Dim>
57 struct IteratorValueBuilder<Dim, false> {
57 struct IteratorValueBuilder<Dim, false> {
58 using DataContainerIterator = DataContainer::iterator;
58 using DataContainerIterator = DataContainer::iterator;
59
59
60 static void swap(IteratorValue<Dim, false> &o1, IteratorValue<Dim, false> &o2)
60 static void swap(IteratorValue<Dim, false> &o1, IteratorValue<Dim, false> &o2)
61 {
61 {
62 for (auto i = 0; i < o1.m_NbComponents; ++i) {
62 for (auto i = 0; i < o1.m_NbComponents; ++i) {
63 std::iter_swap(o1.m_It + i, o2.m_It + i);
63 std::iter_swap(o1.m_It + i, o2.m_It + i);
64 }
64 }
65 }
65 }
66 };
66 };
67
67
68 template <int Dim, bool IsConst>
68 template <int Dim, bool IsConst>
69 class IteratorValue : public ArrayDataIteratorValue::Impl {
69 class IteratorValue : public ArrayDataIteratorValue::Impl {
70 public:
70 public:
71 friend class ArrayData<Dim>;
71 friend class ArrayData<Dim>;
72 friend class IteratorValueBuilder<Dim, IsConst>;
72 friend class IteratorValueBuilder<Dim, IsConst>;
73
73
74 using DataContainerIterator =
74 using DataContainerIterator =
75 typename IteratorValueBuilder<Dim, IsConst>::DataContainerIterator;
75 typename IteratorValueBuilder<Dim, IsConst>::DataContainerIterator;
76
76
77 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
77 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
78 explicit IteratorValue(const DataContainer &container, int nbComponents, bool begin)
78 explicit IteratorValue(const DataContainer &container, int nbComponents, bool begin)
79 : m_It{begin ? container.cbegin() : container.cend()}, m_NbComponents{nbComponents}
79 : m_It{begin ? container.cbegin() : container.cend()}, m_NbComponents{nbComponents}
80 {
80 {
81 }
81 }
82
82
83 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
83 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
84 explicit IteratorValue(DataContainer &container, int nbComponents, bool begin)
84 explicit IteratorValue(DataContainer &container, int nbComponents, bool begin)
85 : m_It{begin ? container.begin() : container.end()}, m_NbComponents{nbComponents}
85 : m_It{begin ? container.begin() : container.end()}, m_NbComponents{nbComponents}
86 {
86 {
87 }
87 }
88
88
89 IteratorValue(const IteratorValue &other) = default;
89 IteratorValue(const IteratorValue &other) = default;
90
90
91 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
91 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
92 {
92 {
93 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
93 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
94 }
94 }
95
95
96 int distance(const ArrayDataIteratorValue::Impl &other) const override try {
96 int distance(const ArrayDataIteratorValue::Impl &other) const override try {
97 /// @todo ALX : validate
97 /// @todo ALX : validate
98 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
98 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
99 return std::distance(otherImpl.m_It, m_It) / m_NbComponents;
99 return std::distance(otherImpl.m_It, m_It) / m_NbComponents;
100 }
100 }
101 catch (const std::bad_cast &) {
101 catch (const std::bad_cast &) {
102 return 0;
102 return 0;
103 }
103 }
104
104
105 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
105 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
106 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
106 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
107 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
107 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
108 }
108 }
109 catch (const std::bad_cast &) {
109 catch (const std::bad_cast &) {
110 return false;
110 return false;
111 }
111 }
112
112
113 bool lowerThan(const ArrayDataIteratorValue::Impl &other) const override try {
113 bool lowerThan(const ArrayDataIteratorValue::Impl &other) const override try {
114 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
114 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
115 return m_It < otherImpl.m_It;
115 return m_It < otherImpl.m_It;
116 }
116 }
117 catch (const std::bad_cast &) {
117 catch (const std::bad_cast &) {
118 return false;
118 return false;
119 }
119 }
120
120
121 std::unique_ptr<ArrayDataIteratorValue::Impl> advance(int offset) const override
121 std::unique_ptr<ArrayDataIteratorValue::Impl> advance(int offset) const override
122 {
122 {
123 auto result = clone();
123 auto result = clone();
124 while (offset--) {
124 while (offset--) {
125 result->next();
125 result->next();
126 }
126 }
127 return result;
127 return result;
128 }
128 }
129
129
130 void next() override { std::advance(m_It, m_NbComponents); }
130 void next() override { std::advance(m_It, m_NbComponents); }
131 void prev() override { std::advance(m_It, -m_NbComponents); }
131 void prev() override { std::advance(m_It, -m_NbComponents); }
132
132
133 double at(int componentIndex) const override { return *(m_It + componentIndex); }
133 double at(int componentIndex) const override { return *(m_It + componentIndex); }
134 double first() const override { return *m_It; }
134 double first() const override { return *m_It; }
135 double min() const override
135 double min() const override
136 {
136 {
137 auto values = this->values();
137 auto values = this->values();
138 auto end = values.cend();
138 auto end = values.cend();
139 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
139 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
140 return SortUtils::minCompareWithNaN(v1, v2);
140 return SortUtils::minCompareWithNaN(v1, v2);
141 });
141 });
142
142
143 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
143 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
144 }
144 }
145 double max() const override
145 double max() const override
146 {
146 {
147 auto values = this->values();
147 auto values = this->values();
148 auto end = values.cend();
148 auto end = values.cend();
149 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
149 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
150 return SortUtils::maxCompareWithNaN(v1, v2);
150 return SortUtils::maxCompareWithNaN(v1, v2);
151 });
151 });
152 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
152 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
153 }
153 }
154
154
155 QVector<double> values() const override
155 QVector<double> values() const override
156 {
156 {
157 auto result = QVector<double>{};
157 auto result = QVector<double>{};
158 for (auto i = 0; i < m_NbComponents; ++i) {
158 for (auto i = 0; i < m_NbComponents; ++i) {
159 result.push_back(*(m_It + i));
159 result.push_back(*(m_It + i));
160 }
160 }
161
161
162 return result;
162 return result;
163 }
163 }
164
164
165 void swap(ArrayDataIteratorValue::Impl &other) override
165 void swap(ArrayDataIteratorValue::Impl &other) override
166 {
166 {
167 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
167 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
168 IteratorValueBuilder<Dim, IsConst>::swap(*this, otherImpl);
168 IteratorValueBuilder<Dim, IsConst>::swap(*this, otherImpl);
169 }
169 }
170
170
171 private:
171 private:
172 DataContainerIterator m_It;
172 DataContainerIterator m_It;
173 int m_NbComponents;
173 int m_NbComponents;
174 };
174 };
175
175
176 } // namespace arraydata_detail
176 } // namespace arraydata_detail
177
177
178 /**
178 /**
179 * @brief The ArrayData class represents a dataset for a data series.
179 * @brief The ArrayData class represents a dataset for a data series.
180 *
180 *
181 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
181 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
182 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
182 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
183 * number of values
183 * number of values
184 *
184 *
185 * @tparam Dim the dimension of the ArrayData (one or two)
185 * @tparam Dim the dimension of the ArrayData (one or two)
186 * @sa IDataSeries
186 * @sa IDataSeries
187 */
187 */
188 template <int Dim>
188 template <int Dim>
189 class ArrayData {
189 class ArrayData {
190 public:
190 public:
191 // ///// //
191 // ///// //
192 // Ctors //
192 // Ctors //
193 // ///// //
193 // ///// //
194
194
195 /**
195 /**
196 * Ctor for a unidimensional ArrayData
196 * Ctor for a unidimensional ArrayData
197 * @param data the data the ArrayData will hold
197 * @param data the data the ArrayData will hold
198 */
198 */
199 template <int D = Dim, typename = std::enable_if_t<D == 1> >
199 template <int D = Dim, typename = std::enable_if_t<D == 1> >
200 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
200 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
201 {
201 {
202 }
202 }
203
203
204 /**
204 /**
205 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
205 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
206 * greater than 2 and must be a divisor of the total number of data in the vector
206 * greater than 2 and must be a divisor of the total number of data in the vector
207 * @param data the data the ArrayData will hold
207 * @param data the data the ArrayData will hold
208 * @param nbComponents the number of components
208 * @param nbComponents the number of components
209 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
209 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
210 * of the size of the data
210 * of the size of the data
211 */
211 */
212 template <int D = Dim, typename = std::enable_if_t<D == 2> >
212 template <int D = Dim, typename = std::enable_if_t<D == 2> >
213 explicit ArrayData(DataContainer data, int nbComponents)
213 explicit ArrayData(DataContainer data, int nbComponents)
214 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
214 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
215 {
215 {
216 if (nbComponents < 2) {
216 if (nbComponents < 2) {
217 throw std::invalid_argument{
217 throw std::invalid_argument{
218 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
218 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
219 .arg(nbComponents)
219 .arg(nbComponents)
220 .toStdString()};
220 .toStdString()};
221 }
221 }
222
222
223 if (m_Data.size() % m_NbComponents != 0) {
223 if (m_Data.size() % m_NbComponents != 0) {
224 throw std::invalid_argument{QString{
224 throw std::invalid_argument{QString{
225 "The number of components (%1) is inconsistent with the total number of data (%2)"}
225 "The number of components (%1) is inconsistent with the total number of data (%2)"}
226 .arg(m_Data.size(), nbComponents)
226 .arg(m_Data.size(), nbComponents)
227 .toStdString()};
227 .toStdString()};
228 }
228 }
229 }
229 }
230
230
231 /// Copy ctor
231 /// Copy ctor
232 explicit ArrayData(const ArrayData &other)
232 explicit ArrayData(const ArrayData &other)
233 {
233 {
234 QReadLocker otherLocker{&other.m_Lock};
234 QReadLocker otherLocker{&other.m_Lock};
235 m_Data = other.m_Data;
235 m_Data = other.m_Data;
236 m_NbComponents = other.m_NbComponents;
236 m_NbComponents = other.m_NbComponents;
237 }
237 }
238
238
239 // /////////////// //
239 // /////////////// //
240 // General methods //
240 // General methods //
241 // /////////////// //
241 // /////////////// //
242
242
243 /**
243 /**
244 * Merges into the array data an other array data. The two array datas must have the same number
244 * Merges into the array data an other array data. The two array datas must have the same number
245 * of components so the merge can be done
245 * of components so the merge can be done
246 * @param other the array data to merge with
246 * @param other the array data to merge with
247 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
247 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
248 * inserted at the end
248 * inserted at the end
249 */
249 */
250 void add(const ArrayData<Dim> &other, bool prepend = false)
250 void add(const ArrayData<Dim> &other, bool prepend = false)
251 {
251 {
252 QWriteLocker locker{&m_Lock};
252 QWriteLocker locker{&m_Lock};
253 QReadLocker otherLocker{&other.m_Lock};
253 QReadLocker otherLocker{&other.m_Lock};
254
254
255 if (m_NbComponents != other.componentCount()) {
255 if (m_NbComponents != other.componentCount()) {
256 return;
256 return;
257 }
257 }
258
258
259 if (prepend) {
259 insert(other.cbegin(), other.cend(), prepend);
260 auto otherDataSize = other.m_Data.size();
261 m_Data.insert(m_Data.begin(), otherDataSize, 0.);
262 for (auto i = 0; i < otherDataSize; ++i) {
263 m_Data.replace(i, other.m_Data.at(i));
264 }
265 }
266 else {
267 m_Data.append(other.m_Data);
268 }
269 }
260 }
270
261
271 void clear()
262 void clear()
272 {
263 {
273 QWriteLocker locker{&m_Lock};
264 QWriteLocker locker{&m_Lock};
274 m_Data.clear();
265 m_Data.clear();
275 }
266 }
276
267
277 int componentCount() const noexcept { return m_NbComponents; }
268 int componentCount() const noexcept { return m_NbComponents; }
278
269
279 /// @return the size (i.e. number of values) of a single component
270 /// @return the size (i.e. number of values) of a single component
280 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
271 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
281 int size() const
272 int size() const
282 {
273 {
283 QReadLocker locker{&m_Lock};
274 QReadLocker locker{&m_Lock};
284 return m_Data.size() / m_NbComponents;
275 return m_Data.size() / m_NbComponents;
285 }
276 }
286
277
287 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
278 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
288 {
279 {
289 QReadLocker locker{&m_Lock};
280 QReadLocker locker{&m_Lock};
290 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
281 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
291 }
282 }
292
283
293 // ///////// //
284 // ///////// //
294 // Iterators //
285 // Iterators //
295 // ///////// //
286 // ///////// //
296
287
297 ArrayDataIterator begin()
288 ArrayDataIterator begin()
298 {
289 {
299 return ArrayDataIterator{
290 return ArrayDataIterator{
300 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
291 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
301 m_Data, m_NbComponents, true)}};
292 m_Data, m_NbComponents, true)}};
302 }
293 }
303
294
304 ArrayDataIterator end()
295 ArrayDataIterator end()
305 {
296 {
306 return ArrayDataIterator{
297 return ArrayDataIterator{
307 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
298 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
308 m_Data, m_NbComponents, false)}};
299 m_Data, m_NbComponents, false)}};
309 }
300 }
310
301
311 ArrayDataIterator cbegin() const
302 ArrayDataIterator cbegin() const
312 {
303 {
313 return ArrayDataIterator{
304 return ArrayDataIterator{
314 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
305 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
315 m_Data, m_NbComponents, true)}};
306 m_Data, m_NbComponents, true)}};
316 }
307 }
317
308
318 ArrayDataIterator cend() const
309 ArrayDataIterator cend() const
319 {
310 {
320 return ArrayDataIterator{
311 return ArrayDataIterator{
321 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
312 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
322 m_Data, m_NbComponents, false)}};
313 m_Data, m_NbComponents, false)}};
323 }
314 }
324
315
325 void erase(ArrayDataIterator first, ArrayDataIterator last)
316 void erase(ArrayDataIterator first, ArrayDataIterator last)
326 {
317 {
327 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(first->impl());
318 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(first->impl());
328 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(last->impl());
319 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(last->impl());
329
320
330 if (firstImpl && lastImpl) {
321 if (firstImpl && lastImpl) {
331 m_Data.erase(firstImpl->m_It, lastImpl->m_It);
322 m_Data.erase(firstImpl->m_It, lastImpl->m_It);
332 }
323 }
333 }
324 }
334
325
335 /// Inserts at the end of the array data the values passed as a parameter. This
326 void insert(ArrayDataIterator first, ArrayDataIterator last, bool prepend = false)
336 /// method is intended to be used in the context of generating a back insert iterator, or only
337 /// if it's ensured that the total size of the vector is consistent with the number of
338 /// components of the array data
339 /// @param values the values to insert
340 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
341 void push_back(const QVector<double> &values)
342 {
327 {
343 Q_ASSERT(values.size() % m_NbComponents == 0);
328 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(first->impl());
344 m_Data.append(values);
329 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, true> *>(last->impl());
330
331 if (firstImpl && lastImpl) {
332 auto insertIt = prepend ? m_Data.begin() : m_Data.end();
333
334 m_Data.insert(insertIt, firstImpl->m_It, lastImpl->m_It);
335 }
345 }
336 }
346
337
347 /**
338 /**
348 * @return the data at a specified index
339 * @return the data at a specified index
349 * @remarks index must be a valid position
340 * @remarks index must be a valid position
350 */
341 */
351 double at(int index) const noexcept
342 double at(int index) const noexcept
352 {
343 {
353 QReadLocker locker{&m_Lock};
344 QReadLocker locker{&m_Lock};
354 return m_Data.at(index);
345 return m_Data.at(index);
355 }
346 }
356
347
357 // ///////////// //
348 // ///////////// //
358 // 1-dim methods //
349 // 1-dim methods //
359 // ///////////// //
350 // ///////////// //
360
351
361 /**
352 /**
362 * @return the data as a vector, as a const reference
353 * @return the data as a vector, as a const reference
363 * @remarks this method is only available for a unidimensional ArrayData
354 * @remarks this method is only available for a unidimensional ArrayData
364 */
355 */
365 template <int D = Dim, typename = std::enable_if_t<D == 1> >
356 template <int D = Dim, typename = std::enable_if_t<D == 1> >
366 DataContainer cdata() const noexcept
357 DataContainer cdata() const noexcept
367 {
358 {
368 return m_Data;
359 return m_Data;
369 }
360 }
370
361
371 private:
362 private:
372 DataContainer m_Data;
363 DataContainer m_Data;
373 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
364 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
374 int m_NbComponents;
365 int m_NbComponents;
375 mutable QReadWriteLock m_Lock;
366 mutable QReadWriteLock m_Lock;
376 };
367 };
377
368
378 #endif // SCIQLOP_ARRAYDATA_H
369 #endif // SCIQLOP_ARRAYDATA_H
@@ -1,410 +1,401
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
11
12 #include <QLoggingCategory>
12 #include <QLoggingCategory>
13 #include <QReadLocker>
13 #include <QReadLocker>
14 #include <QReadWriteLock>
14 #include <QReadWriteLock>
15 #include <memory>
15 #include <memory>
16
16
17 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
17 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 // definitions with inheritance. Inline method is used instead
18 // definitions with inheritance. Inline method is used instead
19 inline const QLoggingCategory &LOG_DataSeries()
19 inline const QLoggingCategory &LOG_DataSeries()
20 {
20 {
21 static const QLoggingCategory category{"DataSeries"};
21 static const QLoggingCategory category{"DataSeries"};
22 return category;
22 return category;
23 }
23 }
24
24
25 template <int Dim>
25 template <int Dim>
26 class DataSeries;
26 class DataSeries;
27
27
28 namespace dataseries_detail {
28 namespace dataseries_detail {
29
29
30 template <int Dim, bool IsConst>
30 template <int Dim, bool IsConst>
31 class IteratorValue : public DataSeriesIteratorValue::Impl {
31 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 public:
32 public:
33 friend class DataSeries<Dim>;
33 friend class DataSeries<Dim>;
34
34
35 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
35 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
36 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
37 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
38 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 {
39 {
40 }
40 }
41
41
42 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
42 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
43 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
43 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
44 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
44 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
45 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
45 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
46 : dataSeries.valuesData()->cend())
46 : dataSeries.valuesData()->cend())
47 {
47 {
48 }
48 }
49
49
50 IteratorValue(const IteratorValue &other) = default;
50 IteratorValue(const IteratorValue &other) = default;
51
51
52 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
52 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
53 {
53 {
54 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
54 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
55 }
55 }
56
56
57 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
57 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 return m_XIt->distance(*otherImpl.m_XIt);
59 return m_XIt->distance(*otherImpl.m_XIt);
60 }
60 }
61 catch (const std::bad_cast &) {
61 catch (const std::bad_cast &) {
62 return 0;
62 return 0;
63 }
63 }
64
64
65 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
65 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
66 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
66 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
67 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
67 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
68 }
68 }
69 catch (const std::bad_cast &) {
69 catch (const std::bad_cast &) {
70 return false;
70 return false;
71 }
71 }
72
72
73 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
73 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
74 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
74 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
75 return m_XIt->lowerThan(*otherImpl.m_XIt);
75 return m_XIt->lowerThan(*otherImpl.m_XIt);
76 }
76 }
77 catch (const std::bad_cast &) {
77 catch (const std::bad_cast &) {
78 return false;
78 return false;
79 }
79 }
80
80
81 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
81 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
82 {
82 {
83 auto result = clone();
83 auto result = clone();
84 while (offset--) {
84 while (offset--) {
85 result->next();
85 result->next();
86 }
86 }
87 return result;
87 return result;
88 }
88 }
89
89
90 void next() override
90 void next() override
91 {
91 {
92 ++m_XIt;
92 ++m_XIt;
93 ++m_ValuesIt;
93 ++m_ValuesIt;
94 }
94 }
95
95
96 void prev() override
96 void prev() override
97 {
97 {
98 --m_XIt;
98 --m_XIt;
99 --m_ValuesIt;
99 --m_ValuesIt;
100 }
100 }
101
101
102 double x() const override { return m_XIt->at(0); }
102 double x() const override { return m_XIt->at(0); }
103 double value() const override { return m_ValuesIt->at(0); }
103 double value() const override { return m_ValuesIt->at(0); }
104 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
104 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
105 double minValue() const override { return m_ValuesIt->min(); }
105 double minValue() const override { return m_ValuesIt->min(); }
106 double maxValue() const override { return m_ValuesIt->max(); }
106 double maxValue() const override { return m_ValuesIt->max(); }
107 QVector<double> values() const override { return m_ValuesIt->values(); }
107 QVector<double> values() const override { return m_ValuesIt->values(); }
108
108
109 void swap(DataSeriesIteratorValue::Impl &other) override
109 void swap(DataSeriesIteratorValue::Impl &other) override
110 {
110 {
111 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
111 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
112 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
112 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
113 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
113 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
114 }
114 }
115
115
116 private:
116 private:
117 ArrayDataIterator m_XIt;
117 ArrayDataIterator m_XIt;
118 ArrayDataIterator m_ValuesIt;
118 ArrayDataIterator m_ValuesIt;
119 };
119 };
120 } // namespace dataseries_detail
120 } // namespace dataseries_detail
121
121
122 /**
122 /**
123 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
123 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
124 *
124 *
125 * It proposes to set a dimension for the values ​​data.
125 * It proposes to set a dimension for the values ​​data.
126 *
126 *
127 * A DataSeries is always sorted on its x-axis data.
127 * A DataSeries is always sorted on its x-axis data.
128 *
128 *
129 * @tparam Dim The dimension of the values data
129 * @tparam Dim The dimension of the values data
130 *
130 *
131 */
131 */
132 template <int Dim>
132 template <int Dim>
133 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
133 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
134 friend class DataSeriesMergeHelper;
134 friend class DataSeriesMergeHelper;
135
135
136 public:
136 public:
137 /// Tag needed to define the push_back() method
138 /// @sa push_back()
139 using value_type = DataSeriesIteratorValue;
140
141 /// @sa IDataSeries::xAxisData()
137 /// @sa IDataSeries::xAxisData()
142 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
138 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
143 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
139 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
144
140
145 /// @sa IDataSeries::xAxisUnit()
141 /// @sa IDataSeries::xAxisUnit()
146 Unit xAxisUnit() const override { return m_XAxisUnit; }
142 Unit xAxisUnit() const override { return m_XAxisUnit; }
147
143
148 /// @return the values dataset
144 /// @return the values dataset
149 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
145 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
150 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
146 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
151
147
152 /// @sa IDataSeries::valuesUnit()
148 /// @sa IDataSeries::valuesUnit()
153 Unit valuesUnit() const override { return m_ValuesUnit; }
149 Unit valuesUnit() const override { return m_ValuesUnit; }
154
150
155
151
156 SqpRange range() const override
152 SqpRange range() const override
157 {
153 {
158 if (!m_XAxisData->cdata().empty()) {
154 if (!m_XAxisData->cdata().empty()) {
159 return SqpRange{m_XAxisData->cdata().front(), m_XAxisData->cdata().back()};
155 return SqpRange{m_XAxisData->cdata().front(), m_XAxisData->cdata().back()};
160 }
156 }
161
157
162 return SqpRange{};
158 return SqpRange{};
163 }
159 }
164
160
165 void clear()
161 void clear()
166 {
162 {
167 m_XAxisData->clear();
163 m_XAxisData->clear();
168 m_ValuesData->clear();
164 m_ValuesData->clear();
169 }
165 }
170
166
171 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
167 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
172
168
173 /// Merges into the data series an other data series
169 /// Merges into the data series an other data series
174 /// @remarks the data series to merge with is cleared after the operation
170 /// @remarks the data series to merge with is cleared after the operation
175 void merge(IDataSeries *dataSeries) override
171 void merge(IDataSeries *dataSeries) override
176 {
172 {
177 dataSeries->lockWrite();
173 dataSeries->lockWrite();
178 lockWrite();
174 lockWrite();
179
175
180 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
176 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
181 DataSeriesMergeHelper::merge(*other, *this);
177 DataSeriesMergeHelper::merge(*other, *this);
182 }
178 }
183 else {
179 else {
184 qCWarning(LOG_DataSeries())
180 qCWarning(LOG_DataSeries())
185 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
181 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
186 }
182 }
187 unlock();
183 unlock();
188 dataSeries->unlock();
184 dataSeries->unlock();
189 }
185 }
190
186
191 void purge(double min, double max) override
187 void purge(double min, double max) override
192 {
188 {
193 // Nothing to purge if series is empty
189 // Nothing to purge if series is empty
194 if (isEmpty()) {
190 if (isEmpty()) {
195 return;
191 return;
196 }
192 }
197
193
198 if (min > max) {
194 if (min > max) {
199 std::swap(min, max);
195 std::swap(min, max);
200 }
196 }
201
197
202 // Nothing to purge if series min/max are inside purge range
198 // Nothing to purge if series min/max are inside purge range
203 auto xMin = cbegin()->x();
199 auto xMin = cbegin()->x();
204 auto xMax = (--cend())->x();
200 auto xMax = (--cend())->x();
205 if (xMin >= min && xMax <= max) {
201 if (xMin >= min && xMax <= max) {
206 return;
202 return;
207 }
203 }
208
204
209 auto lowerIt = std::lower_bound(
205 auto lowerIt = std::lower_bound(
210 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
206 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
211 erase(begin(), lowerIt);
207 erase(begin(), lowerIt);
212 auto upperIt = std::upper_bound(
208 auto upperIt = std::upper_bound(
213 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
209 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
214 erase(upperIt, end());
210 erase(upperIt, end());
215 }
211 }
216
212
217 // ///////// //
213 // ///////// //
218 // Iterators //
214 // Iterators //
219 // ///////// //
215 // ///////// //
220
216
221 DataSeriesIterator begin() override
217 DataSeriesIterator begin() override
222 {
218 {
223 return DataSeriesIterator{DataSeriesIteratorValue{
219 return DataSeriesIterator{DataSeriesIteratorValue{
224 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
220 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
225 }
221 }
226
222
227 DataSeriesIterator end() override
223 DataSeriesIterator end() override
228 {
224 {
229 return DataSeriesIterator{DataSeriesIteratorValue{
225 return DataSeriesIterator{DataSeriesIteratorValue{
230 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
226 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
231 }
227 }
232
228
233 DataSeriesIterator cbegin() const override
229 DataSeriesIterator cbegin() const override
234 {
230 {
235 return DataSeriesIterator{DataSeriesIteratorValue{
231 return DataSeriesIterator{DataSeriesIteratorValue{
236 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
232 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
237 }
233 }
238
234
239 DataSeriesIterator cend() const override
235 DataSeriesIterator cend() const override
240 {
236 {
241 return DataSeriesIterator{DataSeriesIteratorValue{
237 return DataSeriesIterator{DataSeriesIteratorValue{
242 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
238 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
243 }
239 }
244
240
245 void erase(DataSeriesIterator first, DataSeriesIterator last)
241 void erase(DataSeriesIterator first, DataSeriesIterator last)
246 {
242 {
247 auto firstImpl
243 auto firstImpl
248 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
244 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
249 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
245 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
250
246
251 if (firstImpl && lastImpl) {
247 if (firstImpl && lastImpl) {
252 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
248 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
253 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
249 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
254 }
250 }
255 }
251 }
256
252
253 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
254 {
255 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
256 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
257
258 if (firstImpl && lastImpl) {
259 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
260 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
261 }
262 }
263
257 /// @sa IDataSeries::minXAxisData()
264 /// @sa IDataSeries::minXAxisData()
258 DataSeriesIterator minXAxisData(double minXAxisData) const override
265 DataSeriesIterator minXAxisData(double minXAxisData) const override
259 {
266 {
260 return std::lower_bound(
267 return std::lower_bound(
261 cbegin(), cend(), minXAxisData,
268 cbegin(), cend(), minXAxisData,
262 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
269 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
263 }
270 }
264
271
265 /// @sa IDataSeries::maxXAxisData()
272 /// @sa IDataSeries::maxXAxisData()
266 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
273 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
267 {
274 {
268 // Gets the first element that greater than max value
275 // Gets the first element that greater than max value
269 auto it = std::upper_bound(
276 auto it = std::upper_bound(
270 cbegin(), cend(), maxXAxisData,
277 cbegin(), cend(), maxXAxisData,
271 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
278 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
272
279
273 return it == cbegin() ? cend() : --it;
280 return it == cbegin() ? cend() : --it;
274 }
281 }
275
282
276 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
283 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
277 double maxXAxisData) const override
284 double maxXAxisData) const override
278 {
285 {
279 if (minXAxisData > maxXAxisData) {
286 if (minXAxisData > maxXAxisData) {
280 std::swap(minXAxisData, maxXAxisData);
287 std::swap(minXAxisData, maxXAxisData);
281 }
288 }
282
289
283 auto begin = cbegin();
290 auto begin = cbegin();
284 auto end = cend();
291 auto end = cend();
285
292
286 auto lowerIt = std::lower_bound(
293 auto lowerIt = std::lower_bound(
287 begin, end, minXAxisData,
294 begin, end, minXAxisData,
288 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
295 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
289 auto upperIt = std::upper_bound(
296 auto upperIt = std::upper_bound(
290 begin, end, maxXAxisData,
297 lowerIt, end, maxXAxisData,
291 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
298 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
292
299
293 return std::make_pair(lowerIt, upperIt);
300 return std::make_pair(lowerIt, upperIt);
294 }
301 }
295
302
296 std::pair<DataSeriesIterator, DataSeriesIterator>
303 std::pair<DataSeriesIterator, DataSeriesIterator>
297 valuesBounds(double minXAxisData, double maxXAxisData) const override
304 valuesBounds(double minXAxisData, double maxXAxisData) const override
298 {
305 {
299 // Places iterators to the correct x-axis range
306 // Places iterators to the correct x-axis range
300 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
307 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
301
308
302 // Returns end iterators if the range is empty
309 // Returns end iterators if the range is empty
303 if (xAxisRangeIts.first == xAxisRangeIts.second) {
310 if (xAxisRangeIts.first == xAxisRangeIts.second) {
304 return std::make_pair(cend(), cend());
311 return std::make_pair(cend(), cend());
305 }
312 }
306
313
307 // Gets the iterator on the min of all values data
314 // Gets the iterator on the min of all values data
308 auto minIt = std::min_element(
315 auto minIt = std::min_element(
309 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
316 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
310 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
317 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
311 });
318 });
312
319
313 // Gets the iterator on the max of all values data
320 // Gets the iterator on the max of all values data
314 auto maxIt = std::max_element(
321 auto maxIt = std::max_element(
315 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
322 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
316 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
323 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
317 });
324 });
318
325
319 return std::make_pair(minIt, maxIt);
326 return std::make_pair(minIt, maxIt);
320 }
327 }
321
328
322 // /////// //
329 // /////// //
323 // Mutexes //
330 // Mutexes //
324 // /////// //
331 // /////// //
325
332
326 virtual void lockRead() { m_Lock.lockForRead(); }
333 virtual void lockRead() { m_Lock.lockForRead(); }
327 virtual void lockWrite() { m_Lock.lockForWrite(); }
334 virtual void lockWrite() { m_Lock.lockForWrite(); }
328 virtual void unlock() { m_Lock.unlock(); }
335 virtual void unlock() { m_Lock.unlock(); }
329
336
330 // ///// //
331 // Other //
332 // ///// //
333
334 /// Inserts at the end of the data series the value of the iterator passed as a parameter. This
335 /// method is intended to be used in the context of generating a back insert iterator
336 /// @param iteratorValue the iterator value containing the values to insert
337 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
338 /// @sa merge()
339 /// @sa value_type
340 void push_back(const value_type &iteratorValue)
341 {
342 m_XAxisData->push_back(QVector<double>{iteratorValue.x()});
343 m_ValuesData->push_back(iteratorValue.values());
344 }
345
346 protected:
337 protected:
347 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
338 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
348 /// DataSeries with no values will be created.
339 /// DataSeries with no values will be created.
349 /// @remarks data series is automatically sorted on its x-axis data
340 /// @remarks data series is automatically sorted on its x-axis data
350 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
341 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
351 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
342 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
352 : m_XAxisData{xAxisData},
343 : m_XAxisData{xAxisData},
353 m_XAxisUnit{xAxisUnit},
344 m_XAxisUnit{xAxisUnit},
354 m_ValuesData{valuesData},
345 m_ValuesData{valuesData},
355 m_ValuesUnit{valuesUnit}
346 m_ValuesUnit{valuesUnit}
356 {
347 {
357 if (m_XAxisData->size() != m_ValuesData->size()) {
348 if (m_XAxisData->size() != m_ValuesData->size()) {
358 clear();
349 clear();
359 }
350 }
360
351
361 // Sorts data if it's not the case
352 // Sorts data if it's not the case
362 const auto &xAxisCData = m_XAxisData->cdata();
353 const auto &xAxisCData = m_XAxisData->cdata();
363 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
354 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
364 sort();
355 sort();
365 }
356 }
366 }
357 }
367
358
368 /// Copy ctor
359 /// Copy ctor
369 explicit DataSeries(const DataSeries<Dim> &other)
360 explicit DataSeries(const DataSeries<Dim> &other)
370 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
361 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
371 m_XAxisUnit{other.m_XAxisUnit},
362 m_XAxisUnit{other.m_XAxisUnit},
372 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
363 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
373 m_ValuesUnit{other.m_ValuesUnit}
364 m_ValuesUnit{other.m_ValuesUnit}
374 {
365 {
375 // Since a series is ordered from its construction and is always ordered, it is not
366 // Since a series is ordered from its construction and is always ordered, it is not
376 // necessary to call the sort method here ('other' is sorted)
367 // necessary to call the sort method here ('other' is sorted)
377 }
368 }
378
369
379 /// Assignment operator
370 /// Assignment operator
380 template <int D>
371 template <int D>
381 DataSeries &operator=(DataSeries<D> other)
372 DataSeries &operator=(DataSeries<D> other)
382 {
373 {
383 std::swap(m_XAxisData, other.m_XAxisData);
374 std::swap(m_XAxisData, other.m_XAxisData);
384 std::swap(m_XAxisUnit, other.m_XAxisUnit);
375 std::swap(m_XAxisUnit, other.m_XAxisUnit);
385 std::swap(m_ValuesData, other.m_ValuesData);
376 std::swap(m_ValuesData, other.m_ValuesData);
386 std::swap(m_ValuesUnit, other.m_ValuesUnit);
377 std::swap(m_ValuesUnit, other.m_ValuesUnit);
387
378
388 return *this;
379 return *this;
389 }
380 }
390
381
391 private:
382 private:
392 /**
383 /**
393 * Sorts data series on its x-axis data
384 * Sorts data series on its x-axis data
394 */
385 */
395 void sort() noexcept
386 void sort() noexcept
396 {
387 {
397 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
388 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
398 m_XAxisData = m_XAxisData->sort(permutation);
389 m_XAxisData = m_XAxisData->sort(permutation);
399 m_ValuesData = m_ValuesData->sort(permutation);
390 m_ValuesData = m_ValuesData->sort(permutation);
400 }
391 }
401
392
402 std::shared_ptr<ArrayData<1> > m_XAxisData;
393 std::shared_ptr<ArrayData<1> > m_XAxisData;
403 Unit m_XAxisUnit;
394 Unit m_XAxisUnit;
404 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
395 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
405 Unit m_ValuesUnit;
396 Unit m_ValuesUnit;
406
397
407 QReadWriteLock m_Lock;
398 QReadWriteLock m_Lock;
408 };
399 };
409
400
410 #endif // SCIQLOP_DATASERIES_H
401 #endif // SCIQLOP_DATASERIES_H
@@ -1,132 +1,83
1 #ifndef SCIQLOP_DATASERIESMERGEHELPER_H
1 #ifndef SCIQLOP_DATASERIESMERGEHELPER_H
2 #define SCIQLOP_DATASERIESMERGEHELPER_H
2 #define SCIQLOP_DATASERIESMERGEHELPER_H
3
3
4 template <int Dim>
4 template <int Dim>
5 class DataSeries;
5 class DataSeries;
6
6
7 namespace detail {
7 namespace detail {
8
8
9 /**
9 /**
10 * Scope that can be used for a merge operation
10 * Scope that can be used for a merge operation
11 * @tparam FEnd the type of function that will be executed at the end of the scope
11 * @tparam FEnd the type of function that will be executed at the end of the scope
12 */
12 */
13 template <typename FEnd>
13 template <typename FEnd>
14 struct MergeScope {
14 struct MergeScope {
15 explicit MergeScope(FEnd end) : m_End{end} {}
15 explicit MergeScope(FEnd end) : m_End{end} {}
16 virtual ~MergeScope() noexcept { m_End(); }
16 virtual ~MergeScope() noexcept { m_End(); }
17 FEnd m_End;
17 FEnd m_End;
18 };
18 };
19
19
20 /**
20 /**
21 * Creates a scope for merge operation
21 * Creates a scope for merge operation
22 * @tparam end the function executed at the end of the scope
22 * @tparam end the function executed at the end of the scope
23 */
23 */
24 template <typename FEnd>
24 template <typename FEnd>
25 MergeScope<FEnd> scope(FEnd end)
25 MergeScope<FEnd> scope(FEnd end)
26 {
26 {
27 return MergeScope<FEnd>{end};
27 return MergeScope<FEnd>{end};
28 }
28 }
29
29
30 /**
31 * Enum used to position a data series relative to another during a merge operation
32 */
33 enum class MergePosition { LOWER_THAN, GREATER_THAN, EQUAL, OVERLAP };
34
35 /**
36 * Computes the position of the first data series relative to the second data series
37 * @param lhs the first data series
38 * @param rhs the second data series
39 * @return the merge position computed
40 * @remarks the data series must not be empty
41 */
42 template <int Dim>
43 MergePosition mergePosition(DataSeries<Dim> &lhs, DataSeries<Dim> &rhs)
44 {
45 Q_ASSERT(!lhs.isEmpty() && !rhs.isEmpty());
46
47 // Case lhs < rhs
48 auto lhsLast = --lhs.cend();
49 auto rhsFirst = rhs.cbegin();
50 if (lhsLast->x() < rhsFirst->x()) {
51 return MergePosition::LOWER_THAN;
52 }
53
54 // Case lhs > rhs
55 auto lhsFirst = lhs.cbegin();
56 auto rhsLast = --rhs.cend();
57 if (lhsFirst->x() > rhsLast->x()) {
58 return MergePosition::GREATER_THAN;
59 }
60
61 // Other cases
62 auto equal = std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(),
63 [](const auto &it1, const auto &it2) {
64 return it1.x() == it2.x() && it1.values() == it2.values();
65 });
66 return equal ? MergePosition::EQUAL : MergePosition::OVERLAP;
67 }
68
69 } // namespace detail
30 } // namespace detail
70
31
71
32
72 /// Helper used to merge two DataSeries
33 /// Helper used to merge two DataSeries
73 /// @sa DataSeries
34 /// @sa DataSeries
74 struct DataSeriesMergeHelper {
35 struct DataSeriesMergeHelper {
75 /// Merges the source data series into the dest data series. Data of the source data series are
36 /// Merges the source data series into the dest data series. Data of the source data series are
76 /// consumed
37 /// consumed
77 template <int Dim>
38 template <int Dim>
78 static void merge(DataSeries<Dim> &source, DataSeries<Dim> &dest)
39 static void merge(DataSeries<Dim> &source, DataSeries<Dim> &dest)
79 {
40 {
80 // Creates a scope to clear source data series at the end of the merge
41 // Creates a scope to clear source data series at the end of the merge
81 auto _ = detail::scope([&source]() { source.clear(); });
42 auto _ = detail::scope([&source]() { source.clear(); });
82
43
83 // Case : source data series is empty -> no merge is made
44 // Case : source data series is empty -> no merge is made
84 if (source.isEmpty()) {
45 if (source.isEmpty()) {
85 return;
46 return;
86 }
47 }
87
48
88 // Case : dest data series is empty -> we simply swap the data
49 // Case : dest data series is empty -> we simply swap the data
89 if (dest.isEmpty()) {
50 if (dest.isEmpty()) {
90 std::swap(dest.m_XAxisData, source.m_XAxisData);
51 std::swap(dest.m_XAxisData, source.m_XAxisData);
91 std::swap(dest.m_ValuesData, source.m_ValuesData);
52 std::swap(dest.m_ValuesData, source.m_ValuesData);
92 return;
53 return;
93 }
54 }
94
55
95 // Gets the position of the source in relation to the destination
56 auto destMin = dest.cbegin()->x();
96 auto sourcePosition = detail::mergePosition(source, dest);
57 auto destMax = (--dest.cend())->x();
58
59 auto sourceBegin = source.cbegin();
60 auto sourceEnd = source.cend();
61 auto sourceMin = sourceBegin->x();
62 auto sourceMax = (--source.cend())->x();
97
63
98 switch (sourcePosition) {
64 // Case : source bounds are inside dest bounds -> no merge is made
99 case detail::MergePosition::LOWER_THAN:
65 if (sourceMin >= destMin && sourceMax <= destMax) {
100 case detail::MergePosition::GREATER_THAN: {
66 return;
101 auto prepend = sourcePosition == detail::MergePosition::LOWER_THAN;
102 dest.m_XAxisData->add(*source.m_XAxisData, prepend);
103 dest.m_ValuesData->add(*source.m_ValuesData, prepend);
104 break;
105 }
106 case detail::MergePosition::EQUAL:
107 // the data series equal each other : no merge made
108 break;
109 case detail::MergePosition::OVERLAP: {
110 // the two data series overlap : merge is made
111 auto temp = dest.clone();
112 if (auto tempSeries = dynamic_cast<DataSeries<Dim> *>(temp.get())) {
113 // Makes the merge :
114 // - Data are sorted by x-axis values
115 // - If two entries are in the source range and the other range, only one entry
116 // is retained as result
117 // - The results are stored directly in the data series
118 dest.clear();
119 std::set_union(
120 tempSeries->cbegin(), tempSeries->cend(), source.cbegin(), source.cend(),
121 std::back_inserter(dest),
122 [](const auto &it1, const auto &it2) { return it1.x() < it2.x(); });
123 }
124 break;
125 }
126 default:
127 Q_ASSERT(false);
128 }
67 }
68
69 // Default case :
70 // - prepend to dest the values of source that are lower than min value of dest
71 // - append to dest the values of source that are greater than max value of dest
72 auto lowerIt
73 = std::lower_bound(sourceBegin, sourceEnd, destMin,
74 [](const auto &it, const auto &val) { return it.x() < val; });
75 auto upperIt
76 = std::upper_bound(lowerIt, sourceEnd, destMax,
77 [](const auto &val, const auto &it) { return val < it.x(); });
78 dest.insert(sourceBegin, lowerIt, true);
79 dest.insert(upperIt, sourceEnd);
129 }
80 }
130 };
81 };
131
82
132 #endif // SCIQLOP_DATASERIESMERGEHELPER_H
83 #endif // SCIQLOP_DATASERIESMERGEHELPER_H
General Comments 0
You need to be logged in to leave comments. Login now