Timeline: reconsider time handling and Stage/Steam integration
This ZoomWindow_test highlights again the question about the intended usage of the Lumiera time entities. In which way do we want to perform time calculations, and under which circumstances is it adequate to perform arithmetic on raw time values? These questions made me think about rather far reaching concerns regarding subsidiarity and implicit or explicit usage context. Basically I could reconfirm the design choices taken some years ago -- while I must admit that the project is headed towards a way larger scale and more loose coupling of the parts, than I could imagine several years ago, at the time when the design started... As a side note: we can not avoid that some knowledge about the time implementation leaks out from the support lib; time codes themselves are tightly coupled to the usage scenario within the session and can not be used as means for implementing UI concerns. And the more generic time frameworks, like std::chrono (as much as it is desirable to have some integration here) will not be of any help for most of our specific usage patterns. The reason is, for film editing we do not have a global time scale, rather the truth is when the film starts....
This commit is contained in:
parent
7145d0d9ce
commit
f1b3f4e666
12 changed files with 316 additions and 104 deletions
|
|
@ -28,9 +28,9 @@
|
|||
**
|
||||
** Built on top of that, the actual time handling in the GUI and within the Lumiera
|
||||
** session is mostly confined to use the opaque lib::time::Time wrapper objects.
|
||||
** When time values actually need to be \em quantised (aligned to a frame grid),
|
||||
** When time values actually need to be _quantised_ (aligned to a frame grid),
|
||||
** this is expressed at the API through using the lib::time::QuTime type, which
|
||||
** then in turn can be materialised into a number of \em timecode formats.
|
||||
** then in turn can be materialised into a number of _timecode formats_.
|
||||
** These definitions ensure that whenever an actual quantisation (rounding)
|
||||
** operation is performed, the link to the appropriate time grid is available,
|
||||
** so that multiple output or rendering operations can use differing time origins
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ namespace time {
|
|||
|
||||
|
||||
class Quantiser; // API for grid aligning
|
||||
typedef Quantiser const& QuantR;
|
||||
typedef std::shared_ptr<const Quantiser> PQuant;
|
||||
using QuantR = Quantiser const&;
|
||||
using PQuant = std::shared_ptr<const Quantiser>;
|
||||
|
||||
|
||||
namespace format {
|
||||
|
|
@ -147,25 +147,25 @@ namespace time {
|
|||
template<>
|
||||
struct Traits<Frames>
|
||||
{
|
||||
typedef FrameNr TimeCode;
|
||||
using TimeCode = FrameNr;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Traits<Smpte>
|
||||
{
|
||||
typedef SmpteTC TimeCode;
|
||||
using TimeCode = SmpteTC;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Traits<Hms>
|
||||
{
|
||||
typedef HmsTC TimeCode;
|
||||
using TimeCode = HmsTC;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Traits<Seconds>
|
||||
{
|
||||
typedef Secs TimeCode;
|
||||
using TimeCode = Secs;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
#include "lib/time.h"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/util-quant.hpp"
|
||||
#include "lib/format-string.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "lib/tmpbuf.h"
|
||||
|
|
@ -66,13 +67,11 @@ using lib::time::FrameRate;
|
|||
using boost::rational_cast;
|
||||
using boost::lexical_cast;
|
||||
|
||||
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace meta {
|
||||
extern const std::string FAILURE_INDICATOR;
|
||||
|
|
@ -92,11 +91,13 @@ namespace time {
|
|||
const Time Time::NEVER (Time::MAX);
|
||||
|
||||
const Offset Offset::ZERO (Time::ZERO);
|
||||
|
||||
Literal DIAGNOSTIC_FORMAT{"%s%01d:%02d:%02d.%03d"};
|
||||
|
||||
|
||||
/** scale factor _used locally within this implementation header_.
|
||||
* GAVL_TIME_SCALE rsp. TimeValue::SCALE is the correct factor or dividend when using gavl_time_t
|
||||
* for units of whole seconds from gavl_time_t. Since we want to use milliseconds,
|
||||
* GAVL_TIME_SCALE rsp. TimeValue::SCALE is the correct factor or dividend when using
|
||||
* gavl_time_t for display on a scale with seconds. Since we want to use milliseconds,
|
||||
* we need to multiply or divide by 1000 to get correct results. */
|
||||
#define TIME_SCALE_MS (lib::time::TimeValue::SCALE / 1000)
|
||||
|
||||
|
|
@ -112,7 +113,7 @@ namespace time {
|
|||
* any further adjustments.
|
||||
*/
|
||||
Time::Time ( long millis
|
||||
, uint secs
|
||||
, uint secs
|
||||
, uint mins
|
||||
, uint hours
|
||||
)
|
||||
|
|
@ -129,18 +130,6 @@ namespace time {
|
|||
{ }
|
||||
|
||||
|
||||
/** display an internal Lumiera Time value
|
||||
* for diagnostic purposes or internal reporting.
|
||||
* @warning internal Lumiera time values refer to an
|
||||
* implementation dependent time origin/scale.
|
||||
* @return string rendering of the actual, underlying
|
||||
* implementation value, as `h:m:s:ms`
|
||||
*/
|
||||
Time::operator string() const
|
||||
{
|
||||
return string (lumiera_tmpbuf_print_time (t_));
|
||||
}
|
||||
|
||||
/** @note recommendation is to use TCode for external representation
|
||||
* @remarks this is the most prevalent internal diagnostics display
|
||||
* of any "time-like" value, it is meant to be compact. */
|
||||
|
|
@ -161,6 +150,41 @@ namespace time {
|
|||
;
|
||||
}
|
||||
|
||||
/** display an internal Lumiera Time value
|
||||
* for diagnostic purposes or internal reporting.
|
||||
* Format is `-hh:mm:ss.mss`
|
||||
* @warning internal Lumiera time values refer to an
|
||||
* implementation dependent time origin/scale.
|
||||
* @return string rendering of the actual, underlying
|
||||
* implementation value, as `h:m:s:ms`
|
||||
*/
|
||||
Time::operator string() const
|
||||
{
|
||||
gavl_time_t time = t_;
|
||||
int millis, seconds, minutes, hours;
|
||||
bool negative = (time < 0);
|
||||
|
||||
if (negative)
|
||||
time = -time;
|
||||
|
||||
time /= TIME_SCALE_MS;
|
||||
millis = time % 1000;
|
||||
time /= 1000;
|
||||
seconds = time % 60;
|
||||
time /= 60;
|
||||
minutes = time % 60;
|
||||
time /= 60;
|
||||
hours = time;
|
||||
|
||||
return util::_Fmt{string(DIAGNOSTIC_FORMAT)}
|
||||
% (negative? "-":"")
|
||||
% hours
|
||||
% minutes
|
||||
% seconds
|
||||
% millis;
|
||||
}
|
||||
|
||||
|
||||
Offset::operator string() const
|
||||
{
|
||||
return (t_< 0? "" : "∆")
|
||||
|
|
@ -174,15 +198,15 @@ namespace time {
|
|||
|
||||
TimeSpan::operator string() const
|
||||
{
|
||||
return string (lumiera_tmpbuf_print_time (t_))
|
||||
+ string (dur_);
|
||||
return string (start())
|
||||
+ string (duration());
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
template<typename INT>
|
||||
template<typename RAT>
|
||||
string
|
||||
renderFraction (INT const& frac, Literal postfx) noexcept
|
||||
renderFraction (RAT const& frac, Literal postfx) noexcept
|
||||
try {
|
||||
std::ostringstream buffer;
|
||||
if (1 == frac.denominator() or 0 == frac.numerator())
|
||||
|
|
@ -299,7 +323,7 @@ lumiera_tmpbuf_print_time (gavl_time_t time)
|
|||
time /= 60;
|
||||
hours = time;
|
||||
|
||||
char *buffer = lumiera_tmpbuf_snprintf(64, "%s%01d:%02d:%02d.%03d",
|
||||
char *buffer = lumiera_tmpbuf_snprintf(64, lib::time::DIAGNOSTIC_FORMAT,
|
||||
negative ? "-" : "", hours, minutes, seconds, milliseconds);
|
||||
|
||||
ENSURE(buffer != NULL);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,34 @@
|
|||
** And it offers a dedicated API to "materialise" this (still complete and
|
||||
** precise) time value into an external representation.
|
||||
**
|
||||
** # Collaborations
|
||||
**
|
||||
** lib::time::Time is the ubiquitous yet opaque internal time "instant" used
|
||||
** in Lumiera to designate time points (and by extension also a TimeSpan).
|
||||
** What such an _internal time instant_ actually means, depends on the context,
|
||||
** yet it is the implementation's (not the user's) responsibility to keep that
|
||||
** meaning straight.
|
||||
**
|
||||
** However, by creating a [Quantised Time](\ref lib::time::QuTime), the association
|
||||
** to some time scale or time grid is made explicit; the time value within QuTime
|
||||
** is stored in full precision though, without performing any "rounding" yet.
|
||||
**
|
||||
** Only by building or casting into a _time code,_ the actual quantisation is
|
||||
** performed, which means to align the time value to the next grid point and
|
||||
** discard excess timing information. There are several [time formats](\ref formats.hpp)
|
||||
** available to choose from, like Hour-Minute-Second, or SMPTE (h-m-s + frame) or
|
||||
** fractional seconds or just a frame count with implicit frame rate.
|
||||
**
|
||||
** How the _quantisation_ or _grid alignment_ is actually performed is up to the
|
||||
** discretion of the lib::time::Quantiser; an actually Quantiser instance also
|
||||
** represents a common notion of some specific time grid, together with a usage
|
||||
** context. Notably, a "25 fps grid" means to start timing when the playback
|
||||
** starts, and to proceed in steps of 1/25sec. Conceptually (not-yet-implemented
|
||||
** as of 11/2022) a frame grid could also imply to anchor the time at some global
|
||||
** time zone, allowing to integrate _live content_ into the timeline -- and the
|
||||
** actual link to wall-clock time would be made explicit by some _placement_
|
||||
** somewhere on the actual timeline.
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -60,13 +88,13 @@ namespace time {
|
|||
* relative to the defined time scale. Usually this time scale
|
||||
* exists already in the Lumiera session and is referred simply
|
||||
* by symbolic ID, it will be fetched on demand through the
|
||||
* \link advice.hpp advice system.\endlink
|
||||
* [advice system](\ref advice.hpp).
|
||||
*
|
||||
* By creating a QuTime value, the relation to such a predefined
|
||||
* time scale is made explicit. This doesn't change the internal
|
||||
* time value, but the actual creation of a timecode formatted
|
||||
* value (#formatAs) usually implies to quantise or grid align
|
||||
* the time to the frame grid specific to this time scale.
|
||||
* the time to the frame grid specific to this time scale.
|
||||
*/
|
||||
class QuTime
|
||||
: public Time
|
||||
|
|
@ -74,21 +102,21 @@ namespace time {
|
|||
PQuant quantiser_;
|
||||
|
||||
public:
|
||||
QuTime (TimeValue raw, Symbol gridID); ///< @note defined in common-services.cpp
|
||||
QuTime (TimeValue raw, Symbol gridID); ///< @note defined in common-services.cpp
|
||||
QuTime (TimeValue raw, PQuant quantisation_to_use);
|
||||
|
||||
operator PQuant() const;
|
||||
|
||||
operator PQuant() const; ///< @return `shared-ptr` to the associated time grid (or time scale)
|
||||
|
||||
template<class FMT>
|
||||
bool supports() const;
|
||||
bool supports() const; ///< does our implicit time grid support building that timecode format?
|
||||
|
||||
template<class FMT>
|
||||
typename format::Traits<FMT>::TimeCode
|
||||
formatAs() const;
|
||||
formatAs() const; ///< create new time code instance, then #castInto
|
||||
|
||||
template<class TC>
|
||||
void
|
||||
castInto (TC& timecode) const;
|
||||
castInto (TC& timecode) const; ///< quantise into implicit grid, then rebuild the timecode
|
||||
|
||||
/** receive change message, which
|
||||
might cause re-quantisation */
|
||||
|
|
@ -118,16 +146,25 @@ namespace time {
|
|||
inline typename format::Traits<FMT>::TimeCode
|
||||
QuTime::formatAs() const
|
||||
{
|
||||
typedef typename format::Traits<FMT>::TimeCode TC;
|
||||
using TC = typename format::Traits<FMT>::TimeCode;
|
||||
return TC(*this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @tparam TC the kind of time code to use as target
|
||||
* @param timecode existing instance of that timecode, to be overwritten
|
||||
* @remark this is the basic operation to convert an (internal) time value
|
||||
* into a time code format. QuTime is already associated with some
|
||||
* _time grid_ for quantisation, but the internal value is precise.
|
||||
* This operation creates a quantised (frame aligned) transient copy,
|
||||
* and uses this to adjust/modify the fields in the given timecode.
|
||||
*/
|
||||
template<class TC>
|
||||
inline void
|
||||
QuTime::castInto (TC& timecode) const
|
||||
{
|
||||
typedef typename TC::Format Format;
|
||||
using Format = typename TC::Format;
|
||||
REQUIRE (supports<Format>());
|
||||
|
||||
Format::rebuild (timecode, *quantiser_, TimeValue(*this));
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@
|
|||
**
|
||||
** 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 only way
|
||||
** to retrieve such a value is by formatting it into a time code format.
|
||||
** 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 (`gavl_time_t`). The
|
||||
|
|
@ -64,6 +64,34 @@
|
|||
** 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
|
||||
|
|
@ -178,15 +206,15 @@ namespace time {
|
|||
* grid and origin is obvious from the call context.
|
||||
* @warning do not mix up gavl_time_t and FrameCnt.
|
||||
* @warning use 64bit consistently.
|
||||
* beware: \c long is 32bit on i386
|
||||
* beware: `long` is 32bit on i386
|
||||
* @note any conversion to frame numbers should go through
|
||||
* time quantisation followed by conversion to FrameNr
|
||||
*/
|
||||
typedef int64_t FrameCnt;
|
||||
using FrameCnt = int64_t;
|
||||
|
||||
/** rational representation of fractional seconds
|
||||
* @warning do not mix up gavl_time_t and FSecs */
|
||||
typedef boost::rational<int64_t> FSecs;
|
||||
using FSecs = boost::rational<int64_t>;
|
||||
|
||||
|
||||
|
||||
|
|
@ -319,7 +347,7 @@ namespace time {
|
|||
* Similar to (basic) time values, offsets can be compared,
|
||||
* but are otherwise opaque and immutable. Yet they allow
|
||||
* to build derived values, including
|
||||
* - the \em absolute (positive) distance for this offset
|
||||
* - the _absolute (positive) distance_ for this offset: #abs
|
||||
* - a combined offset by chaining another offset
|
||||
*/
|
||||
class Offset
|
||||
|
|
@ -500,7 +528,7 @@ namespace time {
|
|||
*
|
||||
* As an exception to the generally immutable Time
|
||||
* entities, a non constant TimeSpan may receive
|
||||
* \em mutation messages, both for the start point
|
||||
* _mutation messages_, both for the start point
|
||||
* and the duration. This allows for changing
|
||||
* position and length of objects in the timeline.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -26,10 +26,7 @@
|
|||
** This header defines the basics of...
|
||||
**
|
||||
** @note as of X/2014 this is complete bs
|
||||
** @todo WIP ///////////////////////TICKET #
|
||||
**
|
||||
** @see ////TODO_test usage example
|
||||
** @see diagnostics.cpp implementation
|
||||
** @todo planned as of 11/2022 ////////////////////////////////////////////////////////////////////////////TICKET #1257 : provide test-session for GUI
|
||||
**
|
||||
*/
|
||||
|
||||
|
|
@ -78,43 +75,8 @@ namespace model {
|
|||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* == Adapter interface for == */
|
||||
|
||||
void
|
||||
setSolution (string const& solution ="")
|
||||
{
|
||||
UNIMPLEMENTED ("tbw");
|
||||
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #888
|
||||
if (isDeaf())
|
||||
this->transmogrify (solution);
|
||||
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #888
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
void maybe () const;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** @internal in case
|
||||
*/
|
||||
inline void
|
||||
Diagnostics::maybe () const
|
||||
{
|
||||
UNIMPLEMENTED ("tbw");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}} // namespace stage::model
|
||||
#endif /*STAGE_MODEL_DIAGNOSTICS_H*/
|
||||
|
|
|
|||
|
|
@ -128,9 +128,9 @@ namespace model {
|
|||
uint px_per_sec_;
|
||||
|
||||
public:
|
||||
ZoomWindow()
|
||||
: startAll_{Time::ZERO}
|
||||
, afterAll_{Time(0,23)}
|
||||
ZoomWindow (TimeSpan timeline =TimeSpan{Time::ZERO, FSecs(23)})
|
||||
: startAll_{timeline.start()}
|
||||
, afterAll_{nonEmpty(timeline.end())}
|
||||
, startWin_{startAll_}
|
||||
, afterWin_{afterAll_}
|
||||
, px_per_sec_{25}
|
||||
|
|
@ -163,6 +163,16 @@ namespace model {
|
|||
UNIMPLEMENTED ("setMetric");
|
||||
}
|
||||
|
||||
/**
|
||||
* scale up or down on a 2-logarithmic scale.
|
||||
* Each step either doubles or halves the zoom level,
|
||||
* and the visible window is adjusted accordingly, using
|
||||
* the current #anchorPoint as centre for scaling.
|
||||
* @note the zoom factor is limited to be between
|
||||
* 2px per µ-tick and 1px per second (~20min
|
||||
* on a typical 1280 monitor)
|
||||
* @todo support for overview mode ////////////////////////////////////////////////////////////////////TICKET #1255 : implement overview mode
|
||||
*/
|
||||
void
|
||||
nudgeMetric (int steps)
|
||||
{
|
||||
|
|
@ -201,7 +211,7 @@ namespace model {
|
|||
void
|
||||
setVisibleRange (TimeSpan newWindow)
|
||||
{
|
||||
UNIMPLEMENTED ("setVisibleRange");
|
||||
mutateWindow (newWindow.start(), newWindow.end());
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -237,7 +247,9 @@ namespace model {
|
|||
void
|
||||
nudgeVisiblePos (int steps)
|
||||
{
|
||||
UNIMPLEMENTED ("nudgeVisiblePos");
|
||||
FSecs dur{afterWin_-startWin_}; // navigate half window steps
|
||||
setVisibleRange (TimeSpan{Time{startWin_ + dur*steps/2}
|
||||
, dur});
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -249,12 +261,59 @@ namespace model {
|
|||
private:
|
||||
/* === adjust and coordinate === */
|
||||
|
||||
void
|
||||
mutateWindow (TimeValue start, TimeValue after)
|
||||
TimeValue
|
||||
nonEmpty (TimeValue endPoint)
|
||||
{
|
||||
UNIMPLEMENTED ("change Window TimeSpan, validate and adjust all params");
|
||||
if (startAll_ < endPoint)
|
||||
return endPoint;
|
||||
if (startAll_ < Time::MAX)
|
||||
return TimeValue{startAll_ + 1};
|
||||
startAll_ = Time::MAX - TimeValue(1);
|
||||
return Time::MAX;
|
||||
}
|
||||
|
||||
|
||||
/** @internal change Window TimeSpan, validate and adjust all params */
|
||||
void
|
||||
mutateWindow (TimeVar start, TimeVar after)
|
||||
{
|
||||
if (not (start < after))
|
||||
{
|
||||
if (after == Time::MAX)
|
||||
start = Time::MAX - TimeValue(1);
|
||||
else
|
||||
after = start + TimeValue(1);
|
||||
}
|
||||
|
||||
FSecs dur{after - start};
|
||||
if (dur > FSecs{afterAll_ - startAll_})
|
||||
{
|
||||
start = startAll_;
|
||||
after = afterAll_;
|
||||
}
|
||||
else
|
||||
if (start < startAll_)
|
||||
{
|
||||
start = startAll_;
|
||||
after = start + dur;
|
||||
}
|
||||
else
|
||||
if (after > afterAll_)
|
||||
{
|
||||
after = afterAll_;
|
||||
start = after - dur;
|
||||
}
|
||||
ASSERT (after-start <= afterAll_-startAll_);
|
||||
|
||||
px_per_sec_ = adjustedScale (start,after, startWin_,afterWin_);
|
||||
startWin_ = start;
|
||||
afterWin_ = after;
|
||||
fireChangeNotification();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal adjust Window to match given scale,
|
||||
* validate and adjust all params */
|
||||
void
|
||||
mutateScale (uint px_per_sec)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
TEST "Suppport for specific timecode formats" FormatSupport_test <<END
|
||||
TEST "Support for specific timecode formats" FormatSupport_test <<END
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
|
|
|||
|
|
@ -77,8 +77,7 @@ namespace test {
|
|||
}
|
||||
|
||||
|
||||
/** @test the standard use case is to.... TBW
|
||||
*/
|
||||
/** @test simple usage example: double the zoom level, then scroll to the left */
|
||||
void
|
||||
verify_simpleUsage()
|
||||
{
|
||||
|
|
@ -91,6 +90,11 @@ namespace test {
|
|||
CHECK (zoomWin.px_per_sec() == 50);
|
||||
CHECK (zoomWin.visible() == TimeSpan(Time(FSecs(23,4)), FSecs(23,2)));
|
||||
CHECK (zoomWin.overallSpan() == TimeSpan(Time::ZERO, Time(FSecs(23))));
|
||||
|
||||
zoomWin.nudgeVisiblePos(-1);
|
||||
CHECK (zoomWin.px_per_sec() == 50);
|
||||
CHECK (zoomWin.visible() == TimeSpan(Time::ZERO, FSecs(23,2)));
|
||||
CHECK (zoomWin.overallSpan() == TimeSpan(Time::ZERO, Time(FSecs(23))));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@
|
|||
** @see DiffTreeApplication_test
|
||||
** @see DiffComplexApplication_test
|
||||
**
|
||||
** @todo as of 11/2022 this is still a mere placeholder; we need a test setup for
|
||||
** proper UI integration testing (without actually launching the UI) ////////////////////////////////TICKET #1257 : provide test-session for GUI
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
|
|
@ -83,6 +86,8 @@ namespace test {
|
|||
* - it is the responsibility of the "Gui Model" (#SessionFacade)
|
||||
* to ensure a read barrier, so the retrieved data can not be
|
||||
* corrupted by concurrent session mutation operations.
|
||||
* @todo 11/2022 this was an early draft, we need a way to inject
|
||||
* standard test session content /////////////////////////////////////////////////////TICKET #1257 : provide test-session for GUI
|
||||
*
|
||||
* @see SessionElementQuery_test
|
||||
* @see stage::model::SessionFacade
|
||||
|
|
@ -104,7 +109,7 @@ namespace test {
|
|||
void
|
||||
retrieveSessionStructure ()
|
||||
{
|
||||
|
||||
TODO ("Ticket #1257"); /////////////////////////////////////////////////////TICKET #1257 : provide test-session for GUI
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ namespace test{
|
|||
* of the UI, not perform a remote controlled execution of the UI. Thus we
|
||||
* still create a commandline executable, but for this code to work, it
|
||||
* needs to be _linked against the GUI plugin_ (which is in fact a shared library)
|
||||
* @todo as of 11/2022 this is still a mere placeholder; we need a test setup for
|
||||
* proper UI integration testing (without actually launching the UI) ////////////////////////////////TICKET #1257 : provide test-session for GUI
|
||||
*/
|
||||
class TestGui_test : public Test
|
||||
{
|
||||
|
|
|
|||
|
|
@ -101,6 +101,11 @@
|
|||
<arrowlink COLOR="#fee998" DESTINATION="ID_176564515" ENDARROW="Default" ENDINCLINATION="-1211;-7367;" ID="Arrow_ID_157207334" STARTARROW="None" STARTINCLINATION="-617;103;"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1523746656723" ID="ID_1022913151" MODIFIED="1667166830055" TEXT="#1140 UI Integration Testing">
|
||||
<arrowlink COLOR="#eff1e5" DESTINATION="ID_435582808" ENDARROW="Default" ENDINCLINATION="-1920;189;" ID="Arrow_ID_1348453991" STARTARROW="None" STARTINCLINATION="498;45;"/>
|
||||
<arrowlink COLOR="#eff1e5" DESTINATION="ID_406286329" ENDARROW="Default" ENDINCLINATION="-2304;344;" ID="Arrow_ID_1832343822" STARTARROW="None" STARTINCLINATION="498;45;"/>
|
||||
<icon BUILTIN="hourglass"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1538938685096" HGAP="138" ID="ID_511824194" MODIFIED="1557498707215" TEXT="gründen" VSHIFT="42">
|
||||
<node CREATED="1477784793993" ID="ID_1868522177" MODIFIED="1566487594554" TEXT="Umbau">
|
||||
|
|
@ -16184,6 +16189,7 @@
|
|||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<linktarget COLOR="#eff1e5" DESTINATION="ID_435582808" ENDARROW="Default" ENDINCLINATION="-1920;189;" ID="Arrow_ID_1348453991" SOURCE="ID_1022913151" STARTARROW="None" STARTINCLINATION="498;45;"/>
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1523746659654" ID="ID_1299891788" MODIFIED="1523746698882" TEXT="Das Ziel war: alle konkreten Standard-Fälle durchspielen">
|
||||
|
|
@ -16207,10 +16213,13 @@
|
|||
<node COLOR="#435e98" CREATED="1523748895111" ID="ID_613307109" MODIFIED="1523748953311" TEXT="später mal....">
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="idea"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1523748900655" ID="ID_1768095597" MODIFIED="1523748939872" TEXT="#1140 UI integration tests">
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1523748900655" ID="ID_1768095597" MODIFIED="1667166529999" TEXT="#1140 UI integration tests">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1523748905725" ID="ID_748849132" MODIFIED="1523748935631" TEXT="#1141 verify UI default view allocation">
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1523748905725" ID="ID_748849132" MODIFIED="1667166529999" TEXT="#1141 verify UI default view allocation">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1667166492738" ID="ID_1938503803" MODIFIED="1667166529999" TEXT="#1257 provide test-session for GUI">
|
||||
<icon BUILTIN="hourglass"/>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -17118,7 +17127,10 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1523055378032" ID="ID_1727349685" MODIFIED="1525124215035" TEXT="Test-Hilfsmittel">
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1523055378032" ID="ID_406286329" MODIFIED="1525124215035" TEXT="Test-Hilfsmittel">
|
||||
<linktarget COLOR="#eff1e5" DESTINATION="ID_406286329" ENDARROW="Default" ENDINCLINATION="-2304;344;" ID="Arrow_ID_1832343822" SOURCE="ID_1022913151" STARTARROW="None" STARTINCLINATION="498;45;"/>
|
||||
<font NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="hourglass"/>
|
||||
<node CREATED="1523055393950" ID="ID_62932320" MODIFIED="1523055412955" TEXT="TestElementAccess">
|
||||
<icon BUILTIN="forward"/>
|
||||
</node>
|
||||
|
|
@ -38358,7 +38370,7 @@
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1666913370781" ID="ID_1691713188" MODIFIED="1666966354349" TEXT="klassisches Klassen-Design">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1666913375117" ID="ID_1377108042" MODIFIED="1666966405674" TEXT="ZoomWindow ist kopierbare Klasse mit internem State"/>
|
||||
<node CREATED="1666913375117" ID="ID_1377108042" MODIFIED="1667158257168" TEXT="ZoomWindow ist unkopierbare Klasse mit internem State"/>
|
||||
<node CREATED="1666913471725" ID="ID_1373767277" MODIFIED="1666966473259" TEXT="Master-Setter führen die Range-Chacks durch"/>
|
||||
<node CREATED="1666966477745" ID="ID_1255941758" MODIFIED="1666966489636" TEXT="sekundäre / convenience-Setter bieten"/>
|
||||
<node CREATED="1666966493207" ID="ID_872298278" MODIFIED="1666966503634" TEXT="interner State in 4 TimeVar"/>
|
||||
|
|
@ -38386,6 +38398,85 @@
|
|||
<node CREATED="1667093106907" ID="ID_1255657275" MODIFIED="1667093118644" TEXT="nudge Zoom-Faktor"/>
|
||||
<node CREATED="1667093120846" ID="ID_1405458968" MODIFIED="1667093134771" TEXT="⟹ doppelte Auflösung"/>
|
||||
<node CREATED="1667093136007" ID="ID_1309995367" MODIFIED="1667093153233" TEXT="⟹ visible Window liegt in der Mitte und hat hable Länge"/>
|
||||
<node CREATED="1667163315776" ID="ID_1054488196" MODIFIED="1667163336658" TEXT="nudge Position ⟵"/>
|
||||
<node CREATED="1667093136007" ID="ID_1861991162" MODIFIED="1667163359264" TEXT="⟹ visible Window liegt nun genau am Anfang"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1667155790783" ID="ID_1921165636" MODIFIED="1667155805398" TEXT="Frage: Basis für Rechnungen?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1667155806980" ID="ID_250578278" MODIFIED="1667156041888">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<b>FSecs</b> ≙ <font face="Monospaced">boost::rational<int64_t></font>
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1667156043109" ID="ID_234377752" MODIFIED="1667156067733" TEXT="ist sogar noch präziser als µs — und exakt"/>
|
||||
<node COLOR="#338800" CREATED="1667156072512" ID="ID_951906064" MODIFIED="1667156093493" TEXT="TimeVar für Konversionen "aufbohren"">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1667156098084" ID="ID_1341404738" MODIFIED="1667156104554" TEXT="potentiell gefährlich">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1667156192072" ID="ID_957228038" MODIFIED="1667156232815" TEXT="es gibt implizit int64_t ⟶ rational">
|
||||
<icon BUILTIN="info"/>
|
||||
</node>
|
||||
<node CREATED="1667156246932" ID="ID_306732730" MODIFIED="1667156379883" TEXT="int64_t kann aus TimeVar "entkommen" und in FSecs "reingeraten"">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<u>Resultat</u>:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
man hat einen Faktor 1e+6 im Ergebnis
|
||||
</li>
|
||||
<li>
|
||||
ist mir tatsächlich passiert, und zwar ziemlich überraschend
|
||||
</li>
|
||||
<li>
|
||||
Abhilfe: FSecs-ctor explizit anschreiben
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1667156105827" ID="ID_142853354" MODIFIED="1667156141584" TEXT="durch explicit-conversions schützen">
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1667156117746" ID="ID_748178975" MODIFIED="1667156124357" TEXT="geht nicht wie gewünscht"/>
|
||||
<node CREATED="1667156125589" ID="ID_1640624377" MODIFIED="1667156139947" TEXT="weil wir nicht über den FSecs-ctor verfügen"/>
|
||||
<node CREATED="1667156145590" ID="ID_1139847097" MODIFIED="1667156168191" TEXT="und explicit-conversion-operator ⟹ static_cast"/>
|
||||
<node CREATED="1667156173714" ID="ID_1596347322" MODIFIED="1667156188388" TEXT="das würde der fluiden Verwendung schaden"/>
|
||||
<node CREATED="1667156513645" ID="ID_1002330972" MODIFIED="1667156696839" TEXT="mäßig gefährlich, solange TimeVar ehr selten und lokal begrenzt">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
und so ist es auch gedacht: TimeVar sollte nicht auf APIs auftauchen!
|
||||
</p>
|
||||
<p>
|
||||
Wenn man sich daran hält, tritt TimeVar immer nur in einem lokalen Universum auf, wie hier im ZoomWindow, und man weiß genau, wo und warum man dem eine <i>reine Zahl </i>zuweist
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<linktarget COLOR="#ec2dc2" DESTINATION="ID_1002330972" ENDARROW="Default" ENDINCLINATION="-1;56;" ID="Arrow_ID_116751884" SOURCE="ID_907527366" STARTARROW="None" STARTINCLINATION="65;4;"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#d132ba" CREATED="1667156391029" ID="ID_907527366" MODIFIED="1667156702025" TEXT="nicht glücklich — aber geht wohl nicht besser">
|
||||
<arrowlink COLOR="#ec2dc2" DESTINATION="ID_1002330972" ENDARROW="Default" ENDINCLINATION="-1;56;" ID="Arrow_ID_116751884" STARTARROW="None" STARTINCLINATION="65;4;"/>
|
||||
<icon BUILTIN="smily_bad"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue