DataSeries.h
401 lines
| 13.1 KiB
| text/x-c
|
CLexer
|
r125 | #ifndef SCIQLOP_DATASERIES_H | ||
#define SCIQLOP_DATASERIES_H | ||||
|
r562 | #include "CoreGlobal.h" | ||
|
r450 | #include <Common/SortUtils.h> | ||
|
r125 | #include <Data/ArrayData.h> | ||
|
r668 | #include <Data/DataSeriesMergeHelper.h> | ||
|
r125 | #include <Data/IDataSeries.h> | ||
r243 | #include <QLoggingCategory> | |||
r364 | #include <QReadLocker> | |||
#include <QReadWriteLock> | ||||
|
r125 | #include <memory> | ||
|
r562 | // We don't use the Qt macro since the log is used in the header file, which causes multiple log | ||
// definitions with inheritance. Inline method is used instead | ||||
inline const QLoggingCategory &LOG_DataSeries() | ||||
{ | ||||
static const QLoggingCategory category{"DataSeries"}; | ||||
return category; | ||||
} | ||||
r243 | ||||
|
r596 | template <int Dim> | ||
class DataSeries; | ||||
namespace dataseries_detail { | ||||
|
r673 | template <int Dim, bool IsConst> | ||
|
r596 | class IteratorValue : public DataSeriesIteratorValue::Impl { | ||
public: | ||||
|
r673 | friend class DataSeries<Dim>; | ||
template <bool IC = IsConst, typename = std::enable_if_t<IC == false> > | ||||
explicit IteratorValue(DataSeries<Dim> &dataSeries, bool begin) | ||||
: m_XIt(begin ? dataSeries.xAxisData()->begin() : dataSeries.xAxisData()->end()), | ||||
m_ValuesIt(begin ? dataSeries.valuesData()->begin() : dataSeries.valuesData()->end()) | ||||
{ | ||||
} | ||||
template <bool IC = IsConst, typename = std::enable_if_t<IC == true> > | ||||
|
r596 | explicit IteratorValue(const DataSeries<Dim> &dataSeries, bool begin) | ||
: m_XIt(begin ? dataSeries.xAxisData()->cbegin() : dataSeries.xAxisData()->cend()), | ||||
m_ValuesIt(begin ? dataSeries.valuesData()->cbegin() | ||||
: dataSeries.valuesData()->cend()) | ||||
{ | ||||
} | ||||
|
r673 | |||
|
r596 | IteratorValue(const IteratorValue &other) = default; | ||
std::unique_ptr<DataSeriesIteratorValue::Impl> clone() const override | ||||
{ | ||||
|
r673 | return std::make_unique<IteratorValue<Dim, IsConst> >(*this); | ||
|
r596 | } | ||
|
r689 | int distance(const DataSeriesIteratorValue::Impl &other) const override try { | ||
const auto &otherImpl = dynamic_cast<const IteratorValue &>(other); | ||||
return m_XIt->distance(*otherImpl.m_XIt); | ||||
} | ||||
catch (const std::bad_cast &) { | ||||
return 0; | ||||
} | ||||
|
r596 | bool equals(const DataSeriesIteratorValue::Impl &other) const override try { | ||
const auto &otherImpl = dynamic_cast<const IteratorValue &>(other); | ||||
return std::tie(m_XIt, m_ValuesIt) == std::tie(otherImpl.m_XIt, otherImpl.m_ValuesIt); | ||||
} | ||||
catch (const std::bad_cast &) { | ||||
return false; | ||||
} | ||||
|
r689 | bool lowerThan(const DataSeriesIteratorValue::Impl &other) const override try { | ||
const auto &otherImpl = dynamic_cast<const IteratorValue &>(other); | ||||
return m_XIt->lowerThan(*otherImpl.m_XIt); | ||||
} | ||||
catch (const std::bad_cast &) { | ||||
return false; | ||||
} | ||||
std::unique_ptr<DataSeriesIteratorValue::Impl> advance(int offset) const override | ||||
{ | ||||
auto result = clone(); | ||||
while (offset--) { | ||||
result->next(); | ||||
} | ||||
return result; | ||||
} | ||||
|
r596 | void next() override | ||
{ | ||||
++m_XIt; | ||||
++m_ValuesIt; | ||||
} | ||||
void prev() override | ||||
{ | ||||
--m_XIt; | ||||
--m_ValuesIt; | ||||
} | ||||
double x() const override { return m_XIt->at(0); } | ||||
double value() const override { return m_ValuesIt->at(0); } | ||||
double value(int componentIndex) const override { return m_ValuesIt->at(componentIndex); } | ||||
|
r609 | double minValue() const override { return m_ValuesIt->min(); } | ||
double maxValue() const override { return m_ValuesIt->max(); } | ||||
|
r667 | QVector<double> values() const override { return m_ValuesIt->values(); } | ||
|
r596 | |||
|
r674 | void swap(DataSeriesIteratorValue::Impl &other) override | ||
{ | ||||
auto &otherImpl = dynamic_cast<IteratorValue &>(other); | ||||
m_XIt->impl()->swap(*otherImpl.m_XIt->impl()); | ||||
m_ValuesIt->impl()->swap(*otherImpl.m_ValuesIt->impl()); | ||||
} | ||||
|
r596 | private: | ||
|
r641 | ArrayDataIterator m_XIt; | ||
ArrayDataIterator m_ValuesIt; | ||||
|
r596 | }; | ||
} // namespace dataseries_detail | ||||
|
r125 | /** | ||
* @brief The DataSeries class is the base (abstract) implementation of IDataSeries. | ||||
* | ||||
|
r449 | * It proposes to set a dimension for the values data. | ||
* | ||||
* A DataSeries is always sorted on its x-axis data. | ||||
|
r125 | * | ||
* @tparam Dim The dimension of the values data | ||||
* | ||||
*/ | ||||
template <int Dim> | ||||
|
r562 | class SCIQLOP_CORE_EXPORT DataSeries : public IDataSeries { | ||
|
r668 | friend class DataSeriesMergeHelper; | ||
|
r125 | public: | ||
/// @sa IDataSeries::xAxisData() | ||||
std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; } | ||||
|
r310 | const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; } | ||
|
r125 | |||
/// @sa IDataSeries::xAxisUnit() | ||||
|
r177 | Unit xAxisUnit() const override { return m_XAxisUnit; } | ||
|
r125 | |||
/// @return the values dataset | ||||
|
r310 | std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; } | ||
const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; } | ||||
|
r125 | |||
/// @sa IDataSeries::valuesUnit() | ||||
|
r177 | Unit valuesUnit() const override { return m_ValuesUnit; } | ||
|
r125 | |||
r539 | ||||
SqpRange range() const override | ||||
{ | ||||
|
r694 | if (!m_XAxisData->cdata().empty()) { | ||
return SqpRange{m_XAxisData->cdata().front(), m_XAxisData->cdata().back()}; | ||||
r539 | } | |||
return SqpRange{}; | ||||
} | ||||
r364 | void clear() | |||
{ | ||||
m_XAxisData->clear(); | ||||
m_ValuesData->clear(); | ||||
} | ||||
|
r668 | bool isEmpty() const noexcept { return m_XAxisData->size() == 0; } | ||
|
r451 | /// Merges into the data series an other data series | ||
/// @remarks the data series to merge with is cleared after the operation | ||||
r233 | void merge(IDataSeries *dataSeries) override | |||
{ | ||||
|
r451 | dataSeries->lockWrite(); | ||
lockWrite(); | ||||
if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) { | ||||
|
r668 | DataSeriesMergeHelper::merge(*other, *this); | ||
r233 | } | |||
r243 | else { | |||
qCWarning(LOG_DataSeries()) | ||||
|
r451 | << QObject::tr("Detection of a type of IDataSeries we cannot merge with !"); | ||
r243 | } | |||
|
r451 | unlock(); | ||
dataSeries->unlock(); | ||||
r233 | } | |||
|
r676 | void purge(double min, double max) override | ||
{ | ||||
|
r690 | // Nothing to purge if series is empty | ||
if (isEmpty()) { | ||||
return; | ||||
} | ||||
|
r676 | if (min > max) { | ||
std::swap(min, max); | ||||
} | ||||
|
r690 | // Nothing to purge if series min/max are inside purge range | ||
auto xMin = cbegin()->x(); | ||||
auto xMax = (--cend())->x(); | ||||
if (xMin >= min && xMax <= max) { | ||||
return; | ||||
} | ||||
|
r676 | |||
|
r690 | auto lowerIt = std::lower_bound( | ||
begin(), end(), min, [](const auto &it, const auto &val) { return it.x() < val; }); | ||||
erase(begin(), lowerIt); | ||||
auto upperIt = std::upper_bound( | ||||
begin(), end(), max, [](const auto &val, const auto &it) { return val < it.x(); }); | ||||
erase(upperIt, end()); | ||||
|
r676 | } | ||
|
r556 | // ///////// // | ||
// Iterators // | ||||
// ///////// // | ||||
|
r673 | DataSeriesIterator begin() override | ||
{ | ||||
return DataSeriesIterator{DataSeriesIteratorValue{ | ||||
std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, true)}}; | ||||
} | ||||
DataSeriesIterator end() override | ||||
{ | ||||
return DataSeriesIterator{DataSeriesIteratorValue{ | ||||
std::make_unique<dataseries_detail::IteratorValue<Dim, false> >(*this, false)}}; | ||||
} | ||||
|
r596 | DataSeriesIterator cbegin() const override | ||
{ | ||||
return DataSeriesIterator{DataSeriesIteratorValue{ | ||||
|
r673 | std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, true)}}; | ||
|
r596 | } | ||
|
r556 | |||
|
r596 | DataSeriesIterator cend() const override | ||
{ | ||||
return DataSeriesIterator{DataSeriesIteratorValue{ | ||||
|
r673 | std::make_unique<dataseries_detail::IteratorValue<Dim, true> >(*this, false)}}; | ||
|
r596 | } | ||
|
r556 | |||
|
r675 | void erase(DataSeriesIterator first, DataSeriesIterator last) | ||
{ | ||||
auto firstImpl | ||||
= dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(first->impl()); | ||||
auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, false> *>(last->impl()); | ||||
if (firstImpl && lastImpl) { | ||||
m_XAxisData->erase(firstImpl->m_XIt, lastImpl->m_XIt); | ||||
m_ValuesData->erase(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt); | ||||
} | ||||
} | ||||
|
r696 | void insert(DataSeriesIterator first, DataSeriesIterator last, bool prepend = false) | ||
{ | ||||
auto firstImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(first->impl()); | ||||
auto lastImpl = dynamic_cast<dataseries_detail::IteratorValue<Dim, true> *>(last->impl()); | ||||
if (firstImpl && lastImpl) { | ||||
m_XAxisData->insert(firstImpl->m_XIt, lastImpl->m_XIt, prepend); | ||||
m_ValuesData->insert(firstImpl->m_ValuesIt, lastImpl->m_ValuesIt, prepend); | ||||
} | ||||
} | ||||
|
r604 | /// @sa IDataSeries::minXAxisData() | ||
DataSeriesIterator minXAxisData(double minXAxisData) const override | ||||
|
r599 | { | ||
return std::lower_bound( | ||||
cbegin(), cend(), minXAxisData, | ||||
[](const auto &itValue, const auto &value) { return itValue.x() < value; }); | ||||
} | ||||
|
r604 | /// @sa IDataSeries::maxXAxisData() | ||
DataSeriesIterator maxXAxisData(double maxXAxisData) const override | ||||
|
r600 | { | ||
// Gets the first element that greater than max value | ||||
auto it = std::upper_bound( | ||||
cbegin(), cend(), maxXAxisData, | ||||
[](const auto &value, const auto &itValue) { return value < itValue.x(); }); | ||||
return it == cbegin() ? cend() : --it; | ||||
} | ||||
|
r605 | std::pair<DataSeriesIterator, DataSeriesIterator> xAxisRange(double minXAxisData, | ||
double maxXAxisData) const override | ||||
|
r557 | { | ||
|
r605 | if (minXAxisData > maxXAxisData) { | ||
std::swap(minXAxisData, maxXAxisData); | ||||
|
r557 | } | ||
auto begin = cbegin(); | ||||
auto end = cend(); | ||||
|
r605 | auto lowerIt = std::lower_bound( | ||
begin, end, minXAxisData, | ||||
[](const auto &itValue, const auto &value) { return itValue.x() < value; }); | ||||
auto upperIt = std::upper_bound( | ||||
|
r696 | lowerIt, end, maxXAxisData, | ||
|
r605 | [](const auto &value, const auto &itValue) { return value < itValue.x(); }); | ||
|
r557 | |||
return std::make_pair(lowerIt, upperIt); | ||||
} | ||||
|
r608 | std::pair<DataSeriesIterator, DataSeriesIterator> | ||
valuesBounds(double minXAxisData, double maxXAxisData) const override | ||||
{ | ||||
// Places iterators to the correct x-axis range | ||||
auto xAxisRangeIts = xAxisRange(minXAxisData, maxXAxisData); | ||||
// Returns end iterators if the range is empty | ||||
if (xAxisRangeIts.first == xAxisRangeIts.second) { | ||||
return std::make_pair(cend(), cend()); | ||||
} | ||||
|
r609 | // Gets the iterator on the min of all values data | ||
auto minIt = std::min_element( | ||||
xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) { | ||||
return SortUtils::minCompareWithNaN(it1.minValue(), it2.minValue()); | ||||
}); | ||||
|
r608 | |||
|
r609 | // Gets the iterator on the max of all values data | ||
auto maxIt = std::max_element( | ||||
xAxisRangeIts.first, xAxisRangeIts.second, [](const auto &it1, const auto &it2) { | ||||
return SortUtils::maxCompareWithNaN(it1.maxValue(), it2.maxValue()); | ||||
}); | ||||
return std::make_pair(minIt, maxIt); | ||||
|
r608 | } | ||
|
r556 | // /////// // | ||
// Mutexes // | ||||
// /////// // | ||||
r364 | virtual void lockRead() { m_Lock.lockForRead(); } | |||
virtual void lockWrite() { m_Lock.lockForWrite(); } | ||||
virtual void unlock() { m_Lock.unlock(); } | ||||
|
r125 | protected: | ||
|
r449 | /// Protected ctor (DataSeries is abstract). The vectors must have the same size, otherwise a | ||
/// DataSeries with no values will be created. | ||||
/// @remarks data series is automatically sorted on its x-axis data | ||||
|
r188 | explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit, | ||
std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit) | ||||
|
r125 | : m_XAxisData{xAxisData}, | ||
|
r188 | m_XAxisUnit{xAxisUnit}, | ||
|
r125 | m_ValuesData{valuesData}, | ||
|
r188 | m_ValuesUnit{valuesUnit} | ||
|
r125 | { | ||
|
r449 | if (m_XAxisData->size() != m_ValuesData->size()) { | ||
clear(); | ||||
} | ||||
// Sorts data if it's not the case | ||||
const auto &xAxisCData = m_XAxisData->cdata(); | ||||
if (!std::is_sorted(xAxisCData.cbegin(), xAxisCData.cend())) { | ||||
sort(); | ||||
} | ||||
|
r125 | } | ||
|
r310 | /// Copy ctor | ||
explicit DataSeries(const DataSeries<Dim> &other) | ||||
: m_XAxisData{std::make_shared<ArrayData<1> >(*other.m_XAxisData)}, | ||||
m_XAxisUnit{other.m_XAxisUnit}, | ||||
m_ValuesData{std::make_shared<ArrayData<Dim> >(*other.m_ValuesData)}, | ||||
m_ValuesUnit{other.m_ValuesUnit} | ||||
{ | ||||
|
r449 | // Since a series is ordered from its construction and is always ordered, it is not | ||
// necessary to call the sort method here ('other' is sorted) | ||||
|
r310 | } | ||
/// Assignment operator | ||||
template <int D> | ||||
DataSeries &operator=(DataSeries<D> other) | ||||
{ | ||||
std::swap(m_XAxisData, other.m_XAxisData); | ||||
std::swap(m_XAxisUnit, other.m_XAxisUnit); | ||||
std::swap(m_ValuesData, other.m_ValuesData); | ||||
std::swap(m_ValuesUnit, other.m_ValuesUnit); | ||||
return *this; | ||||
} | ||||
|
r125 | private: | ||
|
r449 | /** | ||
* Sorts data series on its x-axis data | ||||
*/ | ||||
void sort() noexcept | ||||
{ | ||||
|
r450 | auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>()); | ||
m_XAxisData = m_XAxisData->sort(permutation); | ||||
m_ValuesData = m_ValuesData->sort(permutation); | ||||
|
r449 | } | ||
|
r125 | std::shared_ptr<ArrayData<1> > m_XAxisData; | ||
|
r177 | Unit m_XAxisUnit; | ||
|
r125 | std::shared_ptr<ArrayData<Dim> > m_ValuesData; | ||
|
r177 | Unit m_ValuesUnit; | ||
r364 | ||||
QReadWriteLock m_Lock; | ||||
|
r125 | }; | ||
#endif // SCIQLOP_DATASERIES_H | ||||