#pragma once #include "Timed.h" #include #include #include #include "tools.h" enum class FindMode { SampleLeft, SampleRight, SearchLeft, SearchRight }; namespace internal { template bool valueEquals(const Timed& lhs, const Timed& rhs) { return lhs.getValue() == rhs.getValue(); } template<> inline bool valueEquals(const Timed& lhs, const Timed& rhs) { UNUSED(lhs); UNUSED(rhs); return true; } } template class Timeline { public: using time_type = TimeRange::time_type; private: struct compare { bool operator()(const Timed& lhs, const Timed& rhs) const { return lhs.getStart() < rhs.getStart(); } bool operator()(const time_type& lhs, const Timed& rhs) const { return lhs < rhs.getStart(); } bool operator()(const Timed& lhs, const time_type& rhs) const { return lhs.getStart() < rhs; } using is_transparent = int; }; public: using set_type = std::set, compare>; using const_iterator = typename set_type::const_iterator; using iterator = const_iterator; using reverse_iterator = typename set_type::reverse_iterator; using size_type = size_t; using value_type = Timed; class reference { public: operator boost::optional() const { auto optional = timeline.get(time); return optional ? optional->getValue() : boost::optional(); } operator boost::optional() const { auto optional = timeline.get(time); return optional ? optional->getValue() : boost::optional(); } operator const T&() const { auto optional = timeline.get(time); assert(optional); return optional->getValue(); } reference& operator=(boost::optional value) { if (value) { timeline.set(time, time + time_type(1), *value); } else { timeline.clear(time, time + time_type(1)); } return *this; } private: friend class Timeline; reference(Timeline& timeline, time_type time) : timeline(timeline), time(time) {} Timeline& timeline; time_type time; }; Timeline() {} template Timeline(InputIterator first, InputIterator last) { for (auto it = first; it != last; ++it) { // Virtual function call in constructor. Derived constructors don't call this one. Timeline::set(*it); } } explicit Timeline(std::initializer_list> initializerList) : Timeline(initializerList.begin(), initializerList.end()) {} virtual ~Timeline() {} bool empty() const { return elements.empty(); } size_type size() const { return elements.size(); } virtual TimeRange getRange() const { return empty() ? TimeRange(time_type::zero(), time_type::zero()) : TimeRange(begin()->getStart(), rbegin()->getEnd()); } iterator begin() const { return elements.begin(); } iterator end() const { return elements.end(); } reverse_iterator rbegin() const { return elements.rbegin(); } reverse_iterator rend() const { return elements.rend(); } iterator find(time_type time, FindMode findMode = FindMode::SampleRight) const { switch (findMode) { case FindMode::SampleLeft: { iterator left = find(time, FindMode::SearchLeft); return left != end() && left->getEnd() >= time ? left : end(); } case FindMode::SampleRight: { iterator right = find(time, FindMode::SearchRight); return right != end() && right->getStart() <= time ? right : end(); } case FindMode::SearchLeft: { // Get first element starting >= time iterator it = elements.lower_bound(time); // Go one element back return it != begin() ? --it : end(); } case FindMode::SearchRight: { // Get first element starting > time iterator it = elements.upper_bound(time); // Go one element back if (it != begin()) { iterator left = it; --left; if (left->getEnd() > time) return left; } return it; } default: throw std::invalid_argument("Unexpected find mode."); } } boost::optional&> get(time_type time) const { iterator it = find(time); return (it != end()) ? *it : boost::optional&>(); } virtual void clear(const TimeRange& range) { // Make sure the time range is not empty if (range.empty()) return; // Split overlapping elements splitAt(range.getStart()); splitAt(range.getEnd()); // Erase overlapping elements elements.erase(find(range.getStart(), FindMode::SearchRight), find(range.getEnd(), FindMode::SearchRight)); } void clear(time_type start, time_type end) { clear(TimeRange(start, end)); } virtual iterator set(Timed timedValue) { // Make sure the timed value is not empty if (timedValue.getTimeRange().empty()) { return end(); } // Extend the timed value if it touches elements with equal value iterator elementBefore = find(timedValue.getStart(), FindMode::SampleLeft); if (elementBefore != end() && ::internal::valueEquals(*elementBefore, timedValue)) { timedValue.getTimeRange().resize(elementBefore->getStart(), timedValue.getEnd()); } iterator elementAfter = find(timedValue.getEnd(), FindMode::SampleRight); if (elementAfter != end() && ::internal::valueEquals(*elementAfter, timedValue)) { timedValue.getTimeRange().resize(timedValue.getStart(), elementAfter->getEnd()); } // Erase overlapping elements Timeline::clear(timedValue.getTimeRange()); // Add timed value return elements.insert(timedValue).first; } template iterator set(const TimeRange& timeRange, const std::enable_if_t::value, T>& value) { return set(Timed(timeRange, value)); } template iterator set(time_type start, time_type end, const std::enable_if_t::value, T>& value) { return set(Timed(start, end, value)); } template std::enable_if_t::value, iterator> set(time_type start, time_type end) { return set(Timed(start, end)); } reference operator[](time_type time) { return reference(*this, time); } // ReSharper disable once CppConstValueFunctionReturnType const reference operator[](time_type time) const { return reference(*this, time); } virtual void shift(time_type offset) { if (offset == time_type::zero()) return; set_type newElements; for (Timed element : elements) { element.getTimeRange().shift(offset); newElements.insert(element); } elements = std::move(newElements); } Timeline(const Timeline&) = default; Timeline(Timeline&&) = default; Timeline& operator=(const Timeline&) = default; Timeline& operator=(Timeline&&) = default; bool operator==(const Timeline& rhs) const { return equals(rhs); } bool operator!=(const Timeline& rhs) const { return !equals(rhs); } protected: bool equals(const Timeline& rhs) const { return elements == rhs.elements; } private: void splitAt(time_type splitTime) { iterator elementBefore = find(splitTime - time_type(1)); iterator elementAfter = find(splitTime); if (elementBefore != elementAfter || elementBefore == end()) return; Timed first = *elementBefore; Timed second = *elementBefore; elements.erase(elementBefore); first.getTimeRange().resize(first.getStart(), splitTime); elements.insert(first); second.getTimeRange().resize(splitTime, second.getEnd()); elements.insert(second); } set_type elements; }; template std::ostream& operator<<(std::ostream& stream, const Timeline& timeline) { stream << "Timeline{"; bool isFirst = true; for (auto element : timeline) { if (!isFirst) stream << ", "; isFirst = false; stream << element; } return stream << "}"; }