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