DataSeriesMergeHelper.h
132 lines
| 4.4 KiB
| text/x-c
|
CLexer
Alexandre Leroux
|
r622 | #ifndef SCIQLOP_DATASERIESMERGEHELPER_H | ||
#define SCIQLOP_DATASERIESMERGEHELPER_H | ||||
template <int Dim> | ||||
class DataSeries; | ||||
namespace detail { | ||||
/** | ||||
* Scope that can be used for a merge operation | ||||
* @tparam FEnd the type of function that will be executed at the end of the scope | ||||
*/ | ||||
template <typename FEnd> | ||||
struct MergeScope { | ||||
explicit MergeScope(FEnd end) : m_End{end} {} | ||||
virtual ~MergeScope() noexcept { m_End(); } | ||||
FEnd m_End; | ||||
}; | ||||
/** | ||||
* Creates a scope for merge operation | ||||
* @tparam end the function executed at the end of the scope | ||||
*/ | ||||
template <typename FEnd> | ||||
MergeScope<FEnd> scope(FEnd end) | ||||
{ | ||||
return MergeScope<FEnd>{end}; | ||||
} | ||||
/** | ||||
* Enum used to position a data series relative to another during a merge operation | ||||
*/ | ||||
enum class MergePosition { LOWER_THAN, GREATER_THAN, EQUAL, OVERLAP }; | ||||
/** | ||||
* Computes the position of the first data series relative to the second data series | ||||
* @param lhs the first data series | ||||
* @param rhs the second data series | ||||
* @return the merge position computed | ||||
* @remarks the data series must not be empty | ||||
*/ | ||||
template <int Dim> | ||||
MergePosition mergePosition(DataSeries<Dim> &lhs, DataSeries<Dim> &rhs) | ||||
{ | ||||
Q_ASSERT(!lhs.isEmpty() && !rhs.isEmpty()); | ||||
// Case lhs < rhs | ||||
auto lhsLast = --lhs.cend(); | ||||
auto rhsFirst = rhs.cbegin(); | ||||
if (lhsLast->x() < rhsFirst->x()) { | ||||
return MergePosition::LOWER_THAN; | ||||
} | ||||
// Case lhs > rhs | ||||
auto lhsFirst = lhs.cbegin(); | ||||
auto rhsLast = --rhs.cend(); | ||||
if (lhsFirst->x() > rhsLast->x()) { | ||||
return MergePosition::GREATER_THAN; | ||||
} | ||||
// Other cases | ||||
auto equal = std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(), | ||||
[](const auto &it1, const auto &it2) { | ||||
return it1.x() == it2.x() && it1.values() == it2.values(); | ||||
}); | ||||
return equal ? MergePosition::EQUAL : MergePosition::OVERLAP; | ||||
} | ||||
} // namespace detail | ||||
/// Helper used to merge two DataSeries | ||||
/// @sa DataSeries | ||||
struct DataSeriesMergeHelper { | ||||
/// Merges the source data series into the dest data series. Data of the source data series are | ||||
/// consumed | ||||
template <int Dim> | ||||
static void merge(DataSeries<Dim> &source, DataSeries<Dim> &dest) | ||||
{ | ||||
// Creates a scope to clear source data series at the end of the merge | ||||
auto _ = detail::scope([&source]() { source.clear(); }); | ||||
// Case : source data series is empty -> no merge is made | ||||
if (source.isEmpty()) { | ||||
return; | ||||
} | ||||
// Case : dest data series is empty -> we simply swap the data | ||||
if (dest.isEmpty()) { | ||||
std::swap(dest.m_XAxisData, source.m_XAxisData); | ||||
std::swap(dest.m_ValuesData, source.m_ValuesData); | ||||
return; | ||||
} | ||||
// Gets the position of the source in relation to the destination | ||||
auto sourcePosition = detail::mergePosition(source, dest); | ||||
switch (sourcePosition) { | ||||
case detail::MergePosition::LOWER_THAN: | ||||
case detail::MergePosition::GREATER_THAN: { | ||||
auto prepend = sourcePosition == detail::MergePosition::LOWER_THAN; | ||||
dest.m_XAxisData->add(*source.m_XAxisData, prepend); | ||||
dest.m_ValuesData->add(*source.m_ValuesData, prepend); | ||||
break; | ||||
} | ||||
case detail::MergePosition::EQUAL: | ||||
// the data series equal each other : no merge made | ||||
break; | ||||
case detail::MergePosition::OVERLAP: { | ||||
// the two data series overlap : merge is made | ||||
auto temp = dest.clone(); | ||||
if (auto tempSeries = dynamic_cast<DataSeries<Dim> *>(temp.get())) { | ||||
// Makes the merge : | ||||
// - Data are sorted by x-axis values | ||||
// - If two entries are in the source range and the other range, only one entry | ||||
// is retained as result | ||||
// - The results are stored directly in the data series | ||||
dest.clear(); | ||||
std::set_union( | ||||
tempSeries->cbegin(), tempSeries->cend(), source.cbegin(), source.cend(), | ||||
std::back_inserter(dest), | ||||
[](const auto &it1, const auto &it2) { return it1.x() < it2.x(); }); | ||||
} | ||||
break; | ||||
} | ||||
default: | ||||
Q_ASSERT(false); | ||||
} | ||||
} | ||||
}; | ||||
#endif // SCIQLOP_DATASERIESMERGEHELPER_H | ||||