LUMIERA.clone/src/lib/time/timevalue.hpp

878 lines
27 KiB
C++
Raw Normal View History

/*
TIMEVALUE.hpp - basic definitions for time values and time intervals
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
Copyright (C)
2010, Hermann Vosseler <Ichthyostega@web.de>
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
  **Lumiera** is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version. See the file COPYING for further details.
*/
/** @file timevalue.hpp
** a family of time value like entities and their relationships.
** This is the foundation for the Lumiera time handling framework. On the implementation
** level, time values are represented as 64bit integer values `raw_time_64`, similar to
** and inspired by `gavl_time_t` from the raw-video handling [Lib-GAVL]. But for the
** actual use, we create several kinds of time "values", based on their logical properties.
** These time values are considered to be fixed (immutable) values, which may only be
** created through some limited construction paths, and any time based calculation
** is forced to go through our time calculation library. This is prerequisite for
** the definition of _frame aligned_ time values and time code representation
** implemented as display format based on these frame quantised time values.
**
** # Time entities
**
** The value types defined in this header represent time points and time intervals
** based on an internal time scale (µs ticks) and not related to any known fixed time
** zone or time base; rather they are interpreted in usage context, and the intended
** way to retrieve such a value is by formatting it into a time code format.
**
** The lib::time::TimeValue serves as foundation for all further time calculations;
** in fact it is implemented as a single 64bit µ-tick value (`raw_time_64`). The
** further time entities are implemented as value objects (without virtual functions):
** - lib::time::Time represents a time instant and is the reference for any usage
** - lib::time::TimeVar is a mutable time variable and can be used for calculations
** - lib::time::Offset can be used to express a positive or negative shift on time scale
** - lib::time::Duration represents the extension or an amount of time
** - lib::time::TimeSpan represents a distinct interval, with start time and duration
** - lib::time::FrameRate can be used to mark a number to denote a frames-per-second spec
** - lib::time::FSecs is a rational number to represent seconds or fractions thereof
**
** # Manipulating time values
**
** Time values are conceived as fixed, immutable entities, similar to numbers; you can't
** just change the number two, and likewise, two seconds are two seconds. However, for
** many use cases we have to combine time values to perform calculations
** - Time entities can be combined with operators, to form new time entities
** - the TimeVar can be used as accumulator or variable for ongoing calculations
** - since TimeSpan, Duration (and the grid-aligned, "quantised" flavours) will often
** represent some time-like property or entity, e.g. the temporal specification of
** a media Clip with start and duration, there is the concept of an explicit *mutation*,
** which is _accepted_ by these entities. Notably the lib::time::Control can be attached
** to these entities, and can then receive manipulations (nudging, offset); moreover it
** is possible to attach as listener to such a "controller" and be notified by any
** manipulation; this setup is the base for running time display, playback cursors etc.
**
** # Quantised time
**
** While these _internal time values_ can be considered _sufficiently precise,_ in practice
** any time specifications in the context of media handling will be aligned to some grid,
** and expressed in a _time code format._ Typically, we want to know the number of frames
** since playback started at the beginning of the timeline, and such a specification also
** relies on some implicitly known _frame rate_ (24fps for film in US, 25fps for film and
** TV in Europe, ...). By _deliberate choice,_ in Lumiera we *do not incorporate* such
** implicit assumptions into the actual time values. Rather, they need to be made explicitly
** in the relevant usage context. This is also the reason why the time entities defined in
** this header _do not offer an API_ to get the "real" time (whatever this means). Rather,
** the user of these time entities should get used to the concept that these abstract
** opaque values are the real thing, and a concrete, human readable time code is only
** a derivation, and any such derivation also incurs information loss. To reiterate that,
** _any time quantisation is a lossy information;_ grid aligned values are not "cleaner",
** they are just easier to handle for humans.
**
** \par how can I extract a time value?
** Taking the aforementioned into account, it depends on the context what to expect and to get
** - the standard path is to create a lib::time::QuTime by associating the internal time value
** with a pre-defined _time grid._ From there you can call QuTime::formatAs() to build an
** actual timecode instance, which can then be investigated or just printed.
** - for debugging purpose, lib::time::Time defines an `operator string()`, which breaks down
** the internal values into the format `-hh:mm:ss.mss`
** - advanced calculations with the need to access the implementation data in full precision
** should go through lib::time::TimeVar, which offers conversions to raw `int64_t` and the
** even more fine grained `FSec`, which is a rational (fraction) `boost::rational<int64_t>`
**
** @see time.h basic time calculation library functions
** @see timequant.hpp
** @see TimeValue_test
**
** [Lib-GAVL]: https://github.com/bplaum/gavl
*/
#ifndef LIB_TIME_TIMEVALUE_H
#define LIB_TIME_TIMEVALUE_H
#include "lib/error.hpp"
#include <boost/operators.hpp>
#include <boost/rational.hpp>
#include <compare>
#include <cstdlib>
#include <string>
namespace lib {
namespace time {
namespace error = lumiera::error;
2011-06-11 20:20:20 +02:00
// forwards...
class FrameRate;
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
class Duration;
2011-06-11 20:20:20 +02:00
class TimeSpan;
class Mutation;
/**
* Raw µ-tick time representation used in Lumiera.
* @remark this representation was inspired by [Lib-GAVL].
* @todo 2025 a mere type alias is up to debate -- very likely we'll use a wrapper soon /////////////////TICKET #1258
* @warning application logic should avoid handling any raw time value
2025-06-07 23:59:57 +02:00
* directly and rather treat time data as an opaque entity.
* [Lib-GAVL]: https://github.com/bplaum/gavl
*/
using raw_time_64 = int64_t;
/**
* basic constant internal time value.
* These time values provide the implementation base
* for all further time types. They can be created by
* wrapping up a raw micro tick value (raw_time_64),
* are totally ordered, but besides that,
* they are opaque and non-mutable.
* @note clients should prefer to use Time instances,
* which explicitly denote an Lumiera internal
* time value and are easier to use.
* @see TimeVar when full arithmetics are required
*/
class TimeValue
{
protected:
/** the raw (internal) time value
* used to implement the time types */
raw_time_64 t_;
/** Assigning of time values is not allowed,
* but derived classed might allow that */
TimeValue&
operator= (TimeValue const& o)
{
t_ = o.t_;
return *this;
}
/** some subclasses may receive modification messages */
friend class Mutation;
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
/** explicit limit of allowed time range */
static raw_time_64 limitedTime (raw_time_64 raw);
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
/** safe calculation of explicitly limited time offset */
static raw_time_64 limitedDelta (raw_time_64 origin, raw_time_64 target);
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
/** @internal for Offset and Duration entities built on top */
TimeValue (TimeValue const& origin, TimeValue const& target)
: t_{limitedDelta (origin.t_, target.t_)}
{ }
public:
/** Number of micro ticks (µs) per second as basic time scale */
static const raw_time_64 SCALE;
explicit
TimeValue (raw_time_64 val) ///< time given in µ ticks here
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
: t_{limitedTime (val)}
{ }
/** copy initialisation allowed */
TimeValue (TimeValue const& o)
: t_(o.t_)
{ }
/** @internal to pass Time values to C functions */
friend raw_time_64 _raw (TimeValue const& time) { return time.t_; }
friend HashVal hash_value (TimeValue const&);
static TimeValue buildRaw_(raw_time_64);
/** @internal diagnostics */
explicit operator std::string() const;
/** @return is in-domain, not a boundary value */
bool isRegular() const;
// Supporting strong total ordering
std::strong_ordering operator<=> (TimeValue const&) const =default;
bool operator== (TimeValue const&) const =default;
std::strong_ordering operator<=> (raw_time_64 tt) const { return t_ <=> tt; }
bool operator== (raw_time_64 tt) const { return t_ == tt; }
};
/* ======= specific Time entities ==================== */
/** relative framecount or frame number.
* Used within the engine at places where the underlying
* grid and origin is obvious from the call context.
* @warning do not mix up raw_time_64 and FrameCnt.
* @warning use 64bit consistently.
* beware: `long` is 32bit on i386
* @note any conversion to frame numbers should go through
* time quantisation followed by conversion to FrameNr
*/
using FrameCnt = int64_t;
/** rational representation of fractional seconds
* @warning do not mix up raw_time_64 and FSecs */
using FSecs = boost::rational<int64_t>;
/** a mutable time value,
* behaving like a plain number,
* allowing copy and re-accessing
* @note supports scaling by a factor,
* which _deliberately_ is chosen
* as int, not raw_time_64, because the
2011-01-27 14:35:46 +01:00
* multiplying of times is meaningless.
*/
class TimeVar
: public TimeValue
, boost::additive<TimeVar,
boost::additive<TimeVar, TimeValue,
boost::multipliable<TimeVar, int>
> >
{
public:
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
TimeVar (TimeValue const& time = TimeValue(0))
: TimeValue(time)
{ }
/** Allow to pick up precise fractional seconds
* @warning truncating fractional µ-ticks */
TimeVar (FSecs const&);
/// Allowing copy and assignment
TimeVar (TimeVar const& o)
: TimeValue(o)
{ }
TimeVar& operator= (TimeVar const&) = default;
TimeVar& operator= (TimeValue const& o)
{
*this = TimeVar(o);
return *this;
}
/// Support for micro-tick precise time arithmetics
operator FSecs() const { return FSecs{t_, TimeValue::SCALE}; }
/// Supporting additive
TimeVar& operator+= (TimeVar const& tx) { t_ += tx.t_; return *this; }
TimeVar& operator-= (TimeVar const& tx) { t_ -= tx.t_; return *this; }
/// Supporting multiplication with integral factor
TimeVar& operator*= (int64_t fact) { t_ *= fact; return *this; }
/// Supporting sign flip
TimeVar operator- () const { return TimeVar(*this)*=-1; }
// baseclass TimeValue is already totally_ordered
};
/**********************************************************//**
* Lumiera's internal time value datatype.
* This is a TimeValue, but now more specifically denoting
* a point in time, measured in reference to an internal
* (opaque) time scale.
*
* Lumiera Time provides some limited capabilities for
* direct manipulation; Time values can be created directly
* from `(ms,sec,min,hour)` specification and there is an
* string representation intended for internal use (reporting
* and debugging). Any real output, formatting and persistent
* storage should be based on the (quantised) timecode
* formats though, which can be generated from time values.
*
* Similar to TimeValue, also Time objects are considered
* immutable values. As convenience shortcut, some operators
* are provided, creating a TimVar for further calculations.
*/
class Time
: public TimeValue
{
/// direct assignment prohibited
Time& operator= (Time const);
/// suppress possible direct conversions
Time(int);
public:
static const Time MAX ;
static const Time MIN ;
static const Time ZERO;
static const Time ANYTIME; ///< border condition marker value. #ANYTIME <= any time value
static const Time NEVER; ///< border condition marker value. #NEVER >= any time value
explicit
Time (TimeValue const& val =TimeValue(0))
: TimeValue(val)
{ }
explicit
Time (FSecs const& fractionalSeconds);
Time (Time const&) = default;
Time (TimeVar const& calcResult)
: TimeValue(calcResult)
{ }
Time ( long millis
, uint secs
, uint mins =0
, uint hours=0
);
/** @internal diagnostics */
explicit operator std::string() const;
/** convenience start for time calculations */
TimeVar operator+ (TimeValue const& tval) const { return TimeVar(*this) + tval; }
TimeVar operator- (TimeValue const& tval) const { return TimeVar(*this) - tval; }
TimeVar operator- () const { return -TimeVar(*this); }
};
/**
* Offset measures a distance in time.
* It may be used to relate two points in time,
* or to create a modification for time-like entities.
* Similar to (basic) time values, offsets can be compared,
* but are otherwise opaque and immutable. Yet they allow
* to build derived values, including
* - the _absolute (positive) distance_ for this offset: #abs
* - a combined offset by chaining another offset
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
* @note on construction, Offset values are checked and limited
* to be within [-Duration::MAX ... +Duration::MAX]
*/
class Offset
: public TimeValue
{
2011-01-22 23:20:12 +01:00
protected:
2011-01-27 14:35:46 +01:00
/** generally immutable,
* but derived classes allow some limited mutation
* through special API calls */
2011-01-22 23:20:12 +01:00
Offset&
operator= (Offset const& o)
2011-01-22 23:20:12 +01:00
{
TimeValue::operator= (o);
return *this;
}
public:
explicit
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
Offset (TimeValue const& distance =Time::ZERO);
explicit
Offset (FSecs const& delta_in_secs);
Offset (Offset const&) = default;
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
Offset (FrameCnt count, FrameRate const& fps);
Offset (TimeValue const& origin, TimeValue const& target)
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
: TimeValue{origin, target}
{ }
2011-06-04 17:16:29 +02:00
static const Offset ZERO;
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
/** interpret the distance given by this offset as a time duration */
Duration abs() const;
2011-04-25 18:51:50 +02:00
/** @internal stretch offset by a possibly fractional factor,
* and quantise into raw (micro tick) grid */
Offset stretchedByRationalFactor (boost::rational<int64_t>) const;
Offset stretchedByFloatFactor (double) const;
/** @internal diagnostics, indicating ∆ */
explicit operator std::string() const;
2011-04-25 18:51:50 +02:00
// Supporting sign flip
Offset operator- () const;
};
//-- support linear offset chaining ---------------
inline Offset
operator+ (Offset const& start, Offset const& toChain)
{
TimeVar distance(start);
distance += toChain;
return Offset(distance);
}
inline Offset
operator- (Offset const& start, Offset const& toSubtract)
{
TimeVar distance(start);
distance -= toSubtract;
return Offset(distance);
}
template<typename FAC>
inline Offset
operator* (Offset const& distance, FAC factor)
{
return factor*distance;
}
template<typename INT>
inline Offset
operator* (INT factor, Offset const& o)
{
TimeVar distance(o);
distance *= factor;
return Offset(distance);
}
template<typename INTX>
inline Offset
operator* (boost::rational<INTX> factor, Offset const& offset)
{
return offset.stretchedByRationalFactor (boost::rational<int64_t>(factor.numerator(), factor.denominator()));
}
inline Offset
operator* (double factor, Offset const& offset)
{
return offset.stretchedByFloatFactor (factor);
}
/** flip offset direction */
2011-04-25 18:51:50 +02:00
inline Offset
Offset::operator- () const
{
return -1 * (*this);
2011-04-25 18:51:50 +02:00
}
2011-01-09 09:49:48 +01:00
/**
* Duration is the internal Lumiera time metric.
* It is an absolute (positive) value, but can be
* promoted from an offset. While Duration generally
* is treated as immutable value, there is the
* possibility to send a _Mutation message_.
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
* @note Duration relies on Offset being limited
*/
class Duration
: public TimeValue
{
2011-01-22 23:20:12 +01:00
/// direct assignment prohibited
Duration& operator= (Duration const&);
public:
Duration()
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
: TimeValue{Time::ZERO}
{ }
Duration (Offset const& distance)
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
: TimeValue{buildRaw_(llabs (_raw(distance)))}
{ }
2011-01-09 09:49:48 +01:00
explicit
Duration (TimeValue const& timeSpec)
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
: Duration{Offset{timeSpec}}
2011-01-09 09:49:48 +01:00
{ }
Duration (FSecs const& timeSpan_in_secs)
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
: Duration{Offset{timeSpan_in_secs}}
{ }
/** duration of the given number of frames.
* @note always positive; count used absolute */
Duration (FrameCnt count, FrameRate const& fps)
: Duration{Offset{count,fps}}
{ }
2011-01-09 09:49:48 +01:00
Duration (TimeSpan const& interval);
Duration (Duration const& o)
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
: TimeValue{o}
{// assuming that negative Duration can not be constructed....
REQUIRE (t_ >= 0, "Copy rejected: negative Duration %lu", o.t_);
}
static const Duration NIL;
static const Duration MAX ;
void accept (Mutation const&);
/** @internal diagnostics */
explicit operator std::string() const;
/// Supporting backwards use as offset
Offset operator- () const;
};
//-- support combining and Durations ---------------
inline Duration
operator+ (Duration const& base, Duration const& toAdd)
{
return Offset(base) + Offset(toAdd);
}
inline Duration
operator- (Duration const& base, Duration const& toRemove)
{
return base > toRemove? Offset(base) - Offset(toRemove)
: Duration::NIL;
}
template<typename NUM>
inline Offset
operator* (NUM factor, Duration const& dur)
{
return factor * Offset(dur);
}
template<typename NUM>
inline Offset
operator* (Duration const& dur, NUM factor)
{
return factor*dur;
}
inline Offset
Duration::operator- () const
{
return -1 * (*this);
}
/**
* A time interval anchored at a specific point in time.
* The start point of this timespan is also its nominal
* position, and the end point is normalised to happen
* never before the start point. A TimeSpan is enough
* to fully specify the temporal properties of an
* object within the model.
*
* As an exception to the generally immutable Time
* entities, a non constant TimeSpan may receive
* _mutation messages_, both for the start point
* and the duration. This allows for changing
* position and length of objects in the timeline.
*
* @todo define these mutations
*/
class TimeSpan
: public Time
{
Duration dur_;
public:
TimeSpan(TimeValue const& start, Duration const& length)
: Time(start)
, dur_(length)
{ }
TimeSpan(TimeValue const& start, FSecs(duration_in_secs))
: Time(start)
, dur_(duration_in_secs)
{ }
TimeSpan(TimeValue const& start, TimeValue const& end)
: Time(start<=end? start:end)
, dur_(Offset(start,end))
{ }
TimeSpan(TimeValue const& start, Offset const& reference_distance)
: TimeSpan{start, Time{start} + reference_distance}
{ }
TimeSpan()
: TimeSpan(Time::ZERO, Time::ZERO)
{ }
TimeSpan conform() const; ///< @return a copy conformed to time domain limits
static const TimeSpan ALL;
Duration&
duration()
{
return dur_;
}
2011-01-09 09:49:48 +01:00
Duration
duration() const
{
return dur_;
}
Time
start() const
{
return *this;
}
Time
2011-01-09 09:49:48 +01:00
end() const
{
return TimeVar(*this) += dur_;
}
bool
contains (TimeValue const& tp) const
{
return *this <= tp
2025-06-07 23:59:57 +02:00
and tp < end();
}
/** may change start / duration */
void accept (Mutation const&);
/** @internal diagnostics */
explicit operator std::string() const;
/// Supporting extended strong total ordering, based on start and interval length
std::strong_ordering
operator<=> (TimeSpan const& ts) const
{
auto ord{ t_ <=> ts.t_ };
return ord != 0? ord
: dur_ <=> ts.dur_;
}
bool operator== (TimeSpan const& ts) const =default;
};
/**
* Framerate specified as frames per second.
* Implemented as rational number.
*/
class FrameRate
: public boost::rational<uint>
{
public:
FrameRate (uint fps) ;
FrameRate (uint num, uint denom);
FrameRate (size_t count, Duration timeReference);
explicit
FrameRate (boost::rational<uint> fractionalRate);
static FrameRate approx(double fps);
// standard copy acceptable;
double asDouble() const;
static const FrameRate PAL;
static const FrameRate NTSC;
static const FrameRate STEP; ///< 1 frame per second
static const FrameRate HALTED;
/** duration of one frame */
Duration duration() const;
/** derive total ordering from base class */
std::strong_ordering operator<=>(FrameRate const&) const = default;
explicit operator std::string() const;
};
/** convenient conversion to duration in fractional seconds */
inline FSecs
operator/ (int n, FrameRate rate)
{
return FSecs{ n*rate.denominator(), rate.numerator()};
}
/* == implementations == */
namespace { // implementation helpers...
template<typename NUM>
inline NUM
__ensure_nonzero (NUM n)
{
if (n == NUM{0})
throw error::Logic ("Degenerated frame grid not allowed"
, LERR_(BOTTOM_VALUE));
return n;
}
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
inline raw_time_64
symmetricLimit (raw_time_64 raw, TimeValue lim)
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
{
return raw > lim? _raw(lim)
: -raw > lim? -_raw(lim)
: raw;
}
}//(End) implementation helpers
/** derive a hash from the µ-tick value
* @return rotation of the raw value to produce a suitable spacing for consecutive time
* @remark picked up by Boost-hash, or std. hashtables with the help of `hash-standard.h`
* @see https://stackoverflow.com/a/31488147
*/
inline HashVal
hash_value (TimeValue const& time)
{
HashVal x = _raw(time); // possibly cap to size of hash
const uint width = sizeof(HashVal) * CHAR_BIT;
const uint mask = width-1;
const uint n = width / 2;
static_assert (0 < n and n <= mask);
return (x<<n) | (x>>((-n)&mask ));
}
/** @internal applies a limiter on the provided
* raw time value to keep it within the arbitrary
* boundaries defined by (Time::MAX, Time::MIN).
* While Time entities are \c not a "safeInt"
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
* implementation, we limit new values to
* lower the likelihood of wrap-around */
inline raw_time_64
TimeValue::limitedTime (raw_time_64 raw)
{
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
return symmetricLimit (raw, Time::MAX);
}
inline raw_time_64
TimeValue::limitedDelta (raw_time_64 origin, raw_time_64 target)
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
{
if (0 > (origin^target))
{// prevent possible numeric wrap
origin = symmetricLimit (origin, Duration::MAX);
target = symmetricLimit (target, Duration::MAX);
}
raw_time_64 res = target - origin;
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
return symmetricLimit (res, Duration::MAX);
}
inline TimeSpan
TimeSpan::conform() const ///< @note: implicitly capped to Duration::MAX
{
Offset extension{dur_};
TimeValue start{_raw(*this)};
return Offset{start} + extension > Time::MAX? TimeSpan{Time::MAX-extension, Time::MAX}
: TimeSpan{start, extension};
}
inline bool
TimeValue::isRegular() const
{
return Time::MIN < *this
and *this < Time::MAX;
}
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
inline
TimeVar::TimeVar (FSecs const& fractionalSeconds)
: TimeVar{Time(fractionalSeconds)}
{ }
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
inline
Offset::Offset (TimeValue const& distance)
: TimeValue{buildRaw_(symmetricLimit(_raw(distance)
, Duration::MAX))}
{ }
2011-01-09 09:49:48 +01:00
inline
Duration::Duration (TimeSpan const& interval)
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
: Duration{interval.duration()}
2011-01-09 09:49:48 +01:00
{ }
inline
FrameRate::FrameRate (uint fps)
: boost::rational<uint> (__ensure_nonzero(fps))
{ }
inline
FrameRate::FrameRate (uint num, uint denom)
: boost::rational<uint> (__ensure_nonzero(num), denom)
{ }
inline
FrameRate::FrameRate (boost::rational<uint> fractionalRate)
: boost::rational<uint> (__ensure_nonzero(fractionalRate))
{ }
boost::rational<uint> __framerate_approximation (size_t, Duration);
boost::rational<uint> __framerate_approximation (double);
inline
FrameRate::FrameRate (size_t count, Duration timeReference)
: FrameRate{__framerate_approximation (count, timeReference)}
{ }
inline FrameRate
FrameRate::approx (double fps)
{
return FrameRate{__framerate_approximation (fps)};
}
inline double
FrameRate::asDouble() const
{
return boost::rational_cast<double> (*this);
}
Library: rectify clipping of time::Duration (see #1263) This is a deep refactoring to allow to represent the distance between all valid time points as a time::Offset or time::Duration. By design this is possible, since Time::MAX was defined as 1/30 of the maximum value technically representable as int64_t. However, introducing a different limiter for offsets and durations turns out difficult, due to the inconsistencies in the exiting hierarchy of temporal entities. Which in turn seems to stem from the unfortunate decision to make time entities immutable, see #1261 Since the limiter is hard wired into the `time::TimeValue` constructor, we are forced to create a "backdoor" of sorts, to pass up values with different limiting from child classes. This would not be so much of a problem if calculations weren't forced to go through `TimeVar`, which does not distinguish between time points and time durations. This solution rearranges all checks to be performed now by time::Offset, while time::Duration will only take the absolute value at construction, based on the fact that there is no valid construction path to yield a duration which does not go through an offset first. Later, when we're ready to sort out the implementation base of time values (see #1258), this design issue should be revisited - either we'll allow derived classes explicitly to invoke the limiter functions - or we may be able to have an automatic conversion path from clearly marked base implementation types, in which case we wouldn't use the buildRaw_() and _raw() "backdoor" functions any more...
2022-12-05 00:58:32 +01:00
inline Duration
Offset::abs() const
{
return Duration{*this};
}
}} // lib::time
namespace util {
inline bool
isnil (lib::time::Duration const& dur)
{
return 0 == dur;
}
// repeated or forward declaration, see meta/util.hpp
template<typename X, typename COND>
struct StringConv;
/** specialisation: render fractional seconds (for diagnostics) */
template<>
struct StringConv<lib::time::FSecs, void>
{
static std::string
invoke (lib::time::FSecs) noexcept;
};
}
#endif /*LIB_TIME_TIMEVALUE_H*/