#pragma once #include "timing/time_units.h" #include #include namespace time_transform { using namespace time_units; namespace SegmentMarks { using MaskType = uint32_t; constexpr MaskType NONE = 0; constexpr MaskType INVALID_ARRANGEMENT_TIME = 1 << 0; constexpr MaskType SEEK_TO_LOOP_START = 1 << 1; } // namespace SegmentMarks // Represents a time range [start, end). template struct TimeRange { using TimePoint = TypedTime; using TimeDelta = TypedTimeDelta; TimePoint start; TimePoint end; bool contains(const TimePoint &t, bool include_end = false) const { if (include_end) { return t >= start && t <= end; } else { return t >= start && t < end; } } bool is_finite() const { return start.is_finite() && end.is_finite(); } TimeDelta duration() const { return end - start; } [[nodiscard]] std::optional intersect(const TimeRange &other) const { // Determine latest start time and earliest end time TimePoint latest_start = std::max(start, other.start); TimePoint earliest_end = std::min(end, other.end); // Check if intersection is valid (start < end) if (latest_start < earliest_end) { return TimeRange{latest_start, earliest_end}; } return std::nullopt; } std::string toString() const { return "[" + start.toString() + ", " + end.toString() + ")"; } }; // Represents a single linear segment of a time transformation. // Maps source_range to target_range. template struct MappedSegment { TimeRange source_range; TimeRange target_range; // Needed in case the ranges are not bounded on one side T slope; bool left_anchored; SegmentMarks::MaskType mask; MappedSegment(const TimeRange &src_r, const TimeRange &tgt_r, T slope, SegmentMarks::MaskType msk = SegmentMarks::NONE) : source_range(src_r), target_range(tgt_r), slope(slope), left_anchored(source_range.start.is_finite()), mask(msk) { // Cannot have mismatched infinite ends assert(source_range.start.is_finite() == target_range.start.is_finite()); assert(source_range.end.is_finite() == target_range.end.is_finite()); // Cannot have both ends infinite assert(source_range.start.is_finite() || source_range.end.is_finite()); assert(!std::isnan(slope)); } public: using DomainTime = time_units::TypedTime; using DomainDelta = time_units::TypedTimeDelta; using CodomainTime = time_units::TypedTime; using CodomainDelta = time_units::TypedTimeDelta; // Map point from domain to codomain within this segment [[nodiscard]] std::optional map_point(const DomainTime &p, bool include_end = false) const { if (!source_range.contains(p, include_end)) { return std::nullopt; } if (!std::isfinite(slope)) { return target_range.end; } else if (slope == 0.0) { return target_range.start; } if (left_anchored) { return CodomainDelta((slope * (p - source_range.start)).raw()) + target_range.start; } else { return CodomainDelta((slope * (p - source_range.end)).raw()) + target_range.end; } } // Map point from codomain back to domain within this segment [[nodiscard]] std::optional inverse_map_point(const CodomainTime &p, bool include_end = false) const { if (!target_range.contains(p, include_end)) { return std::nullopt; } if (!std::isfinite(slope)) { return source_range.start; } else if (slope == 0.0) { return source_range.end; } if (left_anchored) { return DomainDelta((p - target_range.start).raw() / slope) + source_range.start; } else { return DomainDelta((p - target_range.end).raw() / slope) + source_range.end; } } std::string toString() const { return "MappedSegment(" + source_range.toString() + " -> " + target_range.toString() + " m=" + std::to_string(slope) + " anchor=" + (left_anchored ? "left" : "right") + " mask=" + std::to_string(mask) + ")"; } bool has_mark(SegmentMarks::MaskType bit_to_check) const noexcept { return (mask & bit_to_check) != 0; } SegmentMarks::MaskType get_mask() const noexcept { return mask; } }; // Concept to ensure a type behaves like a time transformer with an iterator // interface template concept IsTimeTransformer = requires( const TT &t, TT &mutable_t, const typename TT::DomainTime &domain_time) { // Check for FromTime and ToTime type aliases that satisfy IsTypedTime typename TT::DomainTime; requires IsTypedTime; typename TT::CodomainTime; requires IsTypedTime; // Check for consistent value_type (e.g., both double) requires std::is_same_v; // Check for iterator type and begin/end methods typename TT::iterator; { mutable_t.begin() } -> std::same_as; // Check non-const begin { t.begin() } -> std::same_as; // Check const begin { mutable_t.end() } -> std::same_as; // Check non-const end { t.end() } -> std::same_as; // Check const end // Check for getSegmentIteratorAt (const version) { t.getSegmentIteratorAt(domain_time) } -> std::same_as; // Further checks on the iterator itself requires std::bidirectional_iterator< typename TT::iterator>; // Require bidirectional iterator requires std::same_as< typename std::iterator_traits::value_type, MappedSegment>; }; } // namespace time_transform