##// END OF EJS Templates
Adds erase() method for ArrayData and DataSeries
Alexandre Leroux -
r675:2be6c7467d6b
parent child
Show More
@@ -1,343 +1,353
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 = QVector<double>;
16 using DataContainer = QVector<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 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
96 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
97 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
97 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
98 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
98 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
99 }
99 }
100 catch (const std::bad_cast &) {
100 catch (const std::bad_cast &) {
101 return false;
101 return false;
102 }
102 }
103
103
104 void next() override { std::advance(m_It, m_NbComponents); }
104 void next() override { std::advance(m_It, m_NbComponents); }
105 void prev() override { std::advance(m_It, -m_NbComponents); }
105 void prev() override { std::advance(m_It, -m_NbComponents); }
106
106
107 double at(int componentIndex) const override { return *(m_It + componentIndex); }
107 double at(int componentIndex) const override { return *(m_It + componentIndex); }
108 double first() const override { return *m_It; }
108 double first() const override { return *m_It; }
109 double min() const override
109 double min() const override
110 {
110 {
111 auto values = this->values();
111 auto values = this->values();
112 auto end = values.cend();
112 auto end = values.cend();
113 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
113 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
114 return SortUtils::minCompareWithNaN(v1, v2);
114 return SortUtils::minCompareWithNaN(v1, v2);
115 });
115 });
116
116
117 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
117 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
118 }
118 }
119 double max() const override
119 double max() const override
120 {
120 {
121 auto values = this->values();
121 auto values = this->values();
122 auto end = values.cend();
122 auto end = values.cend();
123 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
123 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
124 return SortUtils::maxCompareWithNaN(v1, v2);
124 return SortUtils::maxCompareWithNaN(v1, v2);
125 });
125 });
126 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
126 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
127 }
127 }
128
128
129 QVector<double> values() const override
129 QVector<double> values() const override
130 {
130 {
131 auto result = QVector<double>{};
131 auto result = QVector<double>{};
132 for (auto i = 0; i < m_NbComponents; ++i) {
132 for (auto i = 0; i < m_NbComponents; ++i) {
133 result.push_back(*(m_It + i));
133 result.push_back(*(m_It + i));
134 }
134 }
135
135
136 return result;
136 return result;
137 }
137 }
138
138
139 void swap(ArrayDataIteratorValue::Impl &other) override
139 void swap(ArrayDataIteratorValue::Impl &other) override
140 {
140 {
141 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
141 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
142 IteratorValueBuilder<Dim, IsConst>::swap(*this, otherImpl);
142 IteratorValueBuilder<Dim, IsConst>::swap(*this, otherImpl);
143 }
143 }
144
144
145 private:
145 private:
146 DataContainerIterator m_It;
146 DataContainerIterator m_It;
147 int m_NbComponents;
147 int m_NbComponents;
148 };
148 };
149
149
150 } // namespace arraydata_detail
150 } // namespace arraydata_detail
151
151
152 /**
152 /**
153 * @brief The ArrayData class represents a dataset for a data series.
153 * @brief The ArrayData class represents a dataset for a data series.
154 *
154 *
155 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
155 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
156 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
156 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
157 * number of values
157 * number of values
158 *
158 *
159 * @tparam Dim the dimension of the ArrayData (one or two)
159 * @tparam Dim the dimension of the ArrayData (one or two)
160 * @sa IDataSeries
160 * @sa IDataSeries
161 */
161 */
162 template <int Dim>
162 template <int Dim>
163 class ArrayData {
163 class ArrayData {
164 public:
164 public:
165 // ///// //
165 // ///// //
166 // Ctors //
166 // Ctors //
167 // ///// //
167 // ///// //
168
168
169 /**
169 /**
170 * Ctor for a unidimensional ArrayData
170 * Ctor for a unidimensional ArrayData
171 * @param data the data the ArrayData will hold
171 * @param data the data the ArrayData will hold
172 */
172 */
173 template <int D = Dim, typename = std::enable_if_t<D == 1> >
173 template <int D = Dim, typename = std::enable_if_t<D == 1> >
174 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
174 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
175 {
175 {
176 }
176 }
177
177
178 /**
178 /**
179 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
179 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
180 * greater than 2 and must be a divisor of the total number of data in the vector
180 * greater than 2 and must be a divisor of the total number of data in the vector
181 * @param data the data the ArrayData will hold
181 * @param data the data the ArrayData will hold
182 * @param nbComponents the number of components
182 * @param nbComponents the number of components
183 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
183 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
184 * of the size of the data
184 * of the size of the data
185 */
185 */
186 template <int D = Dim, typename = std::enable_if_t<D == 2> >
186 template <int D = Dim, typename = std::enable_if_t<D == 2> >
187 explicit ArrayData(DataContainer data, int nbComponents)
187 explicit ArrayData(DataContainer data, int nbComponents)
188 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
188 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
189 {
189 {
190 if (nbComponents < 2) {
190 if (nbComponents < 2) {
191 throw std::invalid_argument{
191 throw std::invalid_argument{
192 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
192 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
193 .arg(nbComponents)
193 .arg(nbComponents)
194 .toStdString()};
194 .toStdString()};
195 }
195 }
196
196
197 if (m_Data.size() % m_NbComponents != 0) {
197 if (m_Data.size() % m_NbComponents != 0) {
198 throw std::invalid_argument{QString{
198 throw std::invalid_argument{QString{
199 "The number of components (%1) is inconsistent with the total number of data (%2)"}
199 "The number of components (%1) is inconsistent with the total number of data (%2)"}
200 .arg(m_Data.size(), nbComponents)
200 .arg(m_Data.size(), nbComponents)
201 .toStdString()};
201 .toStdString()};
202 }
202 }
203 }
203 }
204
204
205 /// Copy ctor
205 /// Copy ctor
206 explicit ArrayData(const ArrayData &other)
206 explicit ArrayData(const ArrayData &other)
207 {
207 {
208 QReadLocker otherLocker{&other.m_Lock};
208 QReadLocker otherLocker{&other.m_Lock};
209 m_Data = other.m_Data;
209 m_Data = other.m_Data;
210 m_NbComponents = other.m_NbComponents;
210 m_NbComponents = other.m_NbComponents;
211 }
211 }
212
212
213 // /////////////// //
213 // /////////////// //
214 // General methods //
214 // General methods //
215 // /////////////// //
215 // /////////////// //
216
216
217 /**
217 /**
218 * Merges into the array data an other array data. The two array datas must have the same number
218 * Merges into the array data an other array data. The two array datas must have the same number
219 * of components so the merge can be done
219 * of components so the merge can be done
220 * @param other the array data to merge with
220 * @param other the array data to merge with
221 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
221 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
222 * inserted at the end
222 * inserted at the end
223 */
223 */
224 void add(const ArrayData<Dim> &other, bool prepend = false)
224 void add(const ArrayData<Dim> &other, bool prepend = false)
225 {
225 {
226 QWriteLocker locker{&m_Lock};
226 QWriteLocker locker{&m_Lock};
227 QReadLocker otherLocker{&other.m_Lock};
227 QReadLocker otherLocker{&other.m_Lock};
228
228
229 if (m_NbComponents != other.componentCount()) {
229 if (m_NbComponents != other.componentCount()) {
230 return;
230 return;
231 }
231 }
232
232
233 if (prepend) {
233 if (prepend) {
234 auto otherDataSize = other.m_Data.size();
234 auto otherDataSize = other.m_Data.size();
235 m_Data.insert(m_Data.begin(), otherDataSize, 0.);
235 m_Data.insert(m_Data.begin(), otherDataSize, 0.);
236 for (auto i = 0; i < otherDataSize; ++i) {
236 for (auto i = 0; i < otherDataSize; ++i) {
237 m_Data.replace(i, other.m_Data.at(i));
237 m_Data.replace(i, other.m_Data.at(i));
238 }
238 }
239 }
239 }
240 else {
240 else {
241 m_Data.append(other.m_Data);
241 m_Data.append(other.m_Data);
242 }
242 }
243 }
243 }
244
244
245 void clear()
245 void clear()
246 {
246 {
247 QWriteLocker locker{&m_Lock};
247 QWriteLocker locker{&m_Lock};
248 m_Data.clear();
248 m_Data.clear();
249 }
249 }
250
250
251 int componentCount() const noexcept { return m_NbComponents; }
251 int componentCount() const noexcept { return m_NbComponents; }
252
252
253 /// @return the size (i.e. number of values) of a single component
253 /// @return the size (i.e. number of values) of a single component
254 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
254 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
255 int size() const
255 int size() const
256 {
256 {
257 QReadLocker locker{&m_Lock};
257 QReadLocker locker{&m_Lock};
258 return m_Data.size() / m_NbComponents;
258 return m_Data.size() / m_NbComponents;
259 }
259 }
260
260
261 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
261 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
262 {
262 {
263 QReadLocker locker{&m_Lock};
263 QReadLocker locker{&m_Lock};
264 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
264 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
265 }
265 }
266
266
267 // ///////// //
267 // ///////// //
268 // Iterators //
268 // Iterators //
269 // ///////// //
269 // ///////// //
270
270
271 ArrayDataIterator begin()
271 ArrayDataIterator begin()
272 {
272 {
273 return ArrayDataIterator{
273 return ArrayDataIterator{
274 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
274 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
275 m_Data, m_NbComponents, true)}};
275 m_Data, m_NbComponents, true)}};
276 }
276 }
277
277
278 ArrayDataIterator end()
278 ArrayDataIterator end()
279 {
279 {
280 return ArrayDataIterator{
280 return ArrayDataIterator{
281 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
281 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
282 m_Data, m_NbComponents, false)}};
282 m_Data, m_NbComponents, false)}};
283 }
283 }
284
284
285 ArrayDataIterator cbegin() const
285 ArrayDataIterator cbegin() const
286 {
286 {
287 return ArrayDataIterator{
287 return ArrayDataIterator{
288 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
288 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
289 m_Data, m_NbComponents, true)}};
289 m_Data, m_NbComponents, true)}};
290 }
290 }
291
291
292 ArrayDataIterator cend() const
292 ArrayDataIterator cend() const
293 {
293 {
294 return ArrayDataIterator{
294 return ArrayDataIterator{
295 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
295 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
296 m_Data, m_NbComponents, false)}};
296 m_Data, m_NbComponents, false)}};
297 }
297 }
298
298
299 void erase(ArrayDataIterator first, ArrayDataIterator last)
300 {
301 auto firstImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(first->impl());
302 auto lastImpl = dynamic_cast<arraydata_detail::IteratorValue<Dim, false> *>(last->impl());
303
304 if (firstImpl && lastImpl) {
305 m_Data.erase(firstImpl->m_It, lastImpl->m_It);
306 }
307 }
308
299 /// Inserts at the end of the array data the values passed as a parameter. This
309 /// Inserts at the end of the array data the values passed as a parameter. This
300 /// method is intended to be used in the context of generating a back insert iterator, or only
310 /// method is intended to be used in the context of generating a back insert iterator, or only
301 /// if it's ensured that the total size of the vector is consistent with the number of
311 /// if it's ensured that the total size of the vector is consistent with the number of
302 /// components of the array data
312 /// components of the array data
303 /// @param values the values to insert
313 /// @param values the values to insert
304 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
314 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
305 void push_back(const QVector<double> &values)
315 void push_back(const QVector<double> &values)
306 {
316 {
307 Q_ASSERT(values.size() % m_NbComponents == 0);
317 Q_ASSERT(values.size() % m_NbComponents == 0);
308 m_Data.append(values);
318 m_Data.append(values);
309 }
319 }
310
320
311 /**
321 /**
312 * @return the data at a specified index
322 * @return the data at a specified index
313 * @remarks index must be a valid position
323 * @remarks index must be a valid position
314 */
324 */
315 double at(int index) const noexcept
325 double at(int index) const noexcept
316 {
326 {
317 QReadLocker locker{&m_Lock};
327 QReadLocker locker{&m_Lock};
318 return m_Data.at(index);
328 return m_Data.at(index);
319 }
329 }
320
330
321 // ///////////// //
331 // ///////////// //
322 // 1-dim methods //
332 // 1-dim methods //
323 // ///////////// //
333 // ///////////// //
324
334
325 /**
335 /**
326 * @return the data as a vector, as a const reference
336 * @return the data as a vector, as a const reference
327 * @remarks this method is only available for a unidimensional ArrayData
337 * @remarks this method is only available for a unidimensional ArrayData
328 */
338 */
329 template <int D = Dim, typename = std::enable_if_t<D == 1> >
339 template <int D = Dim, typename = std::enable_if_t<D == 1> >
330 const QVector<double> &cdata() const noexcept
340 const QVector<double> &cdata() const noexcept
331 {
341 {
332 QReadLocker locker{&m_Lock};
342 QReadLocker locker{&m_Lock};
333 return m_Data;
343 return m_Data;
334 }
344 }
335
345
336 private:
346 private:
337 DataContainer m_Data;
347 DataContainer m_Data;
338 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
348 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
339 int m_NbComponents;
349 int m_NbComponents;
340 mutable QReadWriteLock m_Lock;
350 mutable QReadWriteLock m_Lock;
341 };
351 };
342
352
343 #endif // SCIQLOP_ARRAYDATA_H
353 #endif // SCIQLOP_ARRAYDATA_H
@@ -1,347 +1,359
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 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
57 bool equals(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 std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
59 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
60 }
60 }
61 catch (const std::bad_cast &) {
61 catch (const std::bad_cast &) {
62 return false;
62 return false;
63 }
63 }
64
64
65 void next() override
65 void next() override
66 {
66 {
67 ++m_XIt;
67 ++m_XIt;
68 ++m_ValuesIt;
68 ++m_ValuesIt;
69 }
69 }
70
70
71 void prev() override
71 void prev() override
72 {
72 {
73 --m_XIt;
73 --m_XIt;
74 --m_ValuesIt;
74 --m_ValuesIt;
75 }
75 }
76
76
77 double x() const override { return m_XIt->at(0); }
77 double x() const override { return m_XIt->at(0); }
78 double value() const override { return m_ValuesIt->at(0); }
78 double value() const override { return m_ValuesIt->at(0); }
79 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
79 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
80 double minValue() const override { return m_ValuesIt->min(); }
80 double minValue() const override { return m_ValuesIt->min(); }
81 double maxValue() const override { return m_ValuesIt->max(); }
81 double maxValue() const override { return m_ValuesIt->max(); }
82 QVector<double> values() const override { return m_ValuesIt->values(); }
82 QVector<double> values() const override { return m_ValuesIt->values(); }
83
83
84 void swap(DataSeriesIteratorValue::Impl &other) override
84 void swap(DataSeriesIteratorValue::Impl &other) override
85 {
85 {
86 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
86 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
87 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
87 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
88 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
88 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
89 }
89 }
90
90
91 private:
91 private:
92 ArrayDataIterator m_XIt;
92 ArrayDataIterator m_XIt;
93 ArrayDataIterator m_ValuesIt;
93 ArrayDataIterator m_ValuesIt;
94 };
94 };
95 } // namespace dataseries_detail
95 } // namespace dataseries_detail
96
96
97 /**
97 /**
98 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
98 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
99 *
99 *
100 * It proposes to set a dimension for the values ​​data.
100 * It proposes to set a dimension for the values ​​data.
101 *
101 *
102 * A DataSeries is always sorted on its x-axis data.
102 * A DataSeries is always sorted on its x-axis data.
103 *
103 *
104 * @tparam Dim The dimension of the values data
104 * @tparam Dim The dimension of the values data
105 *
105 *
106 */
106 */
107 template <int Dim>
107 template <int Dim>
108 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
108 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
109 friend class DataSeriesMergeHelper;
109 friend class DataSeriesMergeHelper;
110
110
111 public:
111 public:
112 /// Tag needed to define the push_back() method
112 /// Tag needed to define the push_back() method
113 /// @sa push_back()
113 /// @sa push_back()
114 using value_type = DataSeriesIteratorValue;
114 using value_type = DataSeriesIteratorValue;
115
115
116 /// @sa IDataSeries::xAxisData()
116 /// @sa IDataSeries::xAxisData()
117 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
117 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
118 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
118 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
119
119
120 /// @sa IDataSeries::xAxisUnit()
120 /// @sa IDataSeries::xAxisUnit()
121 Unit xAxisUnit() const override { return m_XAxisUnit; }
121 Unit xAxisUnit() const override { return m_XAxisUnit; }
122
122
123 /// @return the values dataset
123 /// @return the values dataset
124 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
124 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
125 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
125 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
126
126
127 /// @sa IDataSeries::valuesUnit()
127 /// @sa IDataSeries::valuesUnit()
128 Unit valuesUnit() const override { return m_ValuesUnit; }
128 Unit valuesUnit() const override { return m_ValuesUnit; }
129
129
130
130
131 SqpRange range() const override
131 SqpRange range() const override
132 {
132 {
133 if (!m_XAxisData->cdata().isEmpty()) {
133 if (!m_XAxisData->cdata().isEmpty()) {
134 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
134 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
135 }
135 }
136
136
137 return SqpRange{};
137 return SqpRange{};
138 }
138 }
139
139
140 void clear()
140 void clear()
141 {
141 {
142 m_XAxisData->clear();
142 m_XAxisData->clear();
143 m_ValuesData->clear();
143 m_ValuesData->clear();
144 }
144 }
145
145
146 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
146 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
147
147
148 /// Merges into the data series an other data series
148 /// Merges into the data series an other data series
149 /// @remarks the data series to merge with is cleared after the operation
149 /// @remarks the data series to merge with is cleared after the operation
150 void merge(IDataSeries *dataSeries) override
150 void merge(IDataSeries *dataSeries) override
151 {
151 {
152 dataSeries->lockWrite();
152 dataSeries->lockWrite();
153 lockWrite();
153 lockWrite();
154
154
155 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
155 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
156 DataSeriesMergeHelper::merge(*other, *this);
156 DataSeriesMergeHelper::merge(*other, *this);
157 }
157 }
158 else {
158 else {
159 qCWarning(LOG_DataSeries())
159 qCWarning(LOG_DataSeries())
160 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
160 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
161 }
161 }
162 unlock();
162 unlock();
163 dataSeries->unlock();
163 dataSeries->unlock();
164 }
164 }
165
165
166 // ///////// //
166 // ///////// //
167 // Iterators //
167 // Iterators //
168 // ///////// //
168 // ///////// //
169
169
170 DataSeriesIterator begin() override
170 DataSeriesIterator begin() override
171 {
171 {
172 return DataSeriesIterator{DataSeriesIteratorValue{
172 return DataSeriesIterator{DataSeriesIteratorValue{
173 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
173 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
174 }
174 }
175
175
176 DataSeriesIterator end() override
176 DataSeriesIterator end() override
177 {
177 {
178 return DataSeriesIterator{DataSeriesIteratorValue{
178 return DataSeriesIterator{DataSeriesIteratorValue{
179 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
179 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
180 }
180 }
181
181
182 DataSeriesIterator cbegin() const override
182 DataSeriesIterator cbegin() const override
183 {
183 {
184 return DataSeriesIterator{DataSeriesIteratorValue{
184 return DataSeriesIterator{DataSeriesIteratorValue{
185 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
185 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
186 }
186 }
187
187
188 DataSeriesIterator cend() const override
188 DataSeriesIterator cend() const override
189 {
189 {
190 return DataSeriesIterator{DataSeriesIteratorValue{
190 return DataSeriesIterator{DataSeriesIteratorValue{
191 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
191 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
192 }
192 }
193
193
194 void erase(DataSeriesIterator first, DataSeriesIterator last)
195 {
196 auto firstImpl
197 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
198 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
199
200 if (firstImpl && lastImpl) {
201 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
202 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
203 }
204 }
205
194 /// @sa IDataSeries::minXAxisData()
206 /// @sa IDataSeries::minXAxisData()
195 DataSeriesIterator minXAxisData(double minXAxisData) const override
207 DataSeriesIterator minXAxisData(double minXAxisData) const override
196 {
208 {
197 return std::lower_bound(
209 return std::lower_bound(
198 cbegin(), cend(), minXAxisData,
210 cbegin(), cend(), minXAxisData,
199 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
211 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
200 }
212 }
201
213
202 /// @sa IDataSeries::maxXAxisData()
214 /// @sa IDataSeries::maxXAxisData()
203 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
215 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
204 {
216 {
205 // Gets the first element that greater than max value
217 // Gets the first element that greater than max value
206 auto it = std::upper_bound(
218 auto it = std::upper_bound(
207 cbegin(), cend(), maxXAxisData,
219 cbegin(), cend(), maxXAxisData,
208 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
220 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
209
221
210 return it == cbegin() ? cend() : --it;
222 return it == cbegin() ? cend() : --it;
211 }
223 }
212
224
213 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
225 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
214 double maxXAxisData) const override
226 double maxXAxisData) const override
215 {
227 {
216 if (minXAxisData > maxXAxisData) {
228 if (minXAxisData > maxXAxisData) {
217 std::swap(minXAxisData, maxXAxisData);
229 std::swap(minXAxisData, maxXAxisData);
218 }
230 }
219
231
220 auto begin = cbegin();
232 auto begin = cbegin();
221 auto end = cend();
233 auto end = cend();
222
234
223 auto lowerIt = std::lower_bound(
235 auto lowerIt = std::lower_bound(
224 begin, end, minXAxisData,
236 begin, end, minXAxisData,
225 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
237 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
226 auto upperIt = std::upper_bound(
238 auto upperIt = std::upper_bound(
227 begin, end, maxXAxisData,
239 begin, end, maxXAxisData,
228 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
240 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
229
241
230 return std::make_pair(lowerIt, upperIt);
242 return std::make_pair(lowerIt, upperIt);
231 }
243 }
232
244
233 std::pair<DataSeriesIterator, DataSeriesIterator>
245 std::pair<DataSeriesIterator, DataSeriesIterator>
234 valuesBounds(double minXAxisData, double maxXAxisData) const override
246 valuesBounds(double minXAxisData, double maxXAxisData) const override
235 {
247 {
236 // Places iterators to the correct x-axis range
248 // Places iterators to the correct x-axis range
237 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
249 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
238
250
239 // Returns end iterators if the range is empty
251 // Returns end iterators if the range is empty
240 if (xAxisRangeIts.first == xAxisRangeIts.second) {
252 if (xAxisRangeIts.first == xAxisRangeIts.second) {
241 return std::make_pair(cend(), cend());
253 return std::make_pair(cend(), cend());
242 }
254 }
243
255
244 // Gets the iterator on the min of all values data
256 // Gets the iterator on the min of all values data
245 auto minIt = std::min_element(
257 auto minIt = std::min_element(
246 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
258 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
247 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
259 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
248 });
260 });
249
261
250 // Gets the iterator on the max of all values data
262 // Gets the iterator on the max of all values data
251 auto maxIt = std::max_element(
263 auto maxIt = std::max_element(
252 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
264 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
253 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
265 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
254 });
266 });
255
267
256 return std::make_pair(minIt, maxIt);
268 return std::make_pair(minIt, maxIt);
257 }
269 }
258
270
259 // /////// //
271 // /////// //
260 // Mutexes //
272 // Mutexes //
261 // /////// //
273 // /////// //
262
274
263 virtual void lockRead() { m_Lock.lockForRead(); }
275 virtual void lockRead() { m_Lock.lockForRead(); }
264 virtual void lockWrite() { m_Lock.lockForWrite(); }
276 virtual void lockWrite() { m_Lock.lockForWrite(); }
265 virtual void unlock() { m_Lock.unlock(); }
277 virtual void unlock() { m_Lock.unlock(); }
266
278
267 // ///// //
279 // ///// //
268 // Other //
280 // Other //
269 // ///// //
281 // ///// //
270
282
271 /// Inserts at the end of the data series the value of the iterator passed as a parameter. This
283 /// Inserts at the end of the data series the value of the iterator passed as a parameter. This
272 /// method is intended to be used in the context of generating a back insert iterator
284 /// method is intended to be used in the context of generating a back insert iterator
273 /// @param iteratorValue the iterator value containing the values to insert
285 /// @param iteratorValue the iterator value containing the values to insert
274 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
286 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
275 /// @sa merge()
287 /// @sa merge()
276 /// @sa value_type
288 /// @sa value_type
277 void push_back(const value_type &iteratorValue)
289 void push_back(const value_type &iteratorValue)
278 {
290 {
279 m_XAxisData->push_back(QVector<double>{iteratorValue.x()});
291 m_XAxisData->push_back(QVector<double>{iteratorValue.x()});
280 m_ValuesData->push_back(iteratorValue.values());
292 m_ValuesData->push_back(iteratorValue.values());
281 }
293 }
282
294
283 protected:
295 protected:
284 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
296 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
285 /// DataSeries with no values will be created.
297 /// DataSeries with no values will be created.
286 /// @remarks data series is automatically sorted on its x-axis data
298 /// @remarks data series is automatically sorted on its x-axis data
287 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
299 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
288 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
300 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
289 : m_XAxisData{xAxisData},
301 : m_XAxisData{xAxisData},
290 m_XAxisUnit{xAxisUnit},
302 m_XAxisUnit{xAxisUnit},
291 m_ValuesData{valuesData},
303 m_ValuesData{valuesData},
292 m_ValuesUnit{valuesUnit}
304 m_ValuesUnit{valuesUnit}
293 {
305 {
294 if (m_XAxisData->size() != m_ValuesData->size()) {
306 if (m_XAxisData->size() != m_ValuesData->size()) {
295 clear();
307 clear();
296 }
308 }
297
309
298 // Sorts data if it's not the case
310 // Sorts data if it's not the case
299 const auto &xAxisCData = m_XAxisData->cdata();
311 const auto &xAxisCData = m_XAxisData->cdata();
300 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
312 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
301 sort();
313 sort();
302 }
314 }
303 }
315 }
304
316
305 /// Copy ctor
317 /// Copy ctor
306 explicit DataSeries(const DataSeries<Dim> &other)
318 explicit DataSeries(const DataSeries<Dim> &other)
307 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
319 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
308 m_XAxisUnit{other.m_XAxisUnit},
320 m_XAxisUnit{other.m_XAxisUnit},
309 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
321 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
310 m_ValuesUnit{other.m_ValuesUnit}
322 m_ValuesUnit{other.m_ValuesUnit}
311 {
323 {
312 // Since a series is ordered from its construction and is always ordered, it is not
324 // Since a series is ordered from its construction and is always ordered, it is not
313 // necessary to call the sort method here ('other' is sorted)
325 // necessary to call the sort method here ('other' is sorted)
314 }
326 }
315
327
316 /// Assignment operator
328 /// Assignment operator
317 template <int D>
329 template <int D>
318 DataSeries &operator=(DataSeries<D> other)
330 DataSeries &operator=(DataSeries<D> other)
319 {
331 {
320 std::swap(m_XAxisData, other.m_XAxisData);
332 std::swap(m_XAxisData, other.m_XAxisData);
321 std::swap(m_XAxisUnit, other.m_XAxisUnit);
333 std::swap(m_XAxisUnit, other.m_XAxisUnit);
322 std::swap(m_ValuesData, other.m_ValuesData);
334 std::swap(m_ValuesData, other.m_ValuesData);
323 std::swap(m_ValuesUnit, other.m_ValuesUnit);
335 std::swap(m_ValuesUnit, other.m_ValuesUnit);
324
336
325 return *this;
337 return *this;
326 }
338 }
327
339
328 private:
340 private:
329 /**
341 /**
330 * Sorts data series on its x-axis data
342 * Sorts data series on its x-axis data
331 */
343 */
332 void sort() noexcept
344 void sort() noexcept
333 {
345 {
334 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
346 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
335 m_XAxisData = m_XAxisData->sort(permutation);
347 m_XAxisData = m_XAxisData->sort(permutation);
336 m_ValuesData = m_ValuesData->sort(permutation);
348 m_ValuesData = m_ValuesData->sort(permutation);
337 }
349 }
338
350
339 std::shared_ptr<ArrayData<1> > m_XAxisData;
351 std::shared_ptr<ArrayData<1> > m_XAxisData;
340 Unit m_XAxisUnit;
352 Unit m_XAxisUnit;
341 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
353 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
342 Unit m_ValuesUnit;
354 Unit m_ValuesUnit;
343
355
344 QReadWriteLock m_Lock;
356 QReadWriteLock m_Lock;
345 };
357 };
346
358
347 #endif // SCIQLOP_DATASERIES_H
359 #endif // SCIQLOP_DATASERIES_H
General Comments 0
You need to be logged in to leave comments. Login now