##// END OF EJS Templates
Merge branch 'feature/PurgeDataSeries' into develop
Alexandre Leroux -
r633:e899bacb01d9 merge
parent child
Show More
@@ -1,282 +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 template <int Dim, bool IsConst>
43 class IteratorValue;
44
45 template <int Dim, bool IsConst>
46 struct IteratorValueBuilder {
47 };
48
49 template <int Dim>
50 struct IteratorValueBuilder<Dim, true> {
51 using DataContainerIterator = DataContainer::const_iterator;
52
53 static void swap(IteratorValue<Dim, true> &o1, IteratorValue<Dim, true> &o2) {}
54 };
55
42 56 template <int Dim>
57 struct IteratorValueBuilder<Dim, false> {
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 }
66 };
67
68 template <int Dim, bool IsConst>
43 69 class IteratorValue : public ArrayDataIteratorValue::Impl {
44 70 public:
71 friend class ArrayData<Dim>;
72 friend class IteratorValueBuilder<Dim, IsConst>;
73
74 using DataContainerIterator =
75 typename IteratorValueBuilder<Dim, IsConst>::DataContainerIterator;
76
77 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
45 78 explicit IteratorValue(const DataContainer &container, int nbComponents, bool begin)
46 79 : m_It{begin ? container.cbegin() : container.cend()}, m_NbComponents{nbComponents}
47 80 {
48 81 }
49 82
83 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
84 explicit IteratorValue(DataContainer &container, int nbComponents, bool begin)
85 : m_It{begin ? container.begin() : container.end()}, m_NbComponents{nbComponents}
86 {
87 }
88
50 89 IteratorValue(const IteratorValue &other) = default;
51 90
52 91 std::unique_ptr<ArrayDataIteratorValue::Impl> clone() const override
53 92 {
54 return std::make_unique<IteratorValue<Dim> >(*this);
93 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
55 94 }
56 95
57 96 bool equals(const ArrayDataIteratorValue::Impl &other) const override try {
58 97 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 98 return std::tie(m_It, m_NbComponents) == std::tie(otherImpl.m_It, otherImpl.m_NbComponents);
60 99 }
61 100 catch (const std::bad_cast &) {
62 101 return false;
63 102 }
64 103
65 104 void next() override { std::advance(m_It, m_NbComponents); }
66 105 void prev() override { std::advance(m_It, -m_NbComponents); }
67 106
68 107 double at(int componentIndex) const override { return *(m_It + componentIndex); }
69 108 double first() const override { return *m_It; }
70 109 double min() const override
71 110 {
72 111 auto values = this->values();
73 112 auto end = values.cend();
74 113 auto it = std::min_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
75 114 return SortUtils::minCompareWithNaN(v1, v2);
76 115 });
77 116
78 117 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
79 118 }
80 119 double max() const override
81 120 {
82 121 auto values = this->values();
83 122 auto end = values.cend();
84 123 auto it = std::max_element(values.cbegin(), end, [](const auto &v1, const auto &v2) {
85 124 return SortUtils::maxCompareWithNaN(v1, v2);
86 125 });
87 126 return it != end ? *it : std::numeric_limits<double>::quiet_NaN();
88 127 }
89 128
90 129 QVector<double> values() const override
91 130 {
92 131 auto result = QVector<double>{};
93 132 for (auto i = 0; i < m_NbComponents; ++i) {
94 133 result.push_back(*(m_It + i));
95 134 }
96 135
97 136 return result;
98 137 }
99 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
100 145 private:
101 DataContainer::const_iterator m_It;
146 DataContainerIterator m_It;
102 147 int m_NbComponents;
103 148 };
104 149
105 150 } // namespace arraydata_detail
106 151
107 152 /**
108 153 * @brief The ArrayData class represents a dataset for a data series.
109 154 *
110 155 * A dataset can be unidimensional or two-dimensional. This property is determined by the Dim
111 156 * template-parameter. In a case of a two-dimensional dataset, each dataset component has the same
112 157 * number of values
113 158 *
114 159 * @tparam Dim the dimension of the ArrayData (one or two)
115 160 * @sa IDataSeries
116 161 */
117 162 template <int Dim>
118 163 class ArrayData {
119 164 public:
120 165 // ///// //
121 166 // Ctors //
122 167 // ///// //
123 168
124 169 /**
125 170 * Ctor for a unidimensional ArrayData
126 171 * @param data the data the ArrayData will hold
127 172 */
128 173 template <int D = Dim, typename = std::enable_if_t<D == 1> >
129 174 explicit ArrayData(DataContainer data) : m_Data{std::move(data)}, m_NbComponents{1}
130 175 {
131 176 }
132 177
133 178 /**
134 179 * Ctor for a two-dimensional ArrayData. The number of components (number of lines) must be
135 180 * greater than 2 and must be a divisor of the total number of data in the vector
136 181 * @param data the data the ArrayData will hold
137 182 * @param nbComponents the number of components
138 183 * @throws std::invalid_argument if the number of components is less than 2 or is not a divisor
139 184 * of the size of the data
140 185 */
141 186 template <int D = Dim, typename = std::enable_if_t<D == 2> >
142 187 explicit ArrayData(DataContainer data, int nbComponents)
143 188 : m_Data{std::move(data)}, m_NbComponents{nbComponents}
144 189 {
145 190 if (nbComponents < 2) {
146 191 throw std::invalid_argument{
147 192 QString{"A multidimensional ArrayData must have at least 2 components (found: %1)"}
148 193 .arg(nbComponents)
149 194 .toStdString()};
150 195 }
151 196
152 197 if (m_Data.size() % m_NbComponents != 0) {
153 198 throw std::invalid_argument{QString{
154 199 "The number of components (%1) is inconsistent with the total number of data (%2)"}
155 200 .arg(m_Data.size(), nbComponents)
156 201 .toStdString()};
157 202 }
158 203 }
159 204
160 205 /// Copy ctor
161 206 explicit ArrayData(const ArrayData &other)
162 207 {
163 208 QReadLocker otherLocker{&other.m_Lock};
164 209 m_Data = other.m_Data;
165 210 m_NbComponents = other.m_NbComponents;
166 211 }
167 212
168 213 // /////////////// //
169 214 // General methods //
170 215 // /////////////// //
171 216
172 217 /**
173 218 * Merges into the array data an other array data. The two array datas must have the same number
174 219 * of components so the merge can be done
175 220 * @param other the array data to merge with
176 221 * @param prepend if true, the other array data is inserted at the beginning, otherwise it is
177 222 * inserted at the end
178 223 */
179 224 void add(const ArrayData<Dim> &other, bool prepend = false)
180 225 {
181 226 QWriteLocker locker{&m_Lock};
182 227 QReadLocker otherLocker{&other.m_Lock};
183 228
184 229 if (m_NbComponents != other.componentCount()) {
185 230 return;
186 231 }
187 232
188 233 if (prepend) {
189 234 auto otherDataSize = other.m_Data.size();
190 235 m_Data.insert(m_Data.begin(), otherDataSize, 0.);
191 236 for (auto i = 0; i < otherDataSize; ++i) {
192 237 m_Data.replace(i, other.m_Data.at(i));
193 238 }
194 239 }
195 240 else {
196 241 m_Data.append(other.m_Data);
197 242 }
198 243 }
199 244
200 245 void clear()
201 246 {
202 247 QWriteLocker locker{&m_Lock};
203 248 m_Data.clear();
204 249 }
205 250
206 251 int componentCount() const noexcept { return m_NbComponents; }
207 252
208 253 /// @return the size (i.e. number of values) of a single component
209 254 /// @remarks in a case of a two-dimensional ArrayData, each component has the same size
210 255 int size() const
211 256 {
212 257 QReadLocker locker{&m_Lock};
213 258 return m_Data.size() / m_NbComponents;
214 259 }
215 260
216 261 std::shared_ptr<ArrayData<Dim> > sort(const std::vector<int> &sortPermutation)
217 262 {
218 263 QReadLocker locker{&m_Lock};
219 264 return arraydata_detail::Sort<Dim>::sort(m_Data, m_NbComponents, sortPermutation);
220 265 }
221 266
222 267 // ///////// //
223 268 // Iterators //
224 269 // ///////// //
225 270
271 ArrayDataIterator begin()
272 {
273 return ArrayDataIterator{
274 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
275 m_Data, m_NbComponents, true)}};
276 }
277
278 ArrayDataIterator end()
279 {
280 return ArrayDataIterator{
281 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, false> >(
282 m_Data, m_NbComponents, false)}};
283 }
284
226 285 ArrayDataIterator cbegin() const
227 286 {
228 return ArrayDataIterator{ArrayDataIteratorValue{
229 std::make_unique<arraydata_detail::IteratorValue<Dim> >(m_Data, m_NbComponents, true)}};
287 return ArrayDataIterator{
288 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
289 m_Data, m_NbComponents, true)}};
230 290 }
291
231 292 ArrayDataIterator cend() const
232 293 {
233 294 return ArrayDataIterator{
234 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim> >(
295 ArrayDataIteratorValue{std::make_unique<arraydata_detail::IteratorValue<Dim, true> >(
235 296 m_Data, m_NbComponents, false)}};
236 297 }
237 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
238 309 /// Inserts at the end of the array data the values passed as a parameter. This
239 310 /// method is intended to be used in the context of generating a back insert iterator, or only
240 311 /// if it's ensured that the total size of the vector is consistent with the number of
241 312 /// components of the array data
242 313 /// @param values the values to insert
243 314 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
244 315 void push_back(const QVector<double> &values)
245 316 {
246 317 Q_ASSERT(values.size() % m_NbComponents == 0);
247 318 m_Data.append(values);
248 319 }
249 320
250 321 /**
251 322 * @return the data at a specified index
252 323 * @remarks index must be a valid position
253 324 */
254 325 double at(int index) const noexcept
255 326 {
256 327 QReadLocker locker{&m_Lock};
257 328 return m_Data.at(index);
258 329 }
259 330
260 331 // ///////////// //
261 332 // 1-dim methods //
262 333 // ///////////// //
263 334
264 335 /**
265 336 * @return the data as a vector, as a const reference
266 337 * @remarks this method is only available for a unidimensional ArrayData
267 338 */
268 339 template <int D = Dim, typename = std::enable_if_t<D == 1> >
269 340 const QVector<double> &cdata() const noexcept
270 341 {
271 342 QReadLocker locker{&m_Lock};
272 343 return m_Data;
273 344 }
274 345
275 346 private:
276 347 DataContainer m_Data;
277 348 /// Number of components (lines). Is always 1 in a 1-dim ArrayData
278 349 int m_NbComponents;
279 350 mutable QReadWriteLock m_Lock;
280 351 };
281 352
282 353 #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,317 +1,374
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 template <int Dim>
30 template <int Dim, bool IsConst>
31 31 class IteratorValue : public DataSeriesIteratorValue::Impl {
32 32 public:
33 friend class DataSeries<Dim>;
34
35 template <bool IC = IsConst, typename = std::enable_if_t<IC == false> >
36 explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin)
37 : m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()),
38 m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end())
39 {
40 }
41
42 template <bool IC = IsConst, typename = std::enable_if_t<IC == true> >
33 43 explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin)
34 44 : m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()),
35 45 m_ValuesIt(begin ? dataSeries.valuesData()->cbegin()
36 46 : dataSeries.valuesData()->cend())
37 47 {
38 48 }
49
39 50 IteratorValue(const IteratorValue &other) = default;
40 51
41 52 std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override
42 53 {
43 return std::make_unique<IteratorValue<Dim> >(*this);
54 return std::make_unique<IteratorValue<Dim, IsConst> >(*this);
44 55 }
45 56
46 57 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
47 58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
48 59 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
49 60 }
50 61 catch (const std::bad_cast &) {
51 62 return false;
52 63 }
53 64
54 65 void next() override
55 66 {
56 67 ++m_XIt;
57 68 ++m_ValuesIt;
58 69 }
59 70
60 71 void prev() override
61 72 {
62 73 --m_XIt;
63 74 --m_ValuesIt;
64 75 }
65 76
66 77 double x() const override { return m_XIt->at(0); }
67 78 double value() const override { return m_ValuesIt->at(0); }
68 79 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
69 80 double minValue() const override { return m_ValuesIt->min(); }
70 81 double maxValue() const override { return m_ValuesIt->max(); }
71 82 QVector<double> values() const override { return m_ValuesIt->values(); }
72 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
73 91 private:
74 92 ArrayDataIterator m_XIt;
75 93 ArrayDataIterator m_ValuesIt;
76 94 };
77 95 } // namespace dataseries_detail
78 96
79 97 /**
80 98 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
81 99 *
82 100 * It proposes to set a dimension for the values ​​data.
83 101 *
84 102 * A DataSeries is always sorted on its x-axis data.
85 103 *
86 104 * @tparam Dim The dimension of the values data
87 105 *
88 106 */
89 107 template <int Dim>
90 108 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
91 109 friend class DataSeriesMergeHelper;
92 110
93 111 public:
94 112 /// Tag needed to define the push_back() method
95 113 /// @sa push_back()
96 114 using value_type = DataSeriesIteratorValue;
97 115
98 116 /// @sa IDataSeries::xAxisData()
99 117 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
100 118 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
101 119
102 120 /// @sa IDataSeries::xAxisUnit()
103 121 Unit xAxisUnit() const override { return m_XAxisUnit; }
104 122
105 123 /// @return the values dataset
106 124 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
107 125 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
108 126
109 127 /// @sa IDataSeries::valuesUnit()
110 128 Unit valuesUnit() const override { return m_ValuesUnit; }
111 129
112 130
113 131 SqpRange range() const override
114 132 {
115 133 if (!m_XAxisData->cdata().isEmpty()) {
116 134 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
117 135 }
118 136
119 137 return SqpRange{};
120 138 }
121 139
122 140 void clear()
123 141 {
124 142 m_XAxisData->clear();
125 143 m_ValuesData->clear();
126 144 }
127 145
128 146 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
129 147
130 148 /// Merges into the data series an other data series
131 149 /// @remarks the data series to merge with is cleared after the operation
132 150 void merge(IDataSeries *dataSeries) override
133 151 {
134 152 dataSeries->lockWrite();
135 153 lockWrite();
136 154
137 155 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
138 156 DataSeriesMergeHelper::merge(*other, *this);
139 157 }
140 158 else {
141 159 qCWarning(LOG_DataSeries())
142 160 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
143 161 }
144 162 unlock();
145 163 dataSeries->unlock();
146 164 }
147 165
166 void purge(double min, double max) override
167 {
168 if (min > max) {
169 std::swap(min, max);
170 }
171
172 lockWrite();
173
174 auto it = std::remove_if(
175 begin(), end(), [min, max](const auto &it) { return it.x() < min || it.x() > max; });
176 erase(it, end());
177
178 unlock();
179 }
180
148 181 // ///////// //
149 182 // Iterators //
150 183 // ///////// //
151 184
185 DataSeriesIterator begin() override
186 {
187 return DataSeriesIterator{DataSeriesIteratorValue{
188 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
189 }
190
191 DataSeriesIterator end() override
192 {
193 return DataSeriesIterator{DataSeriesIteratorValue{
194 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
195 }
196
152 197 DataSeriesIterator cbegin() const override
153 198 {
154 199 return DataSeriesIterator{DataSeriesIteratorValue{
155 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, true)}};
200 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
156 201 }
157 202
158 203 DataSeriesIterator cend() const override
159 204 {
160 205 return DataSeriesIterator{DataSeriesIteratorValue{
161 std::make_unique<dataseries_detail::IteratorValue<Dim> >(*this, false)}};
206 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
207 }
208
209 void erase(DataSeriesIterator first, DataSeriesIterator last)
210 {
211 auto firstImpl
212 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
213 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
214
215 if (firstImpl && lastImpl) {
216 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
217 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
218 }
162 219 }
163 220
164 221 /// @sa IDataSeries::minXAxisData()
165 222 DataSeriesIterator minXAxisData(double minXAxisData) const override
166 223 {
167 224 return std::lower_bound(
168 225 cbegin(), cend(), minXAxisData,
169 226 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
170 227 }
171 228
172 229 /// @sa IDataSeries::maxXAxisData()
173 230 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
174 231 {
175 232 // Gets the first element that greater than max value
176 233 auto it = std::upper_bound(
177 234 cbegin(), cend(), maxXAxisData,
178 235 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
179 236
180 237 return it == cbegin() ? cend() : --it;
181 238 }
182 239
183 240 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
184 241 double maxXAxisData) const override
185 242 {
186 243 if (minXAxisData > maxXAxisData) {
187 244 std::swap(minXAxisData, maxXAxisData);
188 245 }
189 246
190 247 auto begin = cbegin();
191 248 auto end = cend();
192 249
193 250 auto lowerIt = std::lower_bound(
194 251 begin, end, minXAxisData,
195 252 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
196 253 auto upperIt = std::upper_bound(
197 254 begin, end, maxXAxisData,
198 255 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
199 256
200 257 return std::make_pair(lowerIt, upperIt);
201 258 }
202 259
203 260 std::pair<DataSeriesIterator, DataSeriesIterator>
204 261 valuesBounds(double minXAxisData, double maxXAxisData) const override
205 262 {
206 263 // Places iterators to the correct x-axis range
207 264 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
208 265
209 266 // Returns end iterators if the range is empty
210 267 if (xAxisRangeIts.first == xAxisRangeIts.second) {
211 268 return std::make_pair(cend(), cend());
212 269 }
213 270
214 271 // Gets the iterator on the min of all values data
215 272 auto minIt = std::min_element(
216 273 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
217 274 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
218 275 });
219 276
220 277 // Gets the iterator on the max of all values data
221 278 auto maxIt = std::max_element(
222 279 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
223 280 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
224 281 });
225 282
226 283 return std::make_pair(minIt, maxIt);
227 284 }
228 285
229 286 // /////// //
230 287 // Mutexes //
231 288 // /////// //
232 289
233 290 virtual void lockRead() { m_Lock.lockForRead(); }
234 291 virtual void lockWrite() { m_Lock.lockForWrite(); }
235 292 virtual void unlock() { m_Lock.unlock(); }
236 293
237 294 // ///// //
238 295 // Other //
239 296 // ///// //
240 297
241 298 /// Inserts at the end of the data series the value of the iterator passed as a parameter. This
242 299 /// method is intended to be used in the context of generating a back insert iterator
243 300 /// @param iteratorValue the iterator value containing the values to insert
244 301 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
245 302 /// @sa merge()
246 303 /// @sa value_type
247 304 void push_back(const value_type &iteratorValue)
248 305 {
249 306 m_XAxisData->push_back(QVector<double>{iteratorValue.x()});
250 307 m_ValuesData->push_back(iteratorValue.values());
251 308 }
252 309
253 310 protected:
254 311 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
255 312 /// DataSeries with no values will be created.
256 313 /// @remarks data series is automatically sorted on its x-axis data
257 314 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
258 315 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
259 316 : m_XAxisData{xAxisData},
260 317 m_XAxisUnit{xAxisUnit},
261 318 m_ValuesData{valuesData},
262 319 m_ValuesUnit{valuesUnit}
263 320 {
264 321 if (m_XAxisData->size() != m_ValuesData->size()) {
265 322 clear();
266 323 }
267 324
268 325 // Sorts data if it's not the case
269 326 const auto &xAxisCData = m_XAxisData->cdata();
270 327 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
271 328 sort();
272 329 }
273 330 }
274 331
275 332 /// Copy ctor
276 333 explicit DataSeries(const DataSeries<Dim> &other)
277 334 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
278 335 m_XAxisUnit{other.m_XAxisUnit},
279 336 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
280 337 m_ValuesUnit{other.m_ValuesUnit}
281 338 {
282 339 // Since a series is ordered from its construction and is always ordered, it is not
283 340 // necessary to call the sort method here ('other' is sorted)
284 341 }
285 342
286 343 /// Assignment operator
287 344 template <int D>
288 345 DataSeries &operator=(DataSeries<D> other)
289 346 {
290 347 std::swap(m_XAxisData, other.m_XAxisData);
291 348 std::swap(m_XAxisUnit, other.m_XAxisUnit);
292 349 std::swap(m_ValuesData, other.m_ValuesData);
293 350 std::swap(m_ValuesUnit, other.m_ValuesUnit);
294 351
295 352 return *this;
296 353 }
297 354
298 355 private:
299 356 /**
300 357 * Sorts data series on its x-axis data
301 358 */
302 359 void sort() noexcept
303 360 {
304 361 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
305 362 m_XAxisData = m_XAxisData->sort(permutation);
306 363 m_ValuesData = m_ValuesData->sort(permutation);
307 364 }
308 365
309 366 std::shared_ptr<ArrayData<1> > m_XAxisData;
310 367 Unit m_XAxisUnit;
311 368 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
312 369 Unit m_ValuesUnit;
313 370
314 371 QReadWriteLock m_Lock;
315 372 };
316 373
317 374 #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,104 +1,109
1 1 #ifndef SCIQLOP_IDATASERIES_H
2 2 #define SCIQLOP_IDATASERIES_H
3 3
4 4 #include <Common/MetaTypes.h>
5 5 #include <Data/DataSeriesIterator.h>
6 6 #include <Data/SqpRange.h>
7 7
8 8 #include <memory>
9 9
10 10 #include <QString>
11 11
12 12 template <int Dim>
13 13 class ArrayData;
14 14
15 15 struct Unit {
16 16 explicit Unit(const QString &name = {}, bool timeUnit = false)
17 17 : m_Name{name}, m_TimeUnit{timeUnit}
18 18 {
19 19 }
20 20
21 21 inline bool operator==(const Unit &other) const
22 22 {
23 23 return std::tie(m_Name, m_TimeUnit) == std::tie(other.m_Name, other.m_TimeUnit);
24 24 }
25 25 inline bool operator!=(const Unit &other) const { return !(*this == other); }
26 26
27 27 QString m_Name; ///< Unit name
28 28 bool m_TimeUnit; ///< The unit is a unit of time (UTC)
29 29 };
30 30
31 31 /**
32 32 * @brief The IDataSeries aims to declare a data series.
33 33 *
34 34 * A data series is an entity that contains at least :
35 35 * - one dataset representing the x-axis
36 36 * - one dataset representing the values
37 37 *
38 38 * Each dataset is represented by an ArrayData, and is associated with a unit.
39 39 *
40 40 * An ArrayData can be unidimensional or two-dimensional, depending on the implementation of the
41 41 * IDataSeries. The x-axis dataset is always unidimensional.
42 42 *
43 43 * @sa ArrayData
44 44 */
45 45 class IDataSeries {
46 46 public:
47 47 virtual ~IDataSeries() noexcept = default;
48 48
49 49 /// Returns the x-axis dataset
50 50 virtual std::shared_ptr<ArrayData<1> > xAxisData() = 0;
51 51
52 52 /// Returns the x-axis dataset (as const)
53 53 virtual const std::shared_ptr<ArrayData<1> > xAxisData() const = 0;
54 54
55 55 virtual Unit xAxisUnit() const = 0;
56 56
57 57 virtual Unit valuesUnit() const = 0;
58 58
59 59 virtual void merge(IDataSeries *dataSeries) = 0;
60 /// Removes from data series all entries whose value on the x-axis is not between min and max
61 virtual void purge(double min, double max) = 0;
62
60 63 /// @todo Review the name and signature of this method
61 64 virtual std::shared_ptr<IDataSeries> subDataSeries(const SqpRange &range) = 0;
62 65
63 66 virtual std::unique_ptr<IDataSeries> clone() const = 0;
64 67 virtual SqpRange range() const = 0;
65 68
66 69 // ///////// //
67 70 // Iterators //
68 71 // ///////// //
69 72
70 73 virtual DataSeriesIterator cbegin() const = 0;
71 74 virtual DataSeriesIterator cend() const = 0;
75 virtual DataSeriesIterator begin() = 0;
76 virtual DataSeriesIterator end() = 0;
72 77
73 78 /// @return the iterator to the first entry of the data series whose x-axis data is greater than
74 79 /// or equal to the value passed in parameter, or the end iterator if there is no matching value
75 80 virtual DataSeriesIterator minXAxisData(double minXAxisData) const = 0;
76 81
77 82 /// @return the iterator to the last entry of the data series whose x-axis data is less than or
78 83 /// equal to the value passed in parameter, or the end iterator if there is no matching value
79 84 virtual DataSeriesIterator maxXAxisData(double maxXAxisData) const = 0;
80 85
81 86 /// @return the iterators pointing to the range of data whose x-axis values are between min and
82 87 /// max passed in parameters
83 88 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
84 89 xAxisRange(double minXAxisData, double maxXAxisData) const = 0;
85 90
86 91 /// @return two iterators pointing to the data that have respectively the min and the max value
87 92 /// data of a data series' range. The search is performed for a given x-axis range.
88 93 /// @sa xAxisRange()
89 94 virtual std::pair<DataSeriesIterator, DataSeriesIterator>
90 95 valuesBounds(double minXAxisData, double maxXAxisData) const = 0;
91 96
92 97 // /////// //
93 98 // Mutexes //
94 99 // /////// //
95 100
96 101 virtual void lockRead() = 0;
97 102 virtual void lockWrite() = 0;
98 103 virtual void unlock() = 0;
99 104 };
100 105
101 106 // Required for using shared_ptr in signals/slots
102 107 SCIQLOP_REGISTER_META_TYPE(IDATASERIES_PTR_REGISTRY, std::shared_ptr<IDataSeries>)
103 108
104 109 #endif // SCIQLOP_IDATASERIES_H
@@ -1,54 +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 pointer operator->() const { return &m_CurrentValue; }
42 reference operator*() const { return m_CurrentValue; }
39 const T *operator->() const { return &m_CurrentValue; }
40 const T &operator*() const { return m_CurrentValue; }
41 T *operator->() { return &m_CurrentValue; }
42 T &operator*() { return m_CurrentValue; }
43 43
44 44 bool operator==(const SqpIterator &other) const
45 45 {
46 46 return m_CurrentValue.equals(other.m_CurrentValue);
47 47 }
48 48 bool operator!=(const SqpIterator &other) const { return !(*this == other); }
49 49
50 50 private:
51 51 T m_CurrentValue;
52 52 };
53 53
54 54 #endif // SCIQLOP_SQPITERATOR_H
@@ -1,75 +1,74
1 1 #ifndef SCIQLOP_VARIABLE_H
2 2 #define SCIQLOP_VARIABLE_H
3 3
4 4 #include "CoreGlobal.h"
5 5
6 6 #include <Data/DataSeriesIterator.h>
7 7 #include <Data/SqpRange.h>
8 8
9 9 #include <QLoggingCategory>
10 10 #include <QObject>
11 11
12 12 #include <Common/MetaTypes.h>
13 13 #include <Common/spimpl.h>
14 14
15 15 Q_DECLARE_LOGGING_CATEGORY(LOG_Variable)
16 16
17 17 class IDataSeries;
18 18 class QString;
19 19
20 20 /**
21 21 * @brief The Variable class represents a variable in SciQlop.
22 22 */
23 23 class SCIQLOP_CORE_EXPORT Variable : public QObject {
24 24
25 25 Q_OBJECT
26 26
27 27 public:
28 28 explicit Variable(const QString &name, const SqpRange &dateTime,
29 29 const QVariantHash &metadata = {});
30 30
31 31 QString name() const noexcept;
32 32 SqpRange range() const noexcept;
33 33 void setRange(const SqpRange &range) noexcept;
34 34 SqpRange cacheRange() const noexcept;
35 35 void setCacheRange(const SqpRange &cacheRange) noexcept;
36 36
37 37 /// Returns the real range of the variable, i.e. the min and max x-axis values of the data
38 38 /// series between the range of the variable. The real range is updated each time the variable
39 39 /// range or the data series changed
40 40 /// @return the real range, invalid range if the data series is null or empty
41 41 /// @sa setDataSeries()
42 42 /// @sa setRange()
43 43 SqpRange realRange() const noexcept;
44 44
45 45 /// @return the data of the variable, nullptr if there is no data
46 46 std::shared_ptr<IDataSeries> dataSeries() const noexcept;
47 47
48 48 QVariantHash metadata() const noexcept;
49 49
50 50 bool contains(const SqpRange &range) const noexcept;
51 51 bool intersect(const SqpRange &range) const noexcept;
52 52 bool isInside(const SqpRange &range) const noexcept;
53 53
54 54 bool cacheContains(const SqpRange &range) const noexcept;
55 55 bool cacheIntersect(const SqpRange &range) const noexcept;
56 56 bool cacheIsInside(const SqpRange &range) const noexcept;
57 57
58 58 QVector<SqpRange> provideNotInCacheRangeList(const SqpRange &range) const noexcept;
59 59 QVector<SqpRange> provideInCacheRangeList(const SqpRange &range) const noexcept;
60 void setDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept;
61 60 void mergeDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept;
62 61
63 62 signals:
64 63 void updated();
65 64
66 65 private:
67 66 class VariablePrivate;
68 67 spimpl::unique_impl_ptr<VariablePrivate> impl;
69 68 };
70 69
71 70 // Required for using shared_ptr in signals/slots
72 71 SCIQLOP_REGISTER_META_TYPE(VARIABLE_PTR_REGISTRY, std::shared_ptr<Variable>)
73 72 SCIQLOP_REGISTER_META_TYPE(VARIABLE_PTR_VECTOR_REGISTRY, QVector<std::shared_ptr<Variable> >)
74 73
75 74 #endif // SCIQLOP_VARIABLE_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 }
@@ -1,279 +1,270
1 1 #include "Variable/Variable.h"
2 2
3 3 #include <Data/IDataSeries.h>
4 4 #include <Data/SqpRange.h>
5 5
6 6 #include <QMutex>
7 7 #include <QReadWriteLock>
8 8 #include <QThread>
9 9
10 10 Q_LOGGING_CATEGORY(LOG_Variable, "Variable")
11 11
12 12 struct Variable::VariablePrivate {
13 13 explicit VariablePrivate(const QString &name, const SqpRange &dateTime,
14 14 const QVariantHash &metadata)
15 15 : m_Name{name},
16 16 m_Range{dateTime},
17 17 m_Metadata{metadata},
18 18 m_DataSeries{nullptr},
19 19 m_RealRange{INVALID_RANGE}
20 20 {
21 21 }
22 22
23 23 void lockRead() { m_Lock.lockForRead(); }
24 24 void lockWrite() { m_Lock.lockForWrite(); }
25 25 void unlock() { m_Lock.unlock(); }
26 26
27 void purgeDataSeries()
28 {
29 if (m_DataSeries) {
30 m_DataSeries->purge(m_CacheRange.m_TStart, m_CacheRange.m_TEnd);
31 }
32 updateRealRange();
33 }
34
27 35 /// Updates real range according to current variable range and data series
28 36 void updateRealRange()
29 37 {
30 38 if (m_DataSeries) {
31 39 m_DataSeries->lockRead();
32 40 auto end = m_DataSeries->cend();
33 41 auto minXAxisIt = m_DataSeries->minXAxisData(m_Range.m_TStart);
34 42 auto maxXAxisIt = m_DataSeries->maxXAxisData(m_Range.m_TEnd);
35 43
36 44 m_RealRange = (minXAxisIt != end && maxXAxisIt != end)
37 45 ? SqpRange{minXAxisIt->x(), maxXAxisIt->x()}
38 46 : INVALID_RANGE;
39 47 m_DataSeries->unlock();
40 48 }
41 49 else {
42 50 m_RealRange = INVALID_RANGE;
43 51 }
44 52 }
45 53
46 54 QString m_Name;
47 55
48 56 SqpRange m_Range;
49 57 SqpRange m_CacheRange;
50 58 QVariantHash m_Metadata;
51 59 std::shared_ptr<IDataSeries> m_DataSeries;
52 60 SqpRange m_RealRange;
53 61
54 62 QReadWriteLock m_Lock;
55 63 };
56 64
57 65 Variable::Variable(const QString &name, const SqpRange &dateTime, const QVariantHash &metadata)
58 66 : impl{spimpl::make_unique_impl<VariablePrivate>(name, dateTime, metadata)}
59 67 {
60 68 }
61 69
62 70 QString Variable::name() const noexcept
63 71 {
64 72 impl->lockRead();
65 73 auto name = impl->m_Name;
66 74 impl->unlock();
67 75 return name;
68 76 }
69 77
70 78 SqpRange Variable::range() const noexcept
71 79 {
72 80 impl->lockRead();
73 81 auto range = impl->m_Range;
74 82 impl->unlock();
75 83 return range;
76 84 }
77 85
78 86 void Variable::setRange(const SqpRange &range) noexcept
79 87 {
80 88 impl->lockWrite();
81 89 impl->m_Range = range;
82 90 impl->updateRealRange();
83 91 impl->unlock();
84 92 }
85 93
86 94 SqpRange Variable::cacheRange() const noexcept
87 95 {
88 96 impl->lockRead();
89 97 auto cacheRange = impl->m_CacheRange;
90 98 impl->unlock();
91 99 return cacheRange;
92 100 }
93 101
94 102 void Variable::setCacheRange(const SqpRange &cacheRange) noexcept
95 103 {
96 104 impl->lockWrite();
97 impl->m_CacheRange = cacheRange;
105 if (cacheRange != impl->m_CacheRange) {
106 impl->m_CacheRange = cacheRange;
107 impl->purgeDataSeries();
108 }
98 109 impl->unlock();
99 110 }
100 111
101 112 SqpRange Variable::realRange() const noexcept
102 113 {
103 114 return impl->m_RealRange;
104 115 }
105 116
106 void Variable::setDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept
107 {
108 qCDebug(LOG_Variable()) << "TORM Variable::setDataSeries"
109 << QThread::currentThread()->objectName();
110 if (!dataSeries) {
111 /// @todo ALX : log
112 return;
113 }
114 impl->lockWrite();
115 impl->m_DataSeries = dataSeries->clone();
116 impl->updateRealRange();
117 impl->unlock();
118 }
119
120 117 void Variable::mergeDataSeries(std::shared_ptr<IDataSeries> dataSeries) noexcept
121 118 {
122 119 qCDebug(LOG_Variable()) << "TORM Variable::mergeDataSeries"
123 120 << QThread::currentThread()->objectName();
124 121 if (!dataSeries) {
125 122 /// @todo ALX : log
126 123 return;
127 124 }
128 125
129 126 // Add or merge the data
130 // Inits the data series of the variable
131 127 impl->lockWrite();
132 128 if (!impl->m_DataSeries) {
133 129 impl->m_DataSeries = dataSeries->clone();
134 130 }
135 131 else {
136 132 impl->m_DataSeries->merge(dataSeries.get());
137 133 }
134 impl->purgeDataSeries();
138 135 impl->unlock();
139
140 // sub the data
141 auto subData = this->dataSeries()->subDataSeries(this->cacheRange());
142 qCDebug(LOG_Variable()) << "TORM: Variable::mergeDataSeries sub" << subData->range();
143 this->setDataSeries(subData);
144 qCDebug(LOG_Variable()) << "TORM: Variable::mergeDataSeries set" << this->dataSeries()->range();
145 136 }
146 137
147 138 std::shared_ptr<IDataSeries> Variable::dataSeries() const noexcept
148 139 {
149 140 impl->lockRead();
150 141 auto dataSeries = impl->m_DataSeries;
151 142 impl->unlock();
152 143
153 144 return dataSeries;
154 145 }
155 146
156 147 QVariantHash Variable::metadata() const noexcept
157 148 {
158 149 impl->lockRead();
159 150 auto metadata = impl->m_Metadata;
160 151 impl->unlock();
161 152 return metadata;
162 153 }
163 154
164 155 bool Variable::contains(const SqpRange &range) const noexcept
165 156 {
166 157 impl->lockRead();
167 158 auto res = impl->m_Range.contains(range);
168 159 impl->unlock();
169 160 return res;
170 161 }
171 162
172 163 bool Variable::intersect(const SqpRange &range) const noexcept
173 164 {
174 165
175 166 impl->lockRead();
176 167 auto res = impl->m_Range.intersect(range);
177 168 impl->unlock();
178 169 return res;
179 170 }
180 171
181 172 bool Variable::isInside(const SqpRange &range) const noexcept
182 173 {
183 174 impl->lockRead();
184 175 auto res = range.contains(SqpRange{impl->m_Range.m_TStart, impl->m_Range.m_TEnd});
185 176 impl->unlock();
186 177 return res;
187 178 }
188 179
189 180 bool Variable::cacheContains(const SqpRange &range) const noexcept
190 181 {
191 182 impl->lockRead();
192 183 auto res = impl->m_CacheRange.contains(range);
193 184 impl->unlock();
194 185 return res;
195 186 }
196 187
197 188 bool Variable::cacheIntersect(const SqpRange &range) const noexcept
198 189 {
199 190 impl->lockRead();
200 191 auto res = impl->m_CacheRange.intersect(range);
201 192 impl->unlock();
202 193 return res;
203 194 }
204 195
205 196 bool Variable::cacheIsInside(const SqpRange &range) const noexcept
206 197 {
207 198 impl->lockRead();
208 199 auto res = range.contains(SqpRange{impl->m_CacheRange.m_TStart, impl->m_CacheRange.m_TEnd});
209 200 impl->unlock();
210 201 return res;
211 202 }
212 203
213 204
214 205 QVector<SqpRange> Variable::provideNotInCacheRangeList(const SqpRange &range) const noexcept
215 206 {
216 207 // This code assume that cach in contigue. Can return 0, 1 or 2 SqpRange
217 208
218 209 auto notInCache = QVector<SqpRange>{};
219 210
220 211 if (!this->cacheContains(range)) {
221 212 if (range.m_TEnd <= impl->m_CacheRange.m_TStart
222 213 || range.m_TStart >= impl->m_CacheRange.m_TEnd) {
223 214 notInCache << range;
224 215 }
225 216 else if (range.m_TStart < impl->m_CacheRange.m_TStart
226 217 && range.m_TEnd <= impl->m_CacheRange.m_TEnd) {
227 218 notInCache << SqpRange{range.m_TStart, impl->m_CacheRange.m_TStart};
228 219 }
229 220 else if (range.m_TStart < impl->m_CacheRange.m_TStart
230 221 && range.m_TEnd > impl->m_CacheRange.m_TEnd) {
231 222 notInCache << SqpRange{range.m_TStart, impl->m_CacheRange.m_TStart}
232 223 << SqpRange{impl->m_CacheRange.m_TEnd, range.m_TEnd};
233 224 }
234 225 else if (range.m_TStart < impl->m_CacheRange.m_TEnd) {
235 226 notInCache << SqpRange{impl->m_CacheRange.m_TEnd, range.m_TEnd};
236 227 }
237 228 else {
238 229 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
239 230 << QThread::currentThread();
240 231 }
241 232 }
242 233
243 234 return notInCache;
244 235 }
245 236
246 237 QVector<SqpRange> Variable::provideInCacheRangeList(const SqpRange &range) const noexcept
247 238 {
248 239 // This code assume that cach in contigue. Can return 0 or 1 SqpRange
249 240
250 241 auto inCache = QVector<SqpRange>{};
251 242
252 243
253 244 if (this->intersect(range)) {
254 245 if (range.m_TStart <= impl->m_CacheRange.m_TStart
255 246 && range.m_TEnd >= impl->m_CacheRange.m_TStart
256 247 && range.m_TEnd < impl->m_CacheRange.m_TEnd) {
257 248 inCache << SqpRange{impl->m_CacheRange.m_TStart, range.m_TEnd};
258 249 }
259 250
260 251 else if (range.m_TStart >= impl->m_CacheRange.m_TStart
261 252 && range.m_TEnd <= impl->m_CacheRange.m_TEnd) {
262 253 inCache << range;
263 254 }
264 255 else if (range.m_TStart > impl->m_CacheRange.m_TStart
265 256 && range.m_TEnd > impl->m_CacheRange.m_TEnd) {
266 257 inCache << SqpRange{range.m_TStart, impl->m_CacheRange.m_TEnd};
267 258 }
268 259 else if (range.m_TStart <= impl->m_CacheRange.m_TStart
269 260 && range.m_TEnd >= impl->m_CacheRange.m_TEnd) {
270 261 inCache << impl->m_CacheRange;
271 262 }
272 263 else {
273 264 qCCritical(LOG_Variable()) << tr("Detection of unknown case.")
274 265 << QThread::currentThread();
275 266 }
276 267 }
277 268
278 269 return inCache;
279 270 }
@@ -1,519 +1,638
1 1 #include "Data/DataSeries.h"
2 2 #include "Data/ScalarSeries.h"
3 3 #include "Data/VectorSeries.h"
4 4
5 5 #include <cmath>
6 6
7 7 #include <QObject>
8 8 #include <QtTest>
9 9
10 10 Q_DECLARE_METATYPE(std::shared_ptr<ScalarSeries>)
11 11 Q_DECLARE_METATYPE(std::shared_ptr<VectorSeries>)
12 12
13 13 namespace {
14 14
15 15 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const QVector<double> &xData,
16 16 const QVector<double> &valuesData)
17 17 {
18 18 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
19 19 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
20 20 QVERIFY(std::equal(
21 21 first, last, valuesData.cbegin(), valuesData.cend(),
22 22 [](const auto &it, const auto &expectedVal) { return it.value() == expectedVal; }));
23 23 }
24 24
25 void validateRange(DataSeriesIterator first, DataSeriesIterator last, const QVector<double> &xData,
26 const QVector<QVector<double> > &valuesData)
27 {
28 QVERIFY(std::equal(first, last, xData.cbegin(), xData.cend(),
29 [](const auto &it, const auto &expectedX) { return it.x() == expectedX; }));
30 for (auto i = 0; i < valuesData.size(); ++i) {
31 auto componentData = valuesData.at(i);
32
33 QVERIFY(std::equal(
34 first, last, componentData.cbegin(), componentData.cend(),
35 [i](const auto &it, const auto &expectedVal) { return it.value(i) == expectedVal; }));
36 }
37 }
38
25 39 } // namespace
26 40
27 41 class TestDataSeries : public QObject {
28 42 Q_OBJECT
29 43 private:
30 44 template <typename T>
31 45 void testValuesBoundsStructure()
32 46 {
33 47 // ////////////// //
34 48 // Test structure //
35 49 // ////////////// //
36 50
37 51 // Data series to get values bounds
38 52 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
39 53
40 54 // x-axis range
41 55 QTest::addColumn<double>("minXAxis");
42 56 QTest::addColumn<double>("maxXAxis");
43 57
44 58 // Expected results
45 59 QTest::addColumn<bool>(
46 60 "expectedOK"); // Test is expected to be ok (i.e. method doesn't return end iterators)
47 61 QTest::addColumn<double>("expectedMinValue");
48 62 QTest::addColumn<double>("expectedMaxValue");
49 63 }
50 64
51 65 template <typename T>
52 66 void testValuesBounds()
53 67 {
54 68 QFETCH(std::shared_ptr<T>, dataSeries);
55 69 QFETCH(double, minXAxis);
56 70 QFETCH(double, maxXAxis);
57 71
58 72 QFETCH(bool, expectedOK);
59 73 QFETCH(double, expectedMinValue);
60 74 QFETCH(double, expectedMaxValue);
61 75
62 76 auto minMaxIts = dataSeries->valuesBounds(minXAxis, maxXAxis);
63 77 auto end = dataSeries->cend();
64 78
65 79 // Checks iterators with expected result
66 80 QCOMPARE(expectedOK, minMaxIts.first != end && minMaxIts.second != end);
67 81
68 82 if (expectedOK) {
69 83 auto compare = [](const auto &v1, const auto &v2) {
70 84 return (std::isnan(v1) && std::isnan(v2)) || v1 == v2;
71 85 };
72 86
73 87 QVERIFY(compare(expectedMinValue, minMaxIts.first->minValue()));
74 88 QVERIFY(compare(expectedMaxValue, minMaxIts.second->maxValue()));
75 89 }
76 90 }
77 91
92 template <typename T>
93 void testPurgeStructure()
94 {
95 // ////////////// //
96 // Test structure //
97 // ////////////// //
98
99 // Data series to purge
100 QTest::addColumn<std::shared_ptr<T> >("dataSeries");
101 QTest::addColumn<double>("min");
102 QTest::addColumn<double>("max");
103
104 // Expected values after purge
105 QTest::addColumn<QVector<double> >("expectedXAxisData");
106 QTest::addColumn<QVector<QVector<double> > >("expectedValuesData");
107 }
108
109 template <typename T>
110 void testPurge()
111 {
112 QFETCH(std::shared_ptr<T>, dataSeries);
113 QFETCH(double, min);
114 QFETCH(double, max);
115
116 dataSeries->purge(min, max);
117
118 // Validates results
119 QFETCH(QVector<double>, expectedXAxisData);
120 QFETCH(QVector<QVector<double> >, expectedValuesData);
121
122 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData,
123 expectedValuesData);
124 }
125
78 126 private slots:
127
79 128 /// Input test data
80 129 /// @sa testCtor()
81 130 void testCtor_data();
82 131
83 132 /// Tests construction of a data series
84 133 void testCtor();
85 134
86 135 /// Input test data
87 136 /// @sa testMerge()
88 137 void testMerge_data();
89 138
90 139 /// Tests merge of two data series
91 140 void testMerge();
92 141
93 142 /// Input test data
143 /// @sa testPurgeScalar()
144 void testPurgeScalar_data();
145
146 /// Tests purge of a scalar series
147 void testPurgeScalar();
148
149 /// Input test data
150 /// @sa testPurgeVector()
151 void testPurgeVector_data();
152
153 /// Tests purge of a vector series
154 void testPurgeVector();
155
156 /// Input test data
94 157 /// @sa testMinXAxisData()
95 158 void testMinXAxisData_data();
96 159
97 160 /// Tests get min x-axis data of a data series
98 161 void testMinXAxisData();
99 162
100 163 /// Input test data
101 164 /// @sa testMaxXAxisData()
102 165 void testMaxXAxisData_data();
103 166
104 167 /// Tests get max x-axis data of a data series
105 168 void testMaxXAxisData();
106 169
107 170 /// Input test data
108 171 /// @sa testXAxisRange()
109 172 void testXAxisRange_data();
110 173
111 174 /// Tests get x-axis range of a data series
112 175 void testXAxisRange();
113 176
114 177 /// Input test data
115 178 /// @sa testValuesBoundsScalar()
116 179 void testValuesBoundsScalar_data();
117 180
118 181 /// Tests get values bounds of a scalar series
119 182 void testValuesBoundsScalar();
120 183
121 184 /// Input test data
122 185 /// @sa testValuesBoundsVector()
123 186 void testValuesBoundsVector_data();
124 187
125 188 /// Tests get values bounds of a vector series
126 189 void testValuesBoundsVector();
127 190 };
128 191
129 192 void TestDataSeries::testCtor_data()
130 193 {
131 194 // ////////////// //
132 195 // Test structure //
133 196 // ////////////// //
134 197
135 198 // x-axis data
136 199 QTest::addColumn<QVector<double> >("xAxisData");
137 200 // values data
138 201 QTest::addColumn<QVector<double> >("valuesData");
139 202
140 203 // expected x-axis data
141 204 QTest::addColumn<QVector<double> >("expectedXAxisData");
142 205 // expected values data
143 206 QTest::addColumn<QVector<double> >("expectedValuesData");
144 207
145 208 // ////////// //
146 209 // Test cases //
147 210 // ////////// //
148 211
149 212 QTest::newRow("invalidData (different sizes of vectors)")
150 213 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 200., 300.}
151 214 << QVector<double>{} << QVector<double>{};
152 215
153 216 QTest::newRow("sortedData") << QVector<double>{1., 2., 3., 4., 5.}
154 217 << QVector<double>{100., 200., 300., 400., 500.}
155 218 << QVector<double>{1., 2., 3., 4., 5.}
156 219 << QVector<double>{100., 200., 300., 400., 500.};
157 220
158 221 QTest::newRow("unsortedData") << QVector<double>{5., 4., 3., 2., 1.}
159 222 << QVector<double>{100., 200., 300., 400., 500.}
160 223 << QVector<double>{1., 2., 3., 4., 5.}
161 224 << QVector<double>{500., 400., 300., 200., 100.};
162 225
163 226 QTest::newRow("unsortedData2")
164 227 << QVector<double>{1., 4., 3., 5., 2.} << QVector<double>{100., 200., 300., 400., 500.}
165 228 << QVector<double>{1., 2., 3., 4., 5.} << QVector<double>{100., 500., 300., 200., 400.};
166 229 }
167 230
168 231 void TestDataSeries::testCtor()
169 232 {
170 233 // Creates series
171 234 QFETCH(QVector<double>, xAxisData);
172 235 QFETCH(QVector<double>, valuesData);
173 236
174 237 auto series = std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData),
175 238 Unit{}, Unit{});
176 239
177 240 // Validates results : we check that the data series is sorted on its x-axis data
178 241 QFETCH(QVector<double>, expectedXAxisData);
179 242 QFETCH(QVector<double>, expectedValuesData);
180 243
181 244 validateRange(series->cbegin(), series->cend(), expectedXAxisData, expectedValuesData);
182 245 }
183 246
184 247 namespace {
185 248
186 249 std::shared_ptr<ScalarSeries> createScalarSeries(QVector<double> xAxisData,
187 250 QVector<double> valuesData)
188 251 {
189 252 return std::make_shared<ScalarSeries>(std::move(xAxisData), std::move(valuesData), Unit{},
190 253 Unit{});
191 254 }
192 255
193 256 std::shared_ptr<VectorSeries> createVectorSeries(QVector<double> xAxisData,
194 257 QVector<double> xValuesData,
195 258 QVector<double> yValuesData,
196 259 QVector<double> zValuesData)
197 260 {
198 261 return std::make_shared<VectorSeries>(std::move(xAxisData), std::move(xValuesData),
199 262 std::move(yValuesData), std::move(zValuesData), Unit{},
200 263 Unit{});
201 264 }
202 265
203 266 } // namespace
204 267
205 268 void TestDataSeries::testMerge_data()
206 269 {
207 270 // ////////////// //
208 271 // Test structure //
209 272 // ////////////// //
210 273
211 274 // Data series to merge
212 275 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
213 276 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries2");
214 277
215 278 // Expected values in the first data series after merge
216 279 QTest::addColumn<QVector<double> >("expectedXAxisData");
217 280 QTest::addColumn<QVector<double> >("expectedValuesData");
218 281
219 282 // ////////// //
220 283 // Test cases //
221 284 // ////////// //
222 285
223 286 QTest::newRow("sortedMerge")
224 287 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
225 288 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
226 289 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
227 290 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
228 291
229 292 QTest::newRow("unsortedMerge")
230 293 << createScalarSeries({6., 7., 8., 9., 10.}, {600., 700., 800., 900., 1000.})
231 294 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.})
232 295 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
233 296 << QVector<double>{100., 200., 300., 400., 500., 600., 700., 800., 900., 1000.};
234 297
235 298 QTest::newRow("unsortedMerge2")
236 299 << createScalarSeries({1., 2., 8., 9., 10}, {100., 200., 300., 400., 500.})
237 300 << createScalarSeries({3., 4., 5., 6., 7.}, {600., 700., 800., 900., 1000.})
238 301 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
239 302 << QVector<double>{100., 200., 600., 700., 800., 900., 1000., 300., 400., 500.};
240 303
241 304 QTest::newRow("unsortedMerge3")
242 305 << createScalarSeries({3., 5., 8., 7., 2}, {100., 200., 300., 400., 500.})
243 306 << createScalarSeries({6., 4., 9., 10., 1.}, {600., 700., 800., 900., 1000.})
244 307 << QVector<double>{1., 2., 3., 4., 5., 6., 7., 8., 9., 10.}
245 308 << QVector<double>{1000., 500., 100., 700., 200., 600., 400., 300., 800., 900.};
246 309 }
247 310
248 311 void TestDataSeries::testMerge()
249 312 {
250 313 // Merges series
251 314 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
252 315 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries2);
253 316
254 317 dataSeries->merge(dataSeries2.get());
255 318
256 319 // Validates results : we check that the merge is valid and the data series is sorted on its
257 320 // x-axis data
258 321 QFETCH(QVector<double>, expectedXAxisData);
259 322 QFETCH(QVector<double>, expectedValuesData);
260 323
261 324 validateRange(dataSeries->cbegin(), dataSeries->cend(), expectedXAxisData, expectedValuesData);
262 325 }
263 326
327 void TestDataSeries::testPurgeScalar_data()
328 {
329 testPurgeStructure<ScalarSeries>();
330
331 // ////////// //
332 // Test cases //
333 // ////////// //
334
335 QTest::newRow("purgeScalar") << createScalarSeries({1., 2., 3., 4., 5.},
336 {100., 200., 300., 400., 500.})
337 << 2. << 4. << QVector<double>{2., 3., 4.}
338 << QVector<QVector<double> >{{200., 300., 400.}};
339 QTest::newRow("purgeScalar2") << createScalarSeries({1., 2., 3., 4., 5.},
340 {100., 200., 300., 400., 500.})
341 << 0. << 2.5 << QVector<double>{1., 2.}
342 << QVector<QVector<double> >{{100., 200.}};
343 QTest::newRow("purgeScalar3") << createScalarSeries({1., 2., 3., 4., 5.},
344 {100., 200., 300., 400., 500.})
345 << 3.5 << 7. << QVector<double>{4., 5.}
346 << QVector<QVector<double> >{{400., 500.}};
347 QTest::newRow("purgeScalar4") << createScalarSeries({1., 2., 3., 4., 5.},
348 {100., 200., 300., 400., 500.})
349 << 0. << 7. << QVector<double>{1., 2., 3., 4., 5.}
350 << QVector<QVector<double> >{{100., 200., 300., 400., 500.}};
351 QTest::newRow("purgeScalar5") << createScalarSeries({1., 2., 3., 4., 5.},
352 {100., 200., 300., 400., 500.})
353 << 5.5 << 7. << QVector<double>{}
354 << QVector<QVector<double> >{{}};
355 }
356
357 void TestDataSeries::testPurgeScalar()
358 {
359 testPurge<ScalarSeries>();
360 }
361
362 void TestDataSeries::testPurgeVector_data()
363 {
364 testPurgeStructure<VectorSeries>();
365
366 // ////////// //
367 // Test cases //
368 // ////////// //
369
370 QTest::newRow("purgeVector") << createVectorSeries({1., 2., 3., 4., 5.}, {6., 7., 8., 9., 10.},
371 {11., 12., 13., 14., 15.},
372 {16., 17., 18., 19., 20.})
373 << 2. << 4. << QVector<double>{2., 3., 4.}
374 << QVector<QVector<double> >{
375 {7., 8., 9.}, {12., 13., 14.}, {17., 18., 19.}};
376 }
377
378 void TestDataSeries::testPurgeVector()
379 {
380 testPurge<VectorSeries>();
381 }
382
264 383 void TestDataSeries::testMinXAxisData_data()
265 384 {
266 385 // ////////////// //
267 386 // Test structure //
268 387 // ////////////// //
269 388
270 389 // Data series to get min data
271 390 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
272 391
273 392 // Min data
274 393 QTest::addColumn<double>("min");
275 394
276 395 // Expected results
277 396 QTest::addColumn<bool>(
278 397 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
279 398 QTest::addColumn<double>(
280 399 "expectedMin"); // Expected value when method doesn't return end iterator
281 400
282 401 // ////////// //
283 402 // Test cases //
284 403 // ////////// //
285 404
286 405 QTest::newRow("minData1") << createScalarSeries({1., 2., 3., 4., 5.},
287 406 {100., 200., 300., 400., 500.})
288 407 << 0. << true << 1.;
289 408 QTest::newRow("minData2") << createScalarSeries({1., 2., 3., 4., 5.},
290 409 {100., 200., 300., 400., 500.})
291 410 << 1. << true << 1.;
292 411 QTest::newRow("minData3") << createScalarSeries({1., 2., 3., 4., 5.},
293 412 {100., 200., 300., 400., 500.})
294 413 << 1.1 << true << 2.;
295 414 QTest::newRow("minData4") << createScalarSeries({1., 2., 3., 4., 5.},
296 415 {100., 200., 300., 400., 500.})
297 416 << 5. << true << 5.;
298 417 QTest::newRow("minData5") << createScalarSeries({1., 2., 3., 4., 5.},
299 418 {100., 200., 300., 400., 500.})
300 419 << 5.1 << false << std::numeric_limits<double>::quiet_NaN();
301 420 QTest::newRow("minData6") << createScalarSeries({}, {}) << 1.1 << false
302 421 << std::numeric_limits<double>::quiet_NaN();
303 422 }
304 423
305 424 void TestDataSeries::testMinXAxisData()
306 425 {
307 426 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
308 427 QFETCH(double, min);
309 428
310 429 QFETCH(bool, expectedOK);
311 430 QFETCH(double, expectedMin);
312 431
313 432 auto it = dataSeries->minXAxisData(min);
314 433
315 434 QCOMPARE(expectedOK, it != dataSeries->cend());
316 435
317 436 // If the method doesn't return a end iterator, checks with expected value
318 437 if (expectedOK) {
319 438 QCOMPARE(expectedMin, it->x());
320 439 }
321 440 }
322 441
323 442 void TestDataSeries::testMaxXAxisData_data()
324 443 {
325 444 // ////////////// //
326 445 // Test structure //
327 446 // ////////////// //
328 447
329 448 // Data series to get max data
330 449 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
331 450
332 451 // Max data
333 452 QTest::addColumn<double>("max");
334 453
335 454 // Expected results
336 455 QTest::addColumn<bool>(
337 456 "expectedOK"); // if true, expects to have a result (i.e. the iterator != end iterator)
338 457 QTest::addColumn<double>(
339 458 "expectedMax"); // Expected value when method doesn't return end iterator
340 459
341 460 // ////////// //
342 461 // Test cases //
343 462 // ////////// //
344 463
345 464 QTest::newRow("maxData1") << createScalarSeries({1., 2., 3., 4., 5.},
346 465 {100., 200., 300., 400., 500.})
347 466 << 6. << true << 5.;
348 467 QTest::newRow("maxData2") << createScalarSeries({1., 2., 3., 4., 5.},
349 468 {100., 200., 300., 400., 500.})
350 469 << 5. << true << 5.;
351 470 QTest::newRow("maxData3") << createScalarSeries({1., 2., 3., 4., 5.},
352 471 {100., 200., 300., 400., 500.})
353 472 << 4.9 << true << 4.;
354 473 QTest::newRow("maxData4") << createScalarSeries({1., 2., 3., 4., 5.},
355 474 {100., 200., 300., 400., 500.})
356 475 << 1.1 << true << 1.;
357 476 QTest::newRow("maxData5") << createScalarSeries({1., 2., 3., 4., 5.},
358 477 {100., 200., 300., 400., 500.})
359 478 << 1. << true << 1.;
360 479 QTest::newRow("maxData6") << createScalarSeries({}, {}) << 1.1 << false
361 480 << std::numeric_limits<double>::quiet_NaN();
362 481 }
363 482
364 483 void TestDataSeries::testMaxXAxisData()
365 484 {
366 485 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
367 486 QFETCH(double, max);
368 487
369 488 QFETCH(bool, expectedOK);
370 489 QFETCH(double, expectedMax);
371 490
372 491 auto it = dataSeries->maxXAxisData(max);
373 492
374 493 QCOMPARE(expectedOK, it != dataSeries->cend());
375 494
376 495 // If the method doesn't return a end iterator, checks with expected value
377 496 if (expectedOK) {
378 497 QCOMPARE(expectedMax, it->x());
379 498 }
380 499 }
381 500
382 501 void TestDataSeries::testXAxisRange_data()
383 502 {
384 503 // ////////////// //
385 504 // Test structure //
386 505 // ////////////// //
387 506
388 507 // Data series to get x-axis range
389 508 QTest::addColumn<std::shared_ptr<ScalarSeries> >("dataSeries");
390 509
391 510 // Min/max values
392 511 QTest::addColumn<double>("min");
393 512 QTest::addColumn<double>("max");
394 513
395 514 // Expected values
396 515 QTest::addColumn<QVector<double> >("expectedXAxisData");
397 516 QTest::addColumn<QVector<double> >("expectedValuesData");
398 517
399 518 // ////////// //
400 519 // Test cases //
401 520 // ////////// //
402 521
403 522 QTest::newRow("xAxisRange1") << createScalarSeries({1., 2., 3., 4., 5.},
404 523 {100., 200., 300., 400., 500.})
405 524 << -1. << 3.2 << QVector<double>{1., 2., 3.}
406 525 << QVector<double>{100., 200., 300.};
407 526 QTest::newRow("xAxisRange2") << createScalarSeries({1., 2., 3., 4., 5.},
408 527 {100., 200., 300., 400., 500.})
409 528 << 1. << 4. << QVector<double>{1., 2., 3., 4.}
410 529 << QVector<double>{100., 200., 300., 400.};
411 530 QTest::newRow("xAxisRange3") << createScalarSeries({1., 2., 3., 4., 5.},
412 531 {100., 200., 300., 400., 500.})
413 532 << 1. << 3.9 << QVector<double>{1., 2., 3.}
414 533 << QVector<double>{100., 200., 300.};
415 534 QTest::newRow("xAxisRange4") << createScalarSeries({1., 2., 3., 4., 5.},
416 535 {100., 200., 300., 400., 500.})
417 536 << 0. << 0.9 << QVector<double>{} << QVector<double>{};
418 537 QTest::newRow("xAxisRange5") << createScalarSeries({1., 2., 3., 4., 5.},
419 538 {100., 200., 300., 400., 500.})
420 539 << 0. << 1. << QVector<double>{1.} << QVector<double>{100.};
421 540 QTest::newRow("xAxisRange6") << createScalarSeries({1., 2., 3., 4., 5.},
422 541 {100., 200., 300., 400., 500.})
423 542 << 2.1 << 6. << QVector<double>{3., 4., 5.}
424 543 << QVector<double>{300., 400., 500.};
425 544 QTest::newRow("xAxisRange7") << createScalarSeries({1., 2., 3., 4., 5.},
426 545 {100., 200., 300., 400., 500.})
427 546 << 6. << 9. << QVector<double>{} << QVector<double>{};
428 547 QTest::newRow("xAxisRange8") << createScalarSeries({1., 2., 3., 4., 5.},
429 548 {100., 200., 300., 400., 500.})
430 549 << 5. << 9. << QVector<double>{5.} << QVector<double>{500.};
431 550 }
432 551
433 552 void TestDataSeries::testXAxisRange()
434 553 {
435 554 QFETCH(std::shared_ptr<ScalarSeries>, dataSeries);
436 555 QFETCH(double, min);
437 556 QFETCH(double, max);
438 557
439 558 QFETCH(QVector<double>, expectedXAxisData);
440 559 QFETCH(QVector<double>, expectedValuesData);
441 560
442 561 auto bounds = dataSeries->xAxisRange(min, max);
443 562 validateRange(bounds.first, bounds.second, expectedXAxisData, expectedValuesData);
444 563 }
445 564
446 565 void TestDataSeries::testValuesBoundsScalar_data()
447 566 {
448 567 testValuesBoundsStructure<ScalarSeries>();
449 568
450 569 // ////////// //
451 570 // Test cases //
452 571 // ////////// //
453 572 auto nan = std::numeric_limits<double>::quiet_NaN();
454 573
455 574 QTest::newRow("scalarBounds1")
456 575 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 6.
457 576 << true << 100. << 500.;
458 577 QTest::newRow("scalarBounds2")
459 578 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 2. << 4.
460 579 << true << 200. << 400.;
461 580 QTest::newRow("scalarBounds3")
462 581 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 0. << 0.5
463 582 << false << nan << nan;
464 583 QTest::newRow("scalarBounds4")
465 584 << createScalarSeries({1., 2., 3., 4., 5.}, {100., 200., 300., 400., 500.}) << 5.1 << 6.
466 585 << false << nan << nan;
467 586 QTest::newRow("scalarBounds5") << createScalarSeries({1.}, {100.}) << 0. << 2. << true << 100.
468 587 << 100.;
469 588 QTest::newRow("scalarBounds6") << createScalarSeries({}, {}) << 0. << 2. << false << nan << nan;
470 589
471 590 // Tests with NaN values: NaN values are not included in min/max search
472 591 QTest::newRow("scalarBounds7")
473 592 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, 200., 300., 400., nan}) << 0. << 6.
474 593 << true << 200. << 400.;
475 594 QTest::newRow("scalarBounds8")
476 595 << createScalarSeries({1., 2., 3., 4., 5.}, {nan, nan, nan, nan, nan}) << 0. << 6. << true
477 596 << std::numeric_limits<double>::quiet_NaN() << std::numeric_limits<double>::quiet_NaN();
478 597 }
479 598
480 599 void TestDataSeries::testValuesBoundsScalar()
481 600 {
482 601 testValuesBounds<ScalarSeries>();
483 602 }
484 603
485 604 void TestDataSeries::testValuesBoundsVector_data()
486 605 {
487 606 testValuesBoundsStructure<VectorSeries>();
488 607
489 608 // ////////// //
490 609 // Test cases //
491 610 // ////////// //
492 611 auto nan = std::numeric_limits<double>::quiet_NaN();
493 612
494 613 QTest::newRow("vectorBounds1")
495 614 << createVectorSeries({1., 2., 3., 4., 5.}, {10., 15., 20., 13., 12.},
496 615 {35., 24., 10., 9., 0.3}, {13., 14., 12., 9., 24.})
497 616 << 0. << 6. << true << 0.3 << 35.; // min/max in same component
498 617 QTest::newRow("vectorBounds2")
499 618 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
500 619 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
501 620 << 0. << 6. << true << 2.3 << 35.; // min/max in same entry
502 621 QTest::newRow("vectorBounds3")
503 622 << createVectorSeries({1., 2., 3., 4., 5.}, {2.3, 15., 20., 13., 12.},
504 623 {35., 24., 10., 9., 4.}, {13., 14., 12., 9., 24.})
505 624 << 2. << 3. << true << 10. << 24.;
506 625
507 626 // Tests with NaN values: NaN values are not included in min/max search
508 627 QTest::newRow("vectorBounds4")
509 628 << createVectorSeries({1., 2.}, {nan, nan}, {nan, nan}, {nan, nan}) << 0. << 6. << true
510 629 << nan << nan;
511 630 }
512 631
513 632 void TestDataSeries::testValuesBoundsVector()
514 633 {
515 634 testValuesBounds<VectorSeries>();
516 635 }
517 636
518 637 QTEST_MAIN(TestDataSeries)
519 638 #include "TestDataSeries.moc"
@@ -1,45 +1,52
1 1 # On ignore toutes les règles vera++ pour le fichier spimpl
2 2 Common/spimpl\.h:\d+:.*
3 3
4 4 # Ignore false positive relative to two class definitions in a same file
5 5 ArrayData\.h:\d+:.*IPSIS_S01.*
6 6 ArrayDataIterator\.h:\d+:.*IPSIS_S01.*
7 7 DataSourceItem\.h:\d+:.*IPSIS_S01.*
8 8 DataSeries\.h:\d+:.*IPSIS_S01.*
9 9 DataSeriesIterator\.h:\d+:.*IPSIS_S01.*
10 10 DataSeriesMergeHelper\.h:\d+:.*IPSIS_S01.*
11 11
12 12 # Ignore false positive relative to a template class
13 13 ArrayData\.h:\d+:.*IPSIS_S04_METHOD.*found: push_back
14 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (const_iterator)
14 15 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (D)
16 ArrayData\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (IC)
15 17 ArrayData\.h:\d+:.*IPSIS_S04_NAMESPACE.*found: (arraydata_detail)
16 18 ArrayData\.h:\d+:.*IPSIS_S06.*found: (D)
17 19 ArrayData\.h:\d+:.*IPSIS_S06.*found: (Dim)
20 ArrayData\.h:\d+:.*IPSIS_S06.*found: (IC)
21 ArrayData\.h:\d+:.*IPSIS_S06.*found: (IsConst)
18 22 DataSeries\.h:\d+:.*IPSIS_S04_METHOD.*found: LOG_DataSeries
19 23 DataSeries\.h:\d+:.*IPSIS_S04_METHOD.*found: push_back
20 24 DataSeries\.h:\d+:.*IPSIS_S04_VARIABLE.*
21 25 DataSeries\.h:\d+:.*IPSIS_S04_NAMESPACE.*found: (dataseries_detail)
22 26 DataSeries\.h:\d+:.*IPSIS_S05.*
23 27 DataSeries\.h:\d+:.*IPSIS_S06.*found: (value_type)
24 28 DataSeries\.h:\d+:.*IPSIS_S06.*found: (DataSeriesIteratorValue)
29 DataSeries\.h:\d+:.*IPSIS_S06.*found: (Dim)
30 DataSeries\.h:\d+:.*IPSIS_S06.*found: (IC)
31 DataSeries\.h:\d+:.*IPSIS_S06.*found: (IsConst)
25 32
26 33 # Ignore false positive relative to iterators
27 34 SqpIterator\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (forward_iterator_tag)
28 35 SqpIterator\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (T)
29 36 SqpIterator\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (ptrdiff_t)
30 37 SqpIterator\.h:\d+:.*IPSIS_S04_VARIABLE.*found: (value_type)
31 38 SqpIterator\.h:\d+:.*IPSIS_S06.*found: (iterator_category)
32 39 SqpIterator\.h:\d+:.*IPSIS_S06.*found: (forward_iterator_tag)
33 40 SqpIterator\.h:\d+:.*IPSIS_S06.*found: (value_type)
34 41 SqpIterator\.h:\d+:.*IPSIS_S06.*found: (T)
35 42 SqpIterator\.h:\d+:.*IPSIS_S06.*found: (difference_type)
36 43 SqpIterator\.h:\d+:.*IPSIS_S06.*found: (ptrdiff_t)
37 44 SqpIterator\.h:\d+:.*IPSIS_S06.*found: (pointer)
38 45 SqpIterator\.h:\d+:.*IPSIS_S06.*found: (reference)
39 46 SqpIterator\.h:\d+:.*IPSIS_S06.*found: (value_type)
40 47
41 48 # Ignore false positive relative to an alias
42 49 DataSourceItemAction\.h:\d+:.*IPSIS_S06.*found: (ExecuteFunction)
43 50
44 51 # Ignore false positive relative to unnamed namespace
45 52 VariableController\.cpp:\d+:.*IPSIS_F13.*
General Comments 0
You need to be logged in to leave comments. Login now