DataSeries.h
190 lines
| 6.0 KiB
| text/x-c
|
CLexer
Alexandre Leroux
|
r117 | #ifndef SCIQLOP_DATASERIES_H | ||
#define SCIQLOP_DATASERIES_H | ||||
Alexandre Leroux
|
r416 | #include <Common/SortUtils.h> | ||
Alexandre Leroux
|
r117 | #include <Data/ArrayData.h> | ||
#include <Data/IDataSeries.h> | ||||
r227 | #include <QLoggingCategory> | |||
r336 | #include <QReadLocker> | |||
#include <QReadWriteLock> | ||||
Alexandre Leroux
|
r117 | #include <memory> | ||
r227 | Q_DECLARE_LOGGING_CATEGORY(LOG_DataSeries) | |||
Q_LOGGING_CATEGORY(LOG_DataSeries, "DataSeries") | ||||
Alexandre Leroux
|
r117 | /** | ||
* @brief The DataSeries class is the base (abstract) implementation of IDataSeries. | ||||
* | ||||
Alexandre Leroux
|
r415 | * It proposes to set a dimension for the values data. | ||
* | ||||
* A DataSeries is always sorted on its x-axis data. | ||||
Alexandre Leroux
|
r117 | * | ||
* @tparam Dim The dimension of the values data | ||||
* | ||||
*/ | ||||
template <int Dim> | ||||
class DataSeries : public IDataSeries { | ||||
public: | ||||
/// @sa IDataSeries::xAxisData() | ||||
std::shared_ptr<ArrayData<1> > xAxisData() override { return m_XAxisData; } | ||||
Alexandre Leroux
|
r287 | const std::shared_ptr<ArrayData<1> > xAxisData() const { return m_XAxisData; } | ||
Alexandre Leroux
|
r117 | |||
/// @sa IDataSeries::xAxisUnit() | ||||
Alexandre Leroux
|
r164 | Unit xAxisUnit() const override { return m_XAxisUnit; } | ||
Alexandre Leroux
|
r117 | |||
/// @return the values dataset | ||||
Alexandre Leroux
|
r287 | std::shared_ptr<ArrayData<Dim> > valuesData() { return m_ValuesData; } | ||
const std::shared_ptr<ArrayData<Dim> > valuesData() const { return m_ValuesData; } | ||||
Alexandre Leroux
|
r117 | |||
/// @sa IDataSeries::valuesUnit() | ||||
Alexandre Leroux
|
r164 | Unit valuesUnit() const override { return m_ValuesUnit; } | ||
Alexandre Leroux
|
r117 | |||
r510 | ||||
SqpRange range() const override | ||||
{ | ||||
if (!m_XAxisData->cdata().isEmpty()) { | ||||
return SqpRange{m_XAxisData->cdata().first(), m_XAxisData->cdata().last()}; | ||||
} | ||||
return SqpRange{}; | ||||
} | ||||
r336 | void clear() | |||
{ | ||||
m_XAxisData->clear(); | ||||
m_ValuesData->clear(); | ||||
} | ||||
Alexandre Leroux
|
r417 | /// Merges into the data series an other data series | ||
/// @remarks the data series to merge with is cleared after the operation | ||||
r217 | void merge(IDataSeries *dataSeries) override | |||
{ | ||||
Alexandre Leroux
|
r417 | dataSeries->lockWrite(); | ||
lockWrite(); | ||||
if (auto other = dynamic_cast<DataSeries<Dim> *>(dataSeries)) { | ||||
const auto &otherXAxisData = other->xAxisData()->cdata(); | ||||
const auto &xAxisData = m_XAxisData->cdata(); | ||||
// As data series are sorted, we can improve performances of merge, by call the sort | ||||
// method only if the two data series overlap. | ||||
if (!otherXAxisData.empty()) { | ||||
auto firstValue = otherXAxisData.front(); | ||||
auto lastValue = otherXAxisData.back(); | ||||
auto xAxisDataBegin = xAxisData.cbegin(); | ||||
auto xAxisDataEnd = xAxisData.cend(); | ||||
bool prepend; | ||||
bool sortNeeded; | ||||
if (std::lower_bound(xAxisDataBegin, xAxisDataEnd, firstValue) == xAxisDataEnd) { | ||||
// Other data series if after data series | ||||
prepend = false; | ||||
sortNeeded = false; | ||||
} | ||||
else if (std::upper_bound(xAxisDataBegin, xAxisDataEnd, lastValue) | ||||
== xAxisDataBegin) { | ||||
// Other data series if before data series | ||||
prepend = true; | ||||
sortNeeded = false; | ||||
} | ||||
else { | ||||
// The two data series overlap | ||||
prepend = false; | ||||
sortNeeded = true; | ||||
} | ||||
// Makes the merge | ||||
m_XAxisData->add(*other->xAxisData(), prepend); | ||||
m_ValuesData->add(*other->valuesData(), prepend); | ||||
if (sortNeeded) { | ||||
sort(); | ||||
} | ||||
} | ||||
// Clears the other data series | ||||
other->clear(); | ||||
r217 | } | |||
r227 | else { | |||
qCWarning(LOG_DataSeries()) | ||||
Alexandre Leroux
|
r417 | << QObject::tr("Detection of a type of IDataSeries we cannot merge with !"); | ||
r227 | } | |||
Alexandre Leroux
|
r417 | unlock(); | ||
dataSeries->unlock(); | ||||
r217 | } | |||
r336 | virtual void lockRead() { m_Lock.lockForRead(); } | |||
virtual void lockWrite() { m_Lock.lockForWrite(); } | ||||
virtual void unlock() { m_Lock.unlock(); } | ||||
Alexandre Leroux
|
r117 | protected: | ||
Alexandre Leroux
|
r415 | /// 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 | ||||
Alexandre Leroux
|
r175 | explicit DataSeries(std::shared_ptr<ArrayData<1> > xAxisData, const Unit &xAxisUnit, | ||
std::shared_ptr<ArrayData<Dim> > valuesData, const Unit &valuesUnit) | ||||
Alexandre Leroux
|
r117 | : m_XAxisData{xAxisData}, | ||
Alexandre Leroux
|
r175 | m_XAxisUnit{xAxisUnit}, | ||
Alexandre Leroux
|
r117 | m_ValuesData{valuesData}, | ||
Alexandre Leroux
|
r175 | m_ValuesUnit{valuesUnit} | ||
Alexandre Leroux
|
r117 | { | ||
Alexandre Leroux
|
r415 | 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(); | ||||
} | ||||
Alexandre Leroux
|
r117 | } | ||
Alexandre Leroux
|
r287 | /// 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} | ||||
{ | ||||
Alexandre Leroux
|
r415 | // 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) | ||||
Alexandre Leroux
|
r287 | } | ||
/// 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; | ||||
} | ||||
Alexandre Leroux
|
r117 | private: | ||
Alexandre Leroux
|
r415 | /** | ||
* Sorts data series on its x-axis data | ||||
*/ | ||||
void sort() noexcept | ||||
{ | ||||
Alexandre Leroux
|
r416 | auto permutation = SortUtils::sortPermutation(*m_XAxisData, std::less<double>()); | ||
m_XAxisData = m_XAxisData->sort(permutation); | ||||
m_ValuesData = m_ValuesData->sort(permutation); | ||||
Alexandre Leroux
|
r415 | } | ||
Alexandre Leroux
|
r117 | std::shared_ptr<ArrayData<1> > m_XAxisData; | ||
Alexandre Leroux
|
r164 | Unit m_XAxisUnit; | ||
Alexandre Leroux
|
r117 | std::shared_ptr<ArrayData<Dim> > m_ValuesData; | ||
Alexandre Leroux
|
r164 | Unit m_ValuesUnit; | ||
r336 | ||||
QReadWriteLock m_Lock; | ||||
Alexandre Leroux
|
r117 | }; | ||
#endif // SCIQLOP_DATASERIES_H | ||||