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