##// END OF EJS Templates
Improves purge method
Alexandre Leroux -
r690:03eea8848b1d
parent child
Show More
@@ -1,399 +1,410
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 int distance(const DataSeriesIteratorValue::Impl &other) const override try {
58 58 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
59 59 return m_XIt->distance(*otherImpl.m_XIt);
60 60 }
61 61 catch (const std::bad_cast &) {
62 62 return 0;
63 63 }
64 64
65 65 bool equals(const DataSeriesIteratorValue::Impl &other) const override try {
66 66 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
67 67 return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt);
68 68 }
69 69 catch (const std::bad_cast &) {
70 70 return false;
71 71 }
72 72
73 73 bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try {
74 74 const auto &otherImpl = dynamic_cast<const IteratorValue &>(other);
75 75 return m_XIt->lowerThan(*otherImpl.m_XIt);
76 76 }
77 77 catch (const std::bad_cast &) {
78 78 return false;
79 79 }
80 80
81 81 std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override
82 82 {
83 83 auto result = clone();
84 84 while (offset--) {
85 85 result->next();
86 86 }
87 87 return result;
88 88 }
89 89
90 90 void next() override
91 91 {
92 92 ++m_XIt;
93 93 ++m_ValuesIt;
94 94 }
95 95
96 96 void prev() override
97 97 {
98 98 --m_XIt;
99 99 --m_ValuesIt;
100 100 }
101 101
102 102 double x() const override { return m_XIt->at(0); }
103 103 double value() const override { return m_ValuesIt->at(0); }
104 104 double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); }
105 105 double minValue() const override { return m_ValuesIt->min(); }
106 106 double maxValue() const override { return m_ValuesIt->max(); }
107 107 QVector<double> values() const override { return m_ValuesIt->values(); }
108 108
109 109 void swap(DataSeriesIteratorValue::Impl &other) override
110 110 {
111 111 auto &otherImpl = dynamic_cast<IteratorValue &>(other);
112 112 m_XIt->impl()->swap(*otherImpl.m_XIt->impl());
113 113 m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl());
114 114 }
115 115
116 116 private:
117 117 ArrayDataIterator m_XIt;
118 118 ArrayDataIterator m_ValuesIt;
119 119 };
120 120 } // namespace dataseries_detail
121 121
122 122 /**
123 123 * @brief The DataSeries class is the base (abstract) implementation of IDataSeries.
124 124 *
125 125 * It proposes to set a dimension for the values ​​data.
126 126 *
127 127 * A DataSeries is always sorted on its x-axis data.
128 128 *
129 129 * @tparam Dim The dimension of the values data
130 130 *
131 131 */
132 132 template <int Dim>
133 133 class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries {
134 134 friend class DataSeriesMergeHelper;
135 135
136 136 public:
137 137 /// Tag needed to define the push_back() method
138 138 /// @sa push_back()
139 139 using value_type = DataSeriesIteratorValue;
140 140
141 141 /// @sa IDataSeries::xAxisData()
142 142 std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; }
143 143 const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; }
144 144
145 145 /// @sa IDataSeries::xAxisUnit()
146 146 Unit xAxisUnit() const override { return m_XAxisUnit; }
147 147
148 148 /// @return the values dataset
149 149 std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; }
150 150 const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; }
151 151
152 152 /// @sa IDataSeries::valuesUnit()
153 153 Unit valuesUnit() const override { return m_ValuesUnit; }
154 154
155 155
156 156 SqpRange range() const override
157 157 {
158 158 if (!m_XAxisData->cdata().isEmpty()) {
159 159 return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()};
160 160 }
161 161
162 162 return SqpRange{};
163 163 }
164 164
165 165 void clear()
166 166 {
167 167 m_XAxisData->clear();
168 168 m_ValuesData->clear();
169 169 }
170 170
171 171 bool isEmpty() const noexcept { return m_XAxisData->size() == 0; }
172 172
173 173 /// Merges into the data series an other data series
174 174 /// @remarks the data series to merge with is cleared after the operation
175 175 void merge(IDataSeries *dataSeries) override
176 176 {
177 177 dataSeries->lockWrite();
178 178 lockWrite();
179 179
180 180 if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) {
181 181 DataSeriesMergeHelper::merge(*other, *this);
182 182 }
183 183 else {
184 184 qCWarning(LOG_DataSeries())
185 185 << QObject::tr("Detection of a type of IDataSeries we cannot merge with !");
186 186 }
187 187 unlock();
188 188 dataSeries->unlock();
189 189 }
190 190
191 191 void purge(double min, double max) override
192 192 {
193 // Nothing to purge if series is empty
194 if (isEmpty()) {
195 return;
196 }
197
193 198 if (min > max) {
194 199 std::swap(min, max);
195 200 }
196 201
197 lockWrite();
198
199 auto it = std::remove_if(
200 begin(), end(), [min, max](const auto &it) { return it.x() < min || it.x() > max; });
201 erase(it, end());
202 // Nothing to purge if series min/max are inside purge range
203 auto xMin = cbegin()->x();
204 auto xMax = (--cend())->x();
205 if (xMin >= min && xMax <= max) {
206 return;
207 }
202 208
203 unlock();
209 auto lowerIt = std::lower_bound(
210 begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; });
211 erase(begin(), lowerIt);
212 auto upperIt = std::upper_bound(
213 begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); });
214 erase(upperIt, end());
204 215 }
205 216
206 217 // ///////// //
207 218 // Iterators //
208 219 // ///////// //
209 220
210 221 DataSeriesIterator begin() override
211 222 {
212 223 return DataSeriesIterator{DataSeriesIteratorValue{
213 224 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}};
214 225 }
215 226
216 227 DataSeriesIterator end() override
217 228 {
218 229 return DataSeriesIterator{DataSeriesIteratorValue{
219 230 std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}};
220 231 }
221 232
222 233 DataSeriesIterator cbegin() const override
223 234 {
224 235 return DataSeriesIterator{DataSeriesIteratorValue{
225 236 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}};
226 237 }
227 238
228 239 DataSeriesIterator cend() const override
229 240 {
230 241 return DataSeriesIterator{DataSeriesIteratorValue{
231 242 std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}};
232 243 }
233 244
234 245 void erase(DataSeriesIterator first, DataSeriesIterator last)
235 246 {
236 247 auto firstImpl
237 248 = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl());
238 249 auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl());
239 250
240 251 if (firstImpl && lastImpl) {
241 252 m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt);
242 253 m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt);
243 254 }
244 255 }
245 256
246 257 /// @sa IDataSeries::minXAxisData()
247 258 DataSeriesIterator minXAxisData(double minXAxisData) const override
248 259 {
249 260 return std::lower_bound(
250 261 cbegin(), cend(), minXAxisData,
251 262 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
252 263 }
253 264
254 265 /// @sa IDataSeries::maxXAxisData()
255 266 DataSeriesIterator maxXAxisData(double maxXAxisData) const override
256 267 {
257 268 // Gets the first element that greater than max value
258 269 auto it = std::upper_bound(
259 270 cbegin(), cend(), maxXAxisData,
260 271 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
261 272
262 273 return it == cbegin() ? cend() : --it;
263 274 }
264 275
265 276 std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData,
266 277 double maxXAxisData) const override
267 278 {
268 279 if (minXAxisData > maxXAxisData) {
269 280 std::swap(minXAxisData, maxXAxisData);
270 281 }
271 282
272 283 auto begin = cbegin();
273 284 auto end = cend();
274 285
275 286 auto lowerIt = std::lower_bound(
276 287 begin, end, minXAxisData,
277 288 [](const auto &itValue, const auto &value) { return itValue.x() < value; });
278 289 auto upperIt = std::upper_bound(
279 290 begin, end, maxXAxisData,
280 291 [](const auto &value, const auto &itValue) { return value < itValue.x(); });
281 292
282 293 return std::make_pair(lowerIt, upperIt);
283 294 }
284 295
285 296 std::pair<DataSeriesIterator, DataSeriesIterator>
286 297 valuesBounds(double minXAxisData, double maxXAxisData) const override
287 298 {
288 299 // Places iterators to the correct x-axis range
289 300 auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData);
290 301
291 302 // Returns end iterators if the range is empty
292 303 if (xAxisRangeIts.first == xAxisRangeIts.second) {
293 304 return std::make_pair(cend(), cend());
294 305 }
295 306
296 307 // Gets the iterator on the min of all values data
297 308 auto minIt = std::min_element(
298 309 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
299 310 return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue());
300 311 });
301 312
302 313 // Gets the iterator on the max of all values data
303 314 auto maxIt = std::max_element(
304 315 xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) {
305 316 return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue());
306 317 });
307 318
308 319 return std::make_pair(minIt, maxIt);
309 320 }
310 321
311 322 // /////// //
312 323 // Mutexes //
313 324 // /////// //
314 325
315 326 virtual void lockRead() { m_Lock.lockForRead(); }
316 327 virtual void lockWrite() { m_Lock.lockForWrite(); }
317 328 virtual void unlock() { m_Lock.unlock(); }
318 329
319 330 // ///// //
320 331 // Other //
321 332 // ///// //
322 333
323 334 /// Inserts at the end of the data series the value of the iterator passed as a parameter. This
324 335 /// method is intended to be used in the context of generating a back insert iterator
325 336 /// @param iteratorValue the iterator value containing the values to insert
326 337 /// @sa http://en.cppreference.com/w/cpp/iterator/back_inserter
327 338 /// @sa merge()
328 339 /// @sa value_type
329 340 void push_back(const value_type &iteratorValue)
330 341 {
331 342 m_XAxisData->push_back(QVector<double>{iteratorValue.x()});
332 343 m_ValuesData->push_back(iteratorValue.values());
333 344 }
334 345
335 346 protected:
336 347 /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a
337 348 /// DataSeries with no values will be created.
338 349 /// @remarks data series is automatically sorted on its x-axis data
339 350 explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit,
340 351 std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit)
341 352 : m_XAxisData{xAxisData},
342 353 m_XAxisUnit{xAxisUnit},
343 354 m_ValuesData{valuesData},
344 355 m_ValuesUnit{valuesUnit}
345 356 {
346 357 if (m_XAxisData->size() != m_ValuesData->size()) {
347 358 clear();
348 359 }
349 360
350 361 // Sorts data if it's not the case
351 362 const auto &xAxisCData = m_XAxisData->cdata();
352 363 if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) {
353 364 sort();
354 365 }
355 366 }
356 367
357 368 /// Copy ctor
358 369 explicit DataSeries(const DataSeries<Dim> &other)
359 370 : m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)},
360 371 m_XAxisUnit{other.m_XAxisUnit},
361 372 m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)},
362 373 m_ValuesUnit{other.m_ValuesUnit}
363 374 {
364 375 // Since a series is ordered from its construction and is always ordered, it is not
365 376 // necessary to call the sort method here ('other' is sorted)
366 377 }
367 378
368 379 /// Assignment operator
369 380 template <int D>
370 381 DataSeries &operator=(DataSeries<D> other)
371 382 {
372 383 std::swap(m_XAxisData, other.m_XAxisData);
373 384 std::swap(m_XAxisUnit, other.m_XAxisUnit);
374 385 std::swap(m_ValuesData, other.m_ValuesData);
375 386 std::swap(m_ValuesUnit, other.m_ValuesUnit);
376 387
377 388 return *this;
378 389 }
379 390
380 391 private:
381 392 /**
382 393 * Sorts data series on its x-axis data
383 394 */
384 395 void sort() noexcept
385 396 {
386 397 auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>());
387 398 m_XAxisData = m_XAxisData->sort(permutation);
388 399 m_ValuesData = m_ValuesData->sort(permutation);
389 400 }
390 401
391 402 std::shared_ptr<ArrayData<1> > m_XAxisData;
392 403 Unit m_XAxisUnit;
393 404 std::shared_ptr<ArrayData<Dim> > m_ValuesData;
394 405 Unit m_ValuesUnit;
395 406
396 407 QReadWriteLock m_Lock;
397 408 };
398 409
399 410 #endif // SCIQLOP_DATASERIES_H
General Comments 0
You need to be logged in to leave comments. Login now