##// END OF EJS Templates
Adds comments in DataSeries description
Alexandre Leroux -
r868:6830a7435d55
parent child
Show More
@@ -1,429 +1,482
1 #ifndef SCIQLOP_DATASERIES_H
1 #ifndef SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
2 #define SCIQLOP_DATASERIES_H
3
3
4 #include "CoreGlobal.h"
4 #include "CoreGlobal.h"
5
5
6 #include <Common/SortUtils.h>
6 #include <Common/SortUtils.h>
7
7
8 #include <Data/ArrayData.h>
8 #include <Data/ArrayData.h>
9 #include <Data/DataSeriesMergeHelper.h>
9 #include <Data/DataSeriesMergeHelper.h>
10 #include <Data/IDataSeries.h>
10 #include <Data/IDataSeries.h>
11 #include <Data/OptionalAxis.h>
11 #include <Data/OptionalAxis.h>
12
12
13 #include <QLoggingCategory>
13 #include <QLoggingCategory>
14 #include <QReadLocker>
14 #include <QReadLocker>
15 #include <QReadWriteLock>
15 #include <QReadWriteLock>
16 #include <memory>
16 #include <memory>
17
17
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
18 // We don't use the Qt macro since the log is used in the header file, which causes multiple log
19 // definitions with inheritance. Inline method is used instead
19 // definitions with inheritance. Inline method is used instead
20 inline const QLoggingCategory &LOG_DataSeries()
20 inline const QLoggingCategory &LOG_DataSeries()
21 {
21 {
22 static const QLoggingCategory category{"DataSeries"};
22 static const QLoggingCategory category{"DataSeries"};
23 return category;
23 return category;
24 }
24 }
25
25
26 template <int Dim>
26 template <int Dim>
27 class DataSeries;
27 class DataSeries;
28
28
29 namespace dataseries_detail {
29 namespace dataseries_detail {
30
30
31 template <int Dim, bool IsConst>
31 template <int Dim, bool IsConst>
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 class IteratorValue : public DataSeriesIteratorValue::Impl {
33 public:
33 public:
34 friend class DataSeries<Dim>;
34 friend class DataSeries<Dim>;
35
35
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
40 {
40 {
41 }
41 }
42
42
43 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
43 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
44 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
44 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
45 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
45 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
46 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
46 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
47 : dataSeries.valuesData()->cend())
47 : dataSeries.valuesData()->cend())
48 {
48 {
49 }
49 }
50
50
51 IteratorValue(const IteratorValue &other) = default;
51 IteratorValue(const IteratorValue &other) = default;
52
52
53 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
53 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
54 {
54 {
55 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
55 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
56 }
56 }
57
57
58 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
58 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
59 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
60 return m_XIt->distance(*otherImpl.m_XIt);
60 return m_XIt->distance(*otherImpl.m_XIt);
61 }
61 }
62 catch (const std::bad_cast &) {
62 catch (const std::bad_cast &) {
63 return 0;
63 return 0;
64 }
64 }
65
65
66 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
66 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
67 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
67 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
68 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
68 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
69 }
69 }
70 catch (const std::bad_cast &) {
70 catch (const std::bad_cast &) {
71 return false;
71 return false;
72 }
72 }
73
73
74 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
74 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
75 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
75 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
76 return m_XIt->lowerThan(*otherImpl.m_XIt);
76 return m_XIt->lowerThan(*otherImpl.m_XIt);
77 }
77 }
78 catch (const std::bad_cast &) {
78 catch (const std::bad_cast &) {
79 return false;
79 return false;
80 }
80 }
81
81
82 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
82 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
83 {
83 {
84 auto result = clone();
84 auto result = clone();
85 result->next(offset);
85 result->next(offset);
86 return result;
86 return result;
87 }
87 }
88
88
89 void next(int offset) override
89 void next(int offset) override
90 {
90 {
91 m_XIt->next(offset);
91 m_XIt->next(offset);
92 m_ValuesIt->next(offset);
92 m_ValuesIt->next(offset);
93 }
93 }
94
94
95 void prev() override
95 void prev() override
96 {
96 {
97 --m_XIt;
97 --m_XIt;
98 --m_ValuesIt;
98 --m_ValuesIt;
99 }
99 }
100
100
101 double x() const override { return m_XIt->at(0); }
101 double x() const override { return m_XIt->at(0); }
102 double value() const override { return m_ValuesIt->at(0); }
102 double value() const override { return m_ValuesIt->at(0); }
103 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
103 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
104 double minValue() const override { return m_ValuesIt->min(); }
104 double minValue() const override { return m_ValuesIt->min(); }
105 double maxValue() const override { return m_ValuesIt->max(); }
105 double maxValue() const override { return m_ValuesIt->max(); }
106 QVector<double> values() const override { return m_ValuesIt->values(); }
106 QVector<double> values() const override { return m_ValuesIt->values(); }
107
107
108 void swap(DataSeriesIteratorValue::Impl &other) override
108 void swap(DataSeriesIteratorValue::Impl &other) override
109 {
109 {
110 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
110 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
111 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
111 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
112 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
112 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
113 }
113 }
114
114
115 private:
115 private:
116 ArrayDataIterator m_XIt;
116 ArrayDataIterator m_XIt;
117 ArrayDataIterator m_ValuesIt;
117 ArrayDataIterator m_ValuesIt;
118 };
118 };
119 } // namespace dataseries_detail
119 } // namespace dataseries_detail
120
120
121 /**
121 /**
122 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
122 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
123 *
123 *
124 * It proposes to set a dimension for the values ​​data.
124 * The DataSeries represents values on one or two axes, according to these rules:
125 * - the x-axis is always defined
126 * - an y-axis can be defined or not. If set, additional consistency checks apply to the values (see
127 * below)
128 * - the values are defined on one or two dimensions. In the case of 2-dim values, the data is
129 * distributed into components (for example, a vector defines three components)
130 * - New values can be added to the series, on the x-axis.
131 * - Once initialized to the series creation, the y-axis (if defined) is no longer modifiable
132 * - Data representing values and axes are associated with a unit
133 * - The data series is always sorted in ascending order on the x-axis.
125 *
134 *
126 * A DataSeries is always sorted on its x-axis data.
135 * Consistency checks are carried out between the axes and the values. These controls are provided
136 * throughout the DataSeries lifecycle:
137 * - the number of data on the x-axis must be equal to the number of values (in the case of
138 * 2-dim ArrayData for values, the test is performed on the number of values per component)
139 * - if the y-axis is defined, the number of components of the ArrayData for values must equal the
140 * number of data on the y-axis.
141 *
142 * Examples:
143 * 1)
144 * - x-axis: [1 ; 2 ; 3]
145 * - y-axis: not defined
146 * - values: [10 ; 20 ; 30] (1-dim ArrayData)
147 * => the DataSeries is valid, as x-axis and values have the same number of data
148 *
149 * 2)
150 * - x-axis: [1 ; 2 ; 3]
151 * - y-axis: not defined
152 * - values: [10 ; 20 ; 30 ; 40] (1-dim ArrayData)
153 * => the DataSeries is invalid, as x-axis and values haven't the same number of data
154 *
155 * 3)
156 * - x-axis: [1 ; 2 ; 3]
157 * - y-axis: not defined
158 * - values: [10 ; 20 ; 30
159 * 40 ; 50 ; 60] (2-dim ArrayData)
160 * => the DataSeries is valid, as x-axis has 3 data and values contains 2 components with 3
161 * data each
162 *
163 * 4)
164 * - x-axis: [1 ; 2 ; 3]
165 * - y-axis: [1 ; 2]
166 * - values: [10 ; 20 ; 30
167 * 40 ; 50 ; 60] (2-dim ArrayData)
168 * => the DataSeries is valid, as:
169 * - x-axis has 3 data and values contains 2 components with 3 data each AND
170 * - y-axis has 2 data and values contains 2 components
171 *
172 * 5)
173 * - x-axis: [1 ; 2 ; 3]
174 * - y-axis: [1 ; 2 ; 3]
175 * - values: [10 ; 20 ; 30
176 * 40 ; 50 ; 60] (2-dim ArrayData)
177 * => the DataSeries is invalid, as:
178 * - x-axis has 3 data and values contains 2 components with 3 data each BUT
179 * - y-axis has 3 data and values contains only 2 components
127 *
180 *
128 * @tparam Dim The dimension of the values data
181 * @tparam Dim The dimension of the values data
129 *
182 *
130 */
183 */
131 template <int Dim>
184 template <int Dim>
132 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
185 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
133 friend class DataSeriesMergeHelper;
186 friend class DataSeriesMergeHelper;
134
187
135 public:
188 public:
136 /// @sa IDataSeries::xAxisData()
189 /// @sa IDataSeries::xAxisData()
137 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
190 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
138 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
191 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
139
192
140 /// @sa IDataSeries::xAxisUnit()
193 /// @sa IDataSeries::xAxisUnit()
141 Unit xAxisUnit() const override { return m_XAxisUnit; }
194 Unit xAxisUnit() const override { return m_XAxisUnit; }
142
195
143 /// @return the values dataset
196 /// @return the values dataset
144 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
197 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
145 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
198 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
146
199
147 /// @sa IDataSeries::valuesUnit()
200 /// @sa IDataSeries::valuesUnit()
148 Unit valuesUnit() const override { return m_ValuesUnit; }
201 Unit valuesUnit() const override { return m_ValuesUnit; }
149
202
150 int nbPoints() const override { return m_ValuesData->totalSize(); }
203 int nbPoints() const override { return m_ValuesData->totalSize(); }
151
204
152 void clear()
205 void clear()
153 {
206 {
154 m_XAxisData->clear();
207 m_XAxisData->clear();
155 m_ValuesData->clear();
208 m_ValuesData->clear();
156 }
209 }
157
210
158 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
211 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
159
212
160 /// Merges into the data series an other data series.
213 /// Merges into the data series an other data series.
161 ///
214 ///
162 /// The two dataseries:
215 /// The two dataseries:
163 /// - must be of the same dimension
216 /// - must be of the same dimension
164 /// - must have the same y-axis (if defined)
217 /// - must have the same y-axis (if defined)
165 ///
218 ///
166 /// If the prerequisites are not valid, the method does nothing
219 /// If the prerequisites are not valid, the method does nothing
167 ///
220 ///
168 /// @remarks the data series to merge with is cleared after the operation
221 /// @remarks the data series to merge with is cleared after the operation
169 void merge(IDataSeries *dataSeries) override
222 void merge(IDataSeries *dataSeries) override
170 {
223 {
171 dataSeries->lockWrite();
224 dataSeries->lockWrite();
172 lockWrite();
225 lockWrite();
173
226
174 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
227 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
175 if (m_YAxis == other->m_YAxis) {
228 if (m_YAxis == other->m_YAxis) {
176 DataSeriesMergeHelper::merge(*other, *this);
229 DataSeriesMergeHelper::merge(*other, *this);
177 }
230 }
178 else {
231 else {
179 qCWarning(LOG_DataSeries())
232 qCWarning(LOG_DataSeries())
180 << QObject::tr("Can't merge data series that have not the same y-axis");
233 << QObject::tr("Can't merge data series that have not the same y-axis");
181 }
234 }
182 }
235 }
183 else {
236 else {
184 qCWarning(LOG_DataSeries())
237 qCWarning(LOG_DataSeries())
185 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
238 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
186 }
239 }
187 unlock();
240 unlock();
188 dataSeries->unlock();
241 dataSeries->unlock();
189 }
242 }
190
243
191 void purge(double min, double max) override
244 void purge(double min, double max) override
192 {
245 {
193 // Nothing to purge if series is empty
246 // Nothing to purge if series is empty
194 if (isEmpty()) {
247 if (isEmpty()) {
195 return;
248 return;
196 }
249 }
197
250
198 if (min > max) {
251 if (min > max) {
199 std::swap(min, max);
252 std::swap(min, max);
200 }
253 }
201
254
202 // Nothing to purge if series min/max are inside purge range
255 // Nothing to purge if series min/max are inside purge range
203 auto xMin = cbegin()->x();
256 auto xMin = cbegin()->x();
204 auto xMax = (--cend())->x();
257 auto xMax = (--cend())->x();
205 if (xMin >= min && xMax <= max) {
258 if (xMin >= min && xMax <= max) {
206 return;
259 return;
207 }
260 }
208
261
209 auto lowerIt = std::lower_bound(
262 auto lowerIt = std::lower_bound(
210 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
263 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
211 erase(begin(), lowerIt);
264 erase(begin(), lowerIt);
212 auto upperIt = std::upper_bound(
265 auto upperIt = std::upper_bound(
213 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
266 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
214 erase(upperIt, end());
267 erase(upperIt, end());
215 }
268 }
216
269
217 // ///////// //
270 // ///////// //
218 // Iterators //
271 // Iterators //
219 // ///////// //
272 // ///////// //
220
273
221 DataSeriesIterator begin() override
274 DataSeriesIterator begin() override
222 {
275 {
223 return DataSeriesIterator{DataSeriesIteratorValue{
276 return DataSeriesIterator{DataSeriesIteratorValue{
224 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
277 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
225 }
278 }
226
279
227 DataSeriesIterator end() override
280 DataSeriesIterator end() override
228 {
281 {
229 return DataSeriesIterator{DataSeriesIteratorValue{
282 return DataSeriesIterator{DataSeriesIteratorValue{
230 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
283 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
231 }
284 }
232
285
233 DataSeriesIterator cbegin() const override
286 DataSeriesIterator cbegin() const override
234 {
287 {
235 return DataSeriesIterator{DataSeriesIteratorValue{
288 return DataSeriesIterator{DataSeriesIteratorValue{
236 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
289 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
237 }
290 }
238
291
239 DataSeriesIterator cend() const override
292 DataSeriesIterator cend() const override
240 {
293 {
241 return DataSeriesIterator{DataSeriesIteratorValue{
294 return DataSeriesIterator{DataSeriesIteratorValue{
242 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
295 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
243 }
296 }
244
297
245 void erase(DataSeriesIterator first, DataSeriesIterator last)
298 void erase(DataSeriesIterator first, DataSeriesIterator last)
246 {
299 {
247 auto firstImpl
300 auto firstImpl
248 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
301 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
249 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
302 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
250
303
251 if (firstImpl && lastImpl) {
304 if (firstImpl && lastImpl) {
252 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
305 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
253 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
306 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
254 }
307 }
255 }
308 }
256
309
257 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
310 void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false)
258 {
311 {
259 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
312 auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl());
260 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
313 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl());
261
314
262 if (firstImpl && lastImpl) {
315 if (firstImpl && lastImpl) {
263 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
316 m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend);
264 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
317 m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend);
265 }
318 }
266 }
319 }
267
320
268 /// @sa IDataSeries::minXAxisData()
321 /// @sa IDataSeries::minXAxisData()
269 DataSeriesIterator minXAxisData(double minXAxisData) const override
322 DataSeriesIterator minXAxisData(double minXAxisData) const override
270 {
323 {
271 return std::lower_bound(
324 return std::lower_bound(
272 cbegin(), cend(), minXAxisData,
325 cbegin(), cend(), minXAxisData,
273 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
326 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
274 }
327 }
275
328
276 /// @sa IDataSeries::maxXAxisData()
329 /// @sa IDataSeries::maxXAxisData()
277 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
330 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
278 {
331 {
279 // Gets the first element that greater than max value
332 // Gets the first element that greater than max value
280 auto it = std::upper_bound(
333 auto it = std::upper_bound(
281 cbegin(), cend(), maxXAxisData,
334 cbegin(), cend(), maxXAxisData,
282 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
335 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
283
336
284 return it == cbegin() ? cend() : --it;
337 return it == cbegin() ? cend() : --it;
285 }
338 }
286
339
287 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
340 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
288 double maxXAxisData) const override
341 double maxXAxisData) const override
289 {
342 {
290 if (minXAxisData > maxXAxisData) {
343 if (minXAxisData > maxXAxisData) {
291 std::swap(minXAxisData, maxXAxisData);
344 std::swap(minXAxisData, maxXAxisData);
292 }
345 }
293
346
294 auto begin = cbegin();
347 auto begin = cbegin();
295 auto end = cend();
348 auto end = cend();
296
349
297 auto lowerIt = std::lower_bound(
350 auto lowerIt = std::lower_bound(
298 begin, end, minXAxisData,
351 begin, end, minXAxisData,
299 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
352 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
300 auto upperIt = std::upper_bound(
353 auto upperIt = std::upper_bound(
301 lowerIt, end, maxXAxisData,
354 lowerIt, end, maxXAxisData,
302 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
355 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
303
356
304 return std::make_pair(lowerIt, upperIt);
357 return std::make_pair(lowerIt, upperIt);
305 }
358 }
306
359
307 std::pair<DataSeriesIterator, DataSeriesIterator>
360 std::pair<DataSeriesIterator, DataSeriesIterator>
308 valuesBounds(double minXAxisData, double maxXAxisData) const override
361 valuesBounds(double minXAxisData, double maxXAxisData) const override
309 {
362 {
310 // Places iterators to the correct x-axis range
363 // Places iterators to the correct x-axis range
311 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
364 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
312
365
313 // Returns end iterators if the range is empty
366 // Returns end iterators if the range is empty
314 if (xAxisRangeIts.first == xAxisRangeIts.second) {
367 if (xAxisRangeIts.first == xAxisRangeIts.second) {
315 return std::make_pair(cend(), cend());
368 return std::make_pair(cend(), cend());
316 }
369 }
317
370
318 // Gets the iterator on the min of all values data
371 // Gets the iterator on the min of all values data
319 auto minIt = std::min_element(
372 auto minIt = std::min_element(
320 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
373 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
321 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
374 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
322 });
375 });
323
376
324 // Gets the iterator on the max of all values data
377 // Gets the iterator on the max of all values data
325 auto maxIt = std::max_element(
378 auto maxIt = std::max_element(
326 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
379 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
327 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
380 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
328 });
381 });
329
382
330 return std::make_pair(minIt, maxIt);
383 return std::make_pair(minIt, maxIt);
331 }
384 }
332
385
333 // /////// //
386 // /////// //
334 // Mutexes //
387 // Mutexes //
335 // /////// //
388 // /////// //
336
389
337 virtual void lockRead() { m_Lock.lockForRead(); }
390 virtual void lockRead() { m_Lock.lockForRead(); }
338 virtual void lockWrite() { m_Lock.lockForWrite(); }
391 virtual void lockWrite() { m_Lock.lockForWrite(); }
339 virtual void unlock() { m_Lock.unlock(); }
392 virtual void unlock() { m_Lock.unlock(); }
340
393
341 protected:
394 protected:
342 /// Protected ctor (DataSeries is abstract).
395 /// Protected ctor (DataSeries is abstract).
343 ///
396 ///
344 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
397 /// Data vectors must be consistent with each other, otherwise an exception will be thrown (@sa
345 /// class description for consistent rules)
398 /// class description for consistent rules)
346 /// @remarks data series is automatically sorted on its x-axis data
399 /// @remarks data series is automatically sorted on its x-axis data
347 /// @throws std::invalid_argument if the data are inconsistent with each other
400 /// @throws std::invalid_argument if the data are inconsistent with each other
348 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
401 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
349 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
402 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit,
350 OptionalAxis yAxis = OptionalAxis{})
403 OptionalAxis yAxis = OptionalAxis{})
351 : m_XAxisData{xAxisData},
404 : m_XAxisData{xAxisData},
352 m_XAxisUnit{xAxisUnit},
405 m_XAxisUnit{xAxisUnit},
353 m_ValuesData{valuesData},
406 m_ValuesData{valuesData},
354 m_ValuesUnit{valuesUnit},
407 m_ValuesUnit{valuesUnit},
355 m_YAxis{std::move(yAxis)}
408 m_YAxis{std::move(yAxis)}
356 {
409 {
357 if (m_XAxisData->size() != m_ValuesData->size()) {
410 if (m_XAxisData->size() != m_ValuesData->size()) {
358 throw std::invalid_argument{
411 throw std::invalid_argument{
359 "The number of values by component must be equal to the number of x-axis data"};
412 "The number of values by component must be equal to the number of x-axis data"};
360 }
413 }
361
414
362 // Validates y-axis (if defined)
415 // Validates y-axis (if defined)
363 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
416 if (yAxis.isDefined() && (yAxis.size() != m_ValuesData->componentCount())) {
364 throw std::invalid_argument{
417 throw std::invalid_argument{
365 "As the y-axis is defined, the number of value components must be equal to the "
418 "As the y-axis is defined, the number of value components must be equal to the "
366 "number of y-axis data"};
419 "number of y-axis data"};
367 }
420 }
368
421
369 // Sorts data if it's not the case
422 // Sorts data if it's not the case
370 const auto &xAxisCData = m_XAxisData->cdata();
423 const auto &xAxisCData = m_XAxisData->cdata();
371 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
424 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
372 sort();
425 sort();
373 }
426 }
374 }
427 }
375
428
376 /// Copy ctor
429 /// Copy ctor
377 explicit DataSeries(const DataSeries<Dim> &other)
430 explicit DataSeries(const DataSeries<Dim> &other)
378 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
431 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
379 m_XAxisUnit{other.m_XAxisUnit},
432 m_XAxisUnit{other.m_XAxisUnit},
380 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
433 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
381 m_ValuesUnit{other.m_ValuesUnit},
434 m_ValuesUnit{other.m_ValuesUnit},
382 m_YAxis{other.m_YAxis}
435 m_YAxis{other.m_YAxis}
383 {
436 {
384 // Since a series is ordered from its construction and is always ordered, it is not
437 // Since a series is ordered from its construction and is always ordered, it is not
385 // necessary to call the sort method here ('other' is sorted)
438 // necessary to call the sort method here ('other' is sorted)
386 }
439 }
387
440
388 /// @return the y-axis associated to the data series
441 /// @return the y-axis associated to the data series
389 OptionalAxis yAxis() const { return m_YAxis; }
442 OptionalAxis yAxis() const { return m_YAxis; }
390
443
391 /// Assignment operator
444 /// Assignment operator
392 template <int D>
445 template <int D>
393 DataSeries &operator=(DataSeries<D> other)
446 DataSeries &operator=(DataSeries<D> other)
394 {
447 {
395 std::swap(m_XAxisData, other.m_XAxisData);
448 std::swap(m_XAxisData, other.m_XAxisData);
396 std::swap(m_XAxisUnit, other.m_XAxisUnit);
449 std::swap(m_XAxisUnit, other.m_XAxisUnit);
397 std::swap(m_ValuesData, other.m_ValuesData);
450 std::swap(m_ValuesData, other.m_ValuesData);
398 std::swap(m_ValuesUnit, other.m_ValuesUnit);
451 std::swap(m_ValuesUnit, other.m_ValuesUnit);
399 std::swap(m_YAxis, other.m_YAxis);
452 std::swap(m_YAxis, other.m_YAxis);
400
453
401 return *this;
454 return *this;
402 }
455 }
403
456
404 private:
457 private:
405 /**
458 /**
406 * Sorts data series on its x-axis data
459 * Sorts data series on its x-axis data
407 */
460 */
408 void sort() noexcept
461 void sort() noexcept
409 {
462 {
410 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
463 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
411 m_XAxisData = m_XAxisData->sort(permutation);
464 m_XAxisData = m_XAxisData->sort(permutation);
412 m_ValuesData = m_ValuesData->sort(permutation);
465 m_ValuesData = m_ValuesData->sort(permutation);
413 }
466 }
414
467
415 // x-axis
468 // x-axis
416 std::shared_ptr<ArrayData<1> > m_XAxisData;
469 std::shared_ptr<ArrayData<1> > m_XAxisData;
417 Unit m_XAxisUnit;
470 Unit m_XAxisUnit;
418
471
419 // values
472 // values
420 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
473 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
421 Unit m_ValuesUnit;
474 Unit m_ValuesUnit;
422
475
423 // y-axis (optional)
476 // y-axis (optional)
424 OptionalAxis m_YAxis;
477 OptionalAxis m_YAxis;
425
478
426 QReadWriteLock m_Lock;
479 QReadWriteLock m_Lock;
427 };
480 };
428
481
429 #endif // SCIQLOP_DATASERIES_H
482 #endif // SCIQLOP_DATASERIES_H
General Comments 0
You need to be logged in to leave comments. Login now