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...
This commit is contained in:
parent
4d79bdce5f
commit
50c602ec3f
9 changed files with 450 additions and 53 deletions
|
|
@ -184,7 +184,9 @@ namespace util {
|
|||
// construct approximation quantised to 1/u
|
||||
f128 frac = f128(r) / den;
|
||||
int64_t res = d*u + int64_t(frac*u * ROUND_ULP);
|
||||
ENSURE (abs (f128(res)/u - rational_cast<f128>(Rat{num,den})) <= 1.0/abs(u));
|
||||
ENSURE (abs (f128(res)/u - rational_cast<f128>(Rat{num,den})) <= 1.0/abs(u)
|
||||
,"Requantisation error exceeded num=%lu / den=%lu -> res=%lu / quant=%lu"
|
||||
, num, den, res, u);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -130,6 +130,11 @@ namespace time {
|
|||
: TimeValue(lumiera_rational_to_time (fractionalSeconds))
|
||||
{ }
|
||||
|
||||
Offset::Offset (FSecs const& delta_in_secs)
|
||||
: TimeValue{buildRaw_(symmetricLimit (lumiera_rational_to_time (delta_in_secs)
|
||||
,Duration::MAX))}
|
||||
{ }
|
||||
|
||||
|
||||
/** @note recommendation is to use TCode for external representation
|
||||
* @remarks this is the most prevalent internal diagnostics display
|
||||
|
|
@ -266,29 +271,29 @@ namespace time {
|
|||
boost::rational<int64_t> distance (this->t_);
|
||||
distance *= factor;
|
||||
gavl_time_t microTicks = floordiv (distance.numerator(), distance.denominator());
|
||||
return Offset(TimeValue(microTicks));
|
||||
return Offset{buildRaw_(microTicks)};
|
||||
}
|
||||
|
||||
|
||||
/** offset by the given number of frames. */
|
||||
Offset::Offset (FrameCnt count, FrameRate const& fps)
|
||||
: TimeValue (count? (count<0? -1:+1) * lumiera_framecount_to_time (::abs(count), fps)
|
||||
: _raw(Duration::NIL))
|
||||
: TimeValue{buildRaw_(
|
||||
count? (count<0? -1:+1) * lumiera_framecount_to_time (::abs(count), fps)
|
||||
:_raw(Duration::NIL))}
|
||||
{ }
|
||||
|
||||
/** duration of the given number of frames.
|
||||
* @note always positive; count used absolute */
|
||||
Duration::Duration (FrameCnt count, FrameRate const& fps)
|
||||
: TimeValue (count? lumiera_framecount_to_time (abs(count), fps) : _raw(Duration::NIL))
|
||||
{ }
|
||||
|
||||
|
||||
/** constant to indicate "no duration" */
|
||||
const Duration Duration::NIL {Time::ZERO};
|
||||
|
||||
/** maximum possible temporal extension */
|
||||
const Duration Duration::MAX {Offset{Time::MIN, Time::MAX}};
|
||||
|
||||
const Duration Duration::MAX = []{
|
||||
auto maxDelta {Time::MAX - Time::MIN};
|
||||
// bypass limit check, which requires Duration::MAX
|
||||
return reinterpret_cast<Duration const&> (maxDelta);
|
||||
}();
|
||||
|
||||
|
||||
}} // namespace lib::Time
|
||||
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ namespace time {
|
|||
|
||||
// forwards...
|
||||
class FrameRate;
|
||||
class Duration;
|
||||
class TimeSpan;
|
||||
class Mutation;
|
||||
|
||||
|
|
@ -160,17 +161,24 @@ namespace time {
|
|||
/** some subclasses may receive modification messages */
|
||||
friend class Mutation;
|
||||
|
||||
/** explicit limit of allowed time range */
|
||||
static gavl_time_t limitedTime (gavl_time_t raw);
|
||||
/** safe calculation of explicitly limited time offset */
|
||||
static gavl_time_t limitedDelta (gavl_time_t origin, gavl_time_t target);
|
||||
|
||||
/** @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 gavl_time_t SCALE;
|
||||
|
||||
/** explicit limit of allowed time range */
|
||||
static gavl_time_t limited (gavl_time_t raw);
|
||||
|
||||
|
||||
explicit
|
||||
TimeValue (gavl_time_t val=0) ///< time given in µ ticks here
|
||||
: t_(limited (val))
|
||||
TimeValue (gavl_time_t val) ///< time given in µ ticks here
|
||||
: t_{limitedTime (val)}
|
||||
{ }
|
||||
|
||||
/** copy initialisation allowed */
|
||||
|
|
@ -234,7 +242,7 @@ namespace time {
|
|||
> >
|
||||
{
|
||||
public:
|
||||
TimeVar (TimeValue const& time = TimeValue())
|
||||
TimeVar (TimeValue const& time = TimeValue(0))
|
||||
: TimeValue(time)
|
||||
{ }
|
||||
|
||||
|
|
@ -349,6 +357,8 @@ namespace time {
|
|||
* to build derived values, including
|
||||
* - the _absolute (positive) distance_ for this offset: #abs
|
||||
* - a combined offset by chaining another offset
|
||||
* @note on construction, Offset values are checked and limited
|
||||
* to be within [-Duration::MAX ... +Duration::MAX]
|
||||
*/
|
||||
class Offset
|
||||
: public TimeValue
|
||||
|
|
@ -366,24 +376,21 @@ namespace time {
|
|||
|
||||
public:
|
||||
explicit
|
||||
Offset (TimeValue const& distance =Time::ZERO)
|
||||
: TimeValue(distance)
|
||||
{ }
|
||||
Offset (TimeValue const& distance =Time::ZERO);
|
||||
|
||||
explicit
|
||||
Offset (FSecs const& delta_in_secs);
|
||||
|
||||
Offset (FrameCnt count, FrameRate const& fps);
|
||||
|
||||
Offset (TimeValue const& origin, TimeValue const& target)
|
||||
: TimeValue(TimeVar(target) -= origin)
|
||||
: TimeValue{origin, target}
|
||||
{ }
|
||||
|
||||
Offset (FrameCnt count, FrameRate const& fps);
|
||||
|
||||
static const Offset ZERO;
|
||||
|
||||
|
||||
TimeValue
|
||||
abs() const
|
||||
{
|
||||
return TimeValue(std::llabs (t_));
|
||||
}
|
||||
/** interpret the distance given by this offset as a time duration */
|
||||
Duration abs() const;
|
||||
|
||||
/** @internal stretch offset by a possibly fractional factor,
|
||||
* and quantise into raw (micro tick) grid */
|
||||
|
|
@ -446,6 +453,7 @@ namespace time {
|
|||
* promoted from an offset. While Duration generally
|
||||
* is treated as immutable value, there is the
|
||||
* possibility to send a _Mutation message_.
|
||||
* @note Duration relies on Offset being limited
|
||||
*/
|
||||
class Duration
|
||||
: public TimeValue
|
||||
|
|
@ -455,29 +463,36 @@ namespace time {
|
|||
|
||||
public:
|
||||
Duration()
|
||||
: Duration(Time::ZERO)
|
||||
: TimeValue{Time::ZERO}
|
||||
{ }
|
||||
|
||||
Duration (Offset const& distance)
|
||||
: TimeValue(distance.abs())
|
||||
: TimeValue{buildRaw_(llabs (_raw(distance)))}
|
||||
{ }
|
||||
|
||||
explicit
|
||||
Duration (TimeValue const& timeSpec)
|
||||
: TimeValue(Offset(timeSpec).abs())
|
||||
: Duration{Offset{timeSpec}}
|
||||
{ }
|
||||
|
||||
explicit
|
||||
Duration (FSecs const& timeSpan_in_secs)
|
||||
: TimeValue(Offset(Time(timeSpan_in_secs)).abs())
|
||||
: 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}}
|
||||
{ }
|
||||
|
||||
Duration (TimeSpan const& interval);
|
||||
Duration (FrameCnt count, FrameRate const& fps);
|
||||
|
||||
Duration (Duration const& o)
|
||||
: Duration{Offset(o)}
|
||||
{ }
|
||||
: 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 ;
|
||||
|
|
@ -665,6 +680,14 @@ namespace time {
|
|||
, error::LERR_(BOTTOM_VALUE));
|
||||
return n;
|
||||
}
|
||||
|
||||
inline gavl_time_t
|
||||
symmetricLimit (gavl_time_t raw, TimeValue lim)
|
||||
{
|
||||
return raw > lim? _raw(lim)
|
||||
: -raw > lim? -_raw(lim)
|
||||
: raw;
|
||||
}
|
||||
}//(End) implementation helpers
|
||||
|
||||
|
||||
|
|
@ -673,25 +696,41 @@ namespace time {
|
|||
* raw time value to keep it within the arbitrary
|
||||
* boundaries defined by (Time::MAX, Time::MIN).
|
||||
* While Time entities are \c not a "safeInt"
|
||||
* implementation, we limit new values and
|
||||
* establish this safety margin to prevent
|
||||
* wrap-around during time quantisation */
|
||||
* implementation, we limit new values to
|
||||
* lower the likelihood of wrap-around */
|
||||
inline gavl_time_t
|
||||
TimeValue::limited (gavl_time_t raw)
|
||||
TimeValue::limitedTime (gavl_time_t raw)
|
||||
{
|
||||
return raw > Time::MAX? Time::MAX.t_
|
||||
: raw < Time::MIN? Time::MIN.t_
|
||||
: raw;
|
||||
return symmetricLimit (raw, Time::MAX);
|
||||
}
|
||||
|
||||
inline gavl_time_t
|
||||
TimeValue::limitedDelta (gavl_time_t origin, gavl_time_t target)
|
||||
{
|
||||
if (0 > (origin^target))
|
||||
{// prevent possible numeric wrap
|
||||
origin = symmetricLimit (origin, Duration::MAX);
|
||||
target = symmetricLimit (target, Duration::MAX);
|
||||
}
|
||||
gavl_time_t res = target - origin;
|
||||
return symmetricLimit (res, Duration::MAX);
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
TimeVar::TimeVar (FSecs const& fractionalSeconds)
|
||||
: TimeVar{Time(fractionalSeconds)}
|
||||
{ }
|
||||
|
||||
inline
|
||||
Offset::Offset (TimeValue const& distance)
|
||||
: TimeValue{buildRaw_(symmetricLimit(_raw(distance)
|
||||
, Duration::MAX))}
|
||||
{ }
|
||||
|
||||
inline
|
||||
Duration::Duration (TimeSpan const& interval)
|
||||
: TimeValue(interval.duration())
|
||||
: Duration{interval.duration()}
|
||||
{ }
|
||||
|
||||
inline
|
||||
|
|
@ -715,6 +754,11 @@ namespace time {
|
|||
return boost::rational_cast<double> (*this);
|
||||
}
|
||||
|
||||
inline Duration
|
||||
Offset::abs() const
|
||||
{
|
||||
return Duration{*this};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -732,6 +732,8 @@ namespace model {
|
|||
REQUIRE (dur < MAX_TIMESPAN);
|
||||
REQUIRE (Time::MIN <= startWin_);
|
||||
REQUIRE (afterWin_ <= Time::MAX);
|
||||
startAll_ = max (startAll_, Time::MIN);
|
||||
afterAll_ = min (afterAll_, Time::MAX);
|
||||
if (dur <= _FSecs(afterAll_-startAll_))
|
||||
{//possibly shift into current canvas
|
||||
if (afterWin_ > afterAll_)
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ namespace vault {
|
|||
gavl_time_t ticksSince1970 = now.tv_sec * TimeValue::SCALE
|
||||
+ now.tv_nsec / MICRO_TICS_PER_NANOSECOND;
|
||||
|
||||
ENSURE (ticksSince1970 == Time::limited (ticksSince1970));
|
||||
ENSURE (ticksSince1970 == _raw(TimeValue{ticksSince1970}));
|
||||
return TimeValue::buildRaw_(ticksSince1970); // bypassing the limit check
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ namespace test{
|
|||
CHECK (Time::MIN == case2.gridAlign( secs(-1) )); // resulting values will already exceed
|
||||
CHECK (Time::MIN == case2.gridAlign( secs(-2) )); // allowed range and thus will be clipped
|
||||
|
||||
// maximum frame size is half the time range
|
||||
// use very large frame with size of half the time range
|
||||
Duration hugeFrame(Time::MAX);
|
||||
FixedFrameQuantiser case3 (hugeFrame);
|
||||
CHECK (Time::MIN == case3.gridAlign(Time::MIN ));
|
||||
|
|
@ -205,9 +205,45 @@ namespace test{
|
|||
CHECK (TimeValue(0) == case4.gridAlign(Time::MAX -TimeValue(1) ));
|
||||
CHECK (TimeValue(0) == case4.gridAlign(Time::MAX )); //.......still truncated down to frame #0
|
||||
|
||||
// larger frames aren't possible
|
||||
Duration not_really_larger(secs(10000) + hugeFrame);
|
||||
CHECK (hugeFrame == not_really_larger);
|
||||
// think big...
|
||||
Duration superHuge{secs(12345) + hugeFrame};
|
||||
Duration extraHuge{2*hugeFrame};
|
||||
CHECK (extraHuge == Duration::MAX);
|
||||
|
||||
// Time::MAX < superHuge < Duration::Max is possible, but we can accommodate only one
|
||||
FixedFrameQuantiser case5 (superHuge);
|
||||
CHECK (TimeValue(0) == case5.gridAlign(Time::MAX ));
|
||||
CHECK (TimeValue(0) == case5.gridAlign(Time::MAX -TimeValue(1) ));
|
||||
CHECK (TimeValue(0) == case5.gridAlign( secs( 1) ));
|
||||
CHECK (TimeValue(0) == case5.gridAlign( secs( 0) ));
|
||||
CHECK (Time::MIN == case5.gridAlign( secs(-1) ));
|
||||
CHECK (Time::MIN == case5.gridAlign(Time::MIN +TimeValue(1) ));
|
||||
CHECK (Time::MIN == case5.gridAlign(Time::MIN ));
|
||||
|
||||
// now with offset
|
||||
FixedFrameQuantiser case6 (superHuge, Time::MAX-secs(1));
|
||||
CHECK (TimeValue(0) == case6.gridAlign(Time::MAX ));
|
||||
CHECK (TimeValue(0) == case6.gridAlign(Time::MAX -TimeValue(1) ));
|
||||
CHECK (TimeValue(0) == case6.gridAlign(Time::MAX -secs(1) ));
|
||||
CHECK (Time::MIN == case6.gridAlign(Time::MAX -secs(2) ));
|
||||
CHECK (Time::MIN == case6.gridAlign( secs( 1) ));
|
||||
CHECK (Time::MIN == case6.gridAlign( secs(-12345) ));
|
||||
CHECK (Time::MIN == case6.gridAlign( secs(-12345-1) ));
|
||||
CHECK (Time::MIN == case6.gridAlign( secs(-12345-2) )); // this would be one frame lower, but is clipped
|
||||
CHECK (Time::MIN == case6.gridAlign(Time::MIN +TimeValue(1) ));
|
||||
CHECK (Time::MIN == case6.gridAlign(Time::MIN )); // same... unable to represent time points before Time::MIN
|
||||
|
||||
// maximum frame size is spanning the full time range
|
||||
FixedFrameQuantiser case7 (extraHuge, Time::MIN+secs(1));
|
||||
CHECK (TimeValue(0) == case7.gridAlign(Time::MAX )); // rounded down one frame, i.e. to origin
|
||||
CHECK (TimeValue(0) == case7.gridAlign( secs( 0) ));
|
||||
CHECK (TimeValue(0) == case7.gridAlign(Time::MIN+secs(2) ));
|
||||
CHECK (TimeValue(0) == case7.gridAlign(Time::MIN+secs(1) )); // exactly at origin
|
||||
CHECK (Time::MIN == case7.gridAlign(Time::MIN )); // one frame further down, but clipped to Time::MIN
|
||||
|
||||
// even larger frames aren't possible
|
||||
Duration not_really_larger(secs(10000) + extraHuge);
|
||||
CHECK (extraHuge == not_really_larger);
|
||||
|
||||
// frame sizes below the time micro grid get trapped
|
||||
long subAtomic = 2*TimeValue::SCALE; // too small for this universe...
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ namespace test{
|
|||
void
|
||||
checkBasicTimeValues (TimeValue org)
|
||||
{
|
||||
TimeValue zero;
|
||||
TimeValue zero(0);
|
||||
TimeValue one (1);
|
||||
TimeValue max (Time::MAX);
|
||||
TimeValue min (Time::MIN);
|
||||
|
|
@ -248,7 +248,7 @@ namespace test{
|
|||
void
|
||||
buildDuration (TimeValue org)
|
||||
{
|
||||
TimeValue zero;
|
||||
TimeValue zero(0);
|
||||
TimeVar point(org);
|
||||
point += TimeValue(5);
|
||||
CHECK (org < point);
|
||||
|
|
@ -313,7 +313,6 @@ namespace test{
|
|||
void
|
||||
buildTimeSpan (TimeValue org)
|
||||
{
|
||||
TimeValue zero;
|
||||
TimeValue five(5);
|
||||
|
||||
TimeSpan interval (Time(org), Duration(Offset (org,five)));
|
||||
|
|
|
|||
|
|
@ -533,7 +533,7 @@ namespace test {
|
|||
CHECK (negaTime < Time::ZERO);
|
||||
Duration& evilDuration = reinterpret_cast<Duration&> (negaTime); // attempt fabricate a subverted TimeSpan
|
||||
CHECK (evilDuration < Time::ZERO); // ...sneak in a negative value
|
||||
CHECK (TimeSpan(_t(20), evilDuration) == TimeSpan(_t(20),_t(30))); // .....yet won't make it get past Duration copy ctor!
|
||||
// CHECK (TimeSpan(_t(20), evilDuration) == TimeSpan(_t(20),_t(30))); // .....yet won't make it get past Duration copy ctor!
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -601,6 +601,7 @@ namespace test {
|
|||
|
||||
Rat poison{_raw(Time::MAX)-101010101010101010, _raw(Time::MAX)+23};
|
||||
CHECK (0 < poison and poison < 1);
|
||||
/*--Test-1-----------*/
|
||||
win.setMetric (poison); // inject an evil new value for the metric
|
||||
CHECK (win.visible() == win.overallSpan()); // however, nothing happens
|
||||
CHECK (win.visible().duration() == _t(23)); // since the window is confined to overall canvas size
|
||||
|
|
@ -613,6 +614,7 @@ namespace test {
|
|||
CHECK (win.overallSpan().duration() == Time::MAX);
|
||||
CHECK (win.visible().duration() == _t(23)); // while the visible part remains unaltered
|
||||
|
||||
/*--Test-2-----------*/
|
||||
win.setMetric (poison); // Now attempt again to poison the zoom calculations...
|
||||
CHECK (win.overallSpan().duration() == Time::MAX); // overall canvas unchanged
|
||||
CHECK (win.visible().duration() == TimeValue{856350691}); // visible window expanded (a zoom-out, as required)
|
||||
|
|
@ -636,6 +638,7 @@ namespace test {
|
|||
CHECK (win.overallSpan().duration() == TimeValue{307445734561825860});
|
||||
CHECK (win.visible().duration() == TimeValue{856350691});
|
||||
|
||||
/*--Test-3-----------*/
|
||||
win.setVisiblePos (poison); // Yet another way to sneak in our toxic value...
|
||||
CHECK (win.overallSpan().start() == Time::ZERO);
|
||||
CHECK (win.overallSpan().duration() == TimeValue{307445734561825860}); // However, all base values turn out unaffected
|
||||
|
|
@ -652,6 +655,31 @@ namespace test {
|
|||
|
||||
CHECK (win.px_per_sec() == 575000000_r/856350691); // metric and pixel width are retained
|
||||
CHECK (win.pxWidth() == 575);
|
||||
|
||||
|
||||
win.setOverallStart(Time::MAX - TimeValue(23)); // preparation for Test-4 : shift canvas to end of time
|
||||
CHECK (win.overallSpan() == win.visible()); // consequence: window has been capped to canvas size
|
||||
CHECK (win.overallSpan().start() == TimeValue{307445734561825572}); // window now also located at extreme values
|
||||
CHECK (win.overallSpan().end() == TimeValue{307445734561825860});
|
||||
CHECK (win.overallSpan().duration() == TimeValue{288}); // window (and canvas) were expanded to comply to maximum zoom factor
|
||||
CHECK (win.px_per_sec() == 17968750_r/9); // zoom factor was then slightly reduced to match next pixel boundary
|
||||
CHECK (win.pxWidth() == 575); // established pixel size was retained
|
||||
SHOW_EXPR(win.overallSpan());
|
||||
SHOW_EXPR(_raw(win.overallSpan().start()));
|
||||
SHOW_EXPR(_raw(win.overallSpan().end()));
|
||||
SHOW_EXPR(_raw(win.overallSpan().duration()));
|
||||
SHOW_EXPR(_raw(win.visible().duration()));
|
||||
|
||||
/*--Test-4-----------*/
|
||||
win.setVisiblePos(Time{Time::MIN + TimeValue(13)}); // Test: implicitly provoke poisonous factor through extreme offset
|
||||
|
||||
SHOW_EXPR(win.overallSpan());
|
||||
SHOW_EXPR(_raw(win.overallSpan().start()));
|
||||
SHOW_EXPR(_raw(win.overallSpan().end()));
|
||||
SHOW_EXPR(_raw(win.overallSpan().duration()));
|
||||
SHOW_EXPR(_raw(win.visible().duration()));
|
||||
SHOW_EXPR(win.px_per_sec());
|
||||
SHOW_EXPR(win.pxWidth());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -662,6 +690,7 @@ namespace test {
|
|||
safeguard_extremeZoomOut()
|
||||
{
|
||||
// SHOW_EXPR(win.overallSpan());
|
||||
// SHOW_EXPR(_raw(win.overallSpan().duration()));
|
||||
// SHOW_EXPR(_raw(win.visible().duration()));
|
||||
// SHOW_EXPR(win.px_per_sec());
|
||||
// SHOW_EXPR(win.pxWidth());
|
||||
|
|
|
|||
|
|
@ -40893,6 +40893,286 @@
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1670016158206" ID="ID_1376502254" MODIFIED="1670016169130" TEXT="setVisiblePos(weit-weg)">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1670017946731" ID="ID_410930969" MODIFIED="1670017960838" TEXT="erst mal das visibleWin nahe an Time::MAX"/>
|
||||
<node CREATED="1670017961849" ID="ID_1322153373" MODIFIED="1670017996849" TEXT="dann als gewünschte Position Time::MIN"/>
|
||||
<node CREATED="1670024841228" ID="ID_327609209" MODIFIED="1670024849028" TEXT="ist gar nicht so wirklich toxisch">
|
||||
<node CREATED="1670024857602" ID="ID_10543296" MODIFIED="1670024880586" TEXT="oder meine Funktionen sind inzwischen hinreichend abgesichert">
|
||||
<icon BUILTIN="ksmiletris"/>
|
||||
</node>
|
||||
<node CREATED="1670024990169" ID="ID_737996846" MODIFIED="1670025107388" TEXT="die Division für den posFactor funktioniert (überraschenderweise)">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
also offensichtlich erkennt boost::rational die Möglichkeit, den gemeinsamen Faktor 1e6 aus Zähler und Nenner wegzukürzen. Da wir aber hier einen FSec-Wert als Offset zugeben, haben wir wenig Möglichkeiten, das zu <i>vergiften</i>
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1670024963460" ID="ID_16615073" MODIFIED="1670024981796" TEXT="parabolicAnchorRule kappt den Wert (da außerhalb 0...1)">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1670025133845" ID="ID_1582569279" MODIFIED="1670025153962" TEXT="infolgedessen sind alle weiteren Berechnungen trivial / no-op"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1670025155874" ID="ID_725576556" MODIFIED="1670025170589" TEXT="aber: der Offset wird gekappt">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1670025172247" ID="ID_962949808" MODIFIED="1670025190742" TEXT="konzeptionelles Problem im Time-handling-Framework">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node CREATED="1670025194925" ID="ID_674387327" MODIFIED="1670025296382" TEXT="Offsets und Durations könnten 2*Time::MAX (bzw MIN) akzeptieren">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
rein numerisch würde das gehen, da ich Time::MAX | MIN sinnigerweise auf INT_MAX / 30 gesetzt habe. Das war vorausschauend....
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1670025229777" ID="ID_1471304702" MODIFIED="1670025251370" TEXT="aber sie gehen alle stur durch TimeValue, und der kappt auf 1*Time::MAX (bzw MIN)"/>
|
||||
<node CREATED="1670025302707" ID="ID_408211429" MODIFIED="1670025314899" TEXT="Lösung: Loch bohren">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1670025330923" ID="ID_664088863" MODIFIED="1670025372823" TEXT="das aktuelle Verhalten ist widersinnig und überraschend">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
es untergräbt gradezu den Sinn dedizierter Zeit-Entitäten
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
<node CREATED="1670025386651" ID="ID_1227027753" MODIFIED="1670025402822" TEXT="Idee: der copy-ctor ist nicht limitiert">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1670025414136" ID="ID_1966020819" MODIFIED="1670027497697" TEXT="Probleme und Aufgaben">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1670025543263" ID="ID_528446508" MODIFIED="1670120677591" TEXT="neue Limitierungs-Funktion schaffen (± Duration::MAX)">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1670025513339" ID="ID_1622564783" MODIFIED="1670120675981" TEXT="der Differenz-ctor muß größere Offsets konstruieren können">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1670025579708" ID="ID_967783770" MODIFIED="1670120673953" TEXT="neuer ctor für FSecs">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1670025655456" ID="ID_79471093" MODIFIED="1670120670775" TEXT="die Funktion Offset::abs() neu definieren">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1670025671469" ID="ID_807444515" MODIFIED="1670025688298" TEXT="bisher konstruiert sie einen TimeValue, und kappt deshalb">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node CREATED="1670025690862" ID="ID_1913450557" MODIFIED="1670080620421" TEXT="Idee: sie konstruiert eine Duration">
|
||||
<arrowlink COLOR="#fefdb9" DESTINATION="ID_482497794" ENDARROW="Default" ENDINCLINATION="-192;-7;" ID="Arrow_ID_1014085079" STARTARROW="None" STARTINCLINATION="-207;16;"/>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1670025987045" ID="ID_1491328900" MODIFIED="1670120834295" TEXT="Duration braucht eine »Hintertür«">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1670026021871" ID="ID_1872786617" MODIFIED="1670026051423" TEXT="....durch welche man explizit einen Wert einbringen kann"/>
|
||||
<node CREATED="1670026054003" ID="ID_873519529" MODIFIED="1670026069149" TEXT="und der nicht durch die Limitierung der Basis-Klasse TimeValue läuft"/>
|
||||
<node CREATED="1670026074664" ID="ID_1460012408" MODIFIED="1670027376431" TEXT="diese Hintertür darf nicht offensichtlich sein">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<ul>
|
||||
<li>
|
||||
auf den ersten Blick darf es nicht aussehen wie "aha, und hier kann ich alles machen"
|
||||
</li>
|
||||
<li>
|
||||
so wie der Zugang beschrieben ist, ist er völlig logisch und konsistent
|
||||
</li>
|
||||
<li>
|
||||
die erweiterte Möglichkeit erschließt sich erst dem aufmerksamen Leser
|
||||
</li>
|
||||
<li>
|
||||
diese erweiterte Möglichkeit erweist sich aber letztlich als nichts anderes als die Konsequenz der formalen Definition
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1670026089995" ID="ID_5043644" MODIFIED="1670026098048" TEXT="und sie darf keinen Mißbrauch gestatten">
|
||||
<node CREATED="1670026101388" ID="ID_848547621" MODIFIED="1670026120917" TEXT="also die µ-Ticks müssen nachher positiv sein"/>
|
||||
<node CREATED="1670026121626" ID="ID_1970749903" MODIFIED="1670026130300" TEXT="und limitiert auf Duration::MAX"/>
|
||||
</node>
|
||||
<node COLOR="#690f14" CREATED="1670026155933" ID="ID_1357490453" MODIFIED="1670079761362" TEXT="Idee: die Limitierung auf TimeValue per ctor-Parameter steuerbar machen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<ul>
|
||||
<li>
|
||||
das wäre dann ein ctor mit einem 2. Argument
|
||||
</li>
|
||||
<li>
|
||||
wer so einen ctor aufruft, weiß was er tut
|
||||
</li>
|
||||
<li>
|
||||
das könnte auch später in eine MicroTic-Basisklasse übernommen werden
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1670027393582" ID="ID_630920961" MODIFIED="1670027410287" TEXT="damit würden wir über den normalen Basis-ctor eintreten"/>
|
||||
<node CREATED="1670027412179" ID="ID_558474187" MODIFIED="1670027420848" TEXT="und Werte über den copy-ctor übernehmen können"/>
|
||||
<node CREATED="1670027457629" ID="ID_730521318" MODIFIED="1670027484830" TEXT="der TimeValue-ctor könnte dann sogar tatsächlich die Limitierung enforcen"/>
|
||||
<node CREATED="1670079580092" ID="ID_1783175998" MODIFIED="1670079740173" TEXT="Idee verworfen! das wird zu explizit und zu offen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Das versaut den bisher sehr sauberen Code von TimeValue, und läd gradezu dazu ein, hier beliebige Werte zu konstruieren. Im Hinblick darauf, daß ich umgestalten möchte TimeValue ⟶ MicroTicks, würde damit die Daseinsberechtigung untergraben, denn man kann nun nicht mehr sicher sein, daß MicroTicks ein <i>sicherer Wert </i>ist.
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1670079773858" ID="ID_1230606534" MODIFIED="1670120643313" TEXT="protected Delta-Konstruktur">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1670079791241" ID="ID_1046215971" MODIFIED="1670079812814" TEXT="wasseridcht, da nur von abgeleiteten Klassen aufgerufen"/>
|
||||
<node CREATED="1670079817657" ID="ID_1263461807" MODIFIED="1670079837199" TEXT="nimmt zwei Argumente und macht unmittelbar die limitierte Offset-Berechnung"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1670079953176" ID="ID_482497794" MODIFIED="1670120655974" TEXT="Duration muß selber für die Implementierung des Absolutbetrages sorgen">
|
||||
<linktarget COLOR="#fefdb9" DESTINATION="ID_482497794" ENDARROW="Default" ENDINCLINATION="-192;-7;" ID="Arrow_ID_1014085079" SOURCE="ID_1913450557" STARTARROW="None" STARTINCLINATION="-207;16;"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node COLOR="#338800" CREATED="1670080498535" ID="ID_1961138610" MODIFIED="1670120658621" TEXT="der ctor Duration(Offset) macht diese Umwandlung">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1670080522251" ID="ID_1399417381" MODIFIED="1670120660266" TEXT="alle anderen Konstruktoren werden darauf aufgebaut">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1670120920391" ID="ID_1544328925" MODIFIED="1670120940299" TEXT="bin nicht wirklich glücklich mit dem Ergebnis">
|
||||
<icon BUILTIN="smily_bad"/>
|
||||
<node CREATED="1670120943775" ID="ID_1968873669" MODIFIED="1670120966072" TEXT="wir verwenden nun doch an einigen Stellen buildRaw_() und _raw()"/>
|
||||
<node CREATED="1670120968395" ID="ID_189852460" MODIFIED="1670121014289" TEXT="noch schlimmer: ich muß jetzt Duration::MAX explizit per reinterpret_cast konstruieren"/>
|
||||
<node CREATED="1670121025020" ID="ID_1030076787" MODIFIED="1670121088591" TEXT="⟹ abgeleitete Klassen haben nicht wirklich den Zugangs-Weg, den sie brauchen"/>
|
||||
<node CREATED="1670121089738" ID="ID_1726489244" MODIFIED="1670121157461" TEXT="und der Umweg über Raw ist notwendig, die Zeit-Rechenfunktionen reichen nicht"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1670121166296" ID="ID_1078651329" MODIFIED="1670121217977" TEXT="heißt: die Limitierung sitzt an der falschen Stelle">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1670120849448" ID="ID_633048851" MODIFIED="1670197941515" TEXT="QuantiserBasics_test scheitert">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node CREATED="1670120873385" ID="ID_1718461090" MODIFIED="1670120892890" TEXT="er hat die bisher bestehende Limitierung von Duration explizit abgetestet"/>
|
||||
<node CREATED="1670120893850" ID="ID_788600909" MODIFIED="1670120906512" TEXT="gut so, aber jetzt muß ich diesen Test wieder verstehen....">
|
||||
<icon BUILTIN="smiley-neutral"/>
|
||||
<node CREATED="1670191477920" ID="ID_1536550302" MODIFIED="1670191505143" TEXT="der Test ist ja wirklich schön „basic“">
|
||||
<icon BUILTIN="ksmiletris"/>
|
||||
</node>
|
||||
<node CREATED="1670191507368" ID="ID_1611277271" MODIFIED="1670191525161" TEXT="was hier scheitert ist eine Nebensache"/>
|
||||
<node CREATED="1670191526749" ID="ID_1074184920" MODIFIED="1670191648400" TEXT="dokumentiert lediglich diese unsinnige Limitierung von Offset | Duration">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...ganz dunkel kommt mir die Erinnerung an eine „kognitive Dissonanz“ — die ich dann schell unter den Teppich gekehrt hatte, indem ich sie mit einem Assert dokumentierte....
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1670191650385" ID="ID_1618349097" MODIFIED="1670191678988" TEXT="muß also eigentlich nur diese nebenbei-Assertion der besseren Lösung anpassen"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1670191680385" ID="ID_1961622718" MODIFIED="1670197933608" TEXT="sollte bei der Gelegenheit den Test erweitern und die Grenzen ausreizen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1670191697554" ID="ID_1632102794" MODIFIED="1670191731569" TEXT="zwei weitere Test-Intervalle">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1670191712394" ID="ID_1670947823" MODIFIED="1670191745192" TEXT="größer als Time::MAX aber kleiner als Duration::MAX">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1670191723322" ID="ID_299394278" MODIFIED="1670191744039" TEXT="exakt Duration::MAX">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1670191732657" ID="ID_1301403557" MODIFIED="1670191739848" TEXT="kombinieren mit Offset">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1670191753771" ID="ID_838942043" MODIFIED="1670191797559" TEXT="Nebenbei bemerkt: das Grid::gridAligned() - API ist „überraschend“">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1670191803951" ID="ID_62422663" MODIFIED="1670191874006" TEXT="das Ergebnis ist relativ zum Grid-Origin">
|
||||
<icon BUILTIN="info"/>
|
||||
</node>
|
||||
<node CREATED="1670191815870" ID="ID_1441457979" MODIFIED="1670191824182" TEXT="tatsächlich so, ist auch so dokumentiert"/>
|
||||
<node CREATED="1670191825469" ID="ID_64245414" MODIFIED="1670191888904" TEXT="trotzdem schräg — Input absolut, output relativ zum Origin">
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1670191890964" ID="ID_77008687" MODIFIED="1670192169799" TEXT="gibts dafür einen Grund?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1670191912001" ID="ID_1930426088" MODIFIED="1670191918387" TEXT="Verwendungen: nur Tests">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1670191945933" ID="ID_838705253" MODIFIED="1670192032827" TEXT="Grid::timeOf() funktioniert andres (und wie erwartet)">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
und die unterliegende lumiera_time_of_gridpoint weicht da ebenfalls auffällig ab...
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node CREATED="1670192005076" ID="ID_1578782665" MODIFIED="1670192067197" TEXT="Grid::timeOf() hat reale Verwendung in der Timecode-Handhabung"/>
|
||||
<node CREATED="1670192068956" ID="ID_1880098778" MODIFIED="1670192159480" TEXT="Fazit: das ist ein »hexagonales Rad«">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
....also ein Feature, das zwar auf theoretischer Basis entwickelt wurde, aber nur im testgetriebenen Kontext; hier wohl entstanden aus der implementierungsmäßigen Symmetrie zu Grid::gridPoint(n)
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="forward"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1670192170956" ID="ID_1026606570" MODIFIED="1670192201815" TEXT="API ändern: gridAligned() soll den Origin mit einschließen">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1670192209793" ID="ID_1904533160" MODIFIED="1670192229367" TEXT="Limitierung bedenken">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1670192204551" ID="ID_1757335290" MODIFIED="1670192230352" TEXT="Implementierung anpassen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1670192239363" ID="ID_968994817" MODIFIED="1670192243152" TEXT="dokumentieren">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1670192231262" ID="ID_185488622" MODIFIED="1670192236941" TEXT="Test anpassen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1670027504991" ID="ID_824635341" MODIFIED="1670027509353" TEXT="Tests ergänzen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue