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