diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 926d9ea0e..e497979df 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -565,25 +565,25 @@ namespace time { , dur_(length) { } - TimeSpan(TimeValue const& start, Offset const& reference_distance) - : Time(start) - , dur_(reference_distance) - { } - - TimeSpan(TimeValue const& start, TimeValue const& end) - : Time(start) - , dur_(Offset(start,end)) - { } - 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 Duration& duration() @@ -715,6 +715,13 @@ namespace time { return symmetricLimit (res, Duration::MAX); } + inline TimeSpan + TimeSpan::conform() const ///< @note: implicitly capped to Duration::MAX + { + return Offset{*this} + dur_ <= Time::MAX? TimeSpan{*this} + : TimeSpan{Time::MAX-dur_, Time::MAX}; + } + inline TimeVar::TimeVar (FSecs const& fractionalSeconds) diff --git a/src/stage/model/zoom-window.hpp b/src/stage/model/zoom-window.hpp index 776befd69..54603036d 100644 --- a/src/stage/model/zoom-window.hpp +++ b/src/stage/model/zoom-window.hpp @@ -224,8 +224,8 @@ namespace model { public: ZoomWindow (uint pxWidth, TimeSpan timeline =TimeSpan{Time::ZERO, DEFAULT_CANVAS}) - : startAll_{timeline.start()} - , afterAll_{ensureNonEmpty(startAll_, timeline.end())} + : startAll_{ensureNonEmpty(timeline).start()} + , afterAll_{ensureNonEmpty(timeline).end()} , startWin_{startAll_} , afterWin_{afterAll_} , px_per_sec_{establishMetric (pxWidth, startWin_, afterWin_)} @@ -673,11 +673,13 @@ namespace model { * - visibleWindow ⊂ Canvas */ - static TimeValue - ensureNonEmpty (Time start, TimeValue endPoint) + static TimeSpan + ensureNonEmpty (TimeSpan const& span) { - return (start < endPoint)? endPoint - : start + Time{DEFAULT_CANVAS}; + return TimeSpan{span.start() + ,util::isnil(span.duration())? Duration{DEFAULT_CANVAS} + : span.duration() + }.conform(); } /** Assertion helper: resulting pxWidth matches expectations */ @@ -847,8 +849,6 @@ namespace model { { FSecs dur{afterWin_-startWin_}; 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_)) @@ -874,6 +874,8 @@ namespace model { } ENSURE (startAll_ <= startWin_); ENSURE (afterWin_ <= afterAll_); + ENSURE (Time::MIN <= startWin_); + ENSURE (afterWin_ <= Time::MAX); } void @@ -922,8 +924,8 @@ namespace model { void mutateCanvas (TimeSpan canvas) { - startAll_ = canvas.start(); - afterAll_ = ensureNonEmpty (startAll_, canvas.end()); + startAll_ = ensureNonEmpty(canvas).start(); + afterAll_ = ensureNonEmpty(canvas).end(); ensureInvariants(); } @@ -934,12 +936,11 @@ namespace model { mutateWindow (TimeSpan window) { uint px{pxWidth()}; - startWin_ = window.start(); - afterWin_ = ensureNonEmpty (startWin_, window.end()); + startWin_ = ensureNonEmpty(window).start(); + afterWin_ = ensureNonEmpty(window).end(); conformWindowToMetricLimits (px); startAll_ = min (startAll_, startWin_); afterAll_ = max (afterAll_, afterWin_); - px_per_sec_ = conformMetricToWindow (px); ensureInvariants (px); } @@ -949,12 +950,11 @@ namespace model { mutateRanges (TimeSpan canvas, TimeSpan window) { uint px{pxWidth()}; - startAll_ = canvas.start(); - afterAll_ = ensureNonEmpty (startAll_, canvas.end()); - startWin_ = window.start(); - afterWin_ = ensureNonEmpty (startWin_, window.end()); + startAll_ = ensureNonEmpty(canvas).start(); + afterAll_ = ensureNonEmpty(canvas).end(); + startWin_ = ensureNonEmpty(window).start(); + afterWin_ = ensureNonEmpty(window).end(); conformWindowToMetricLimits (px); - px_per_sec_ = conformMetricToWindow (px); ensureInvariants (px); } @@ -997,7 +997,6 @@ namespace model { adaptedWindow = max (adaptedWindow, MICRO_TICK); // prevent void window adaptedWindow = min (adaptedWindow, maxSaneWinExtension (pxWidth)); establishWindowDuration (adaptedWindow); - px_per_sec_ = conformMetricToWindow (pxWidth); ensureInvariants (pxWidth); } @@ -1020,7 +1019,6 @@ namespace model { establishWindowDuration (duration); startAll_ = min (startAll_, startWin_); afterAll_ = max (afterAll_, afterWin_); - px_per_sec_ = conformMetricToWindow (px); ensureInvariants (px); } diff --git a/tests/basics/time/time-value-test.cpp b/tests/basics/time/time-value-test.cpp index a3c611c7d..da537a1b7 100644 --- a/tests/basics/time/time-value-test.cpp +++ b/tests/basics/time/time-value-test.cpp @@ -312,6 +312,16 @@ namespace test{ CHECK ( Offset{Time::MAX + Duration::MAX} == Duration::MAX); // clipped at max CHECK ( Offset{Time::MIN - Duration::MAX} == -Duration::MAX); // clipped at min CHECK (Duration{Offset{Time::MIN - Duration::MAX}} == Duration::MAX); // duration is absolute + + CHECK (TimeSpan(Time::MIN, Time::MAX) == TimeSpan(Time::MAX, Time::MIN)); + CHECK (TimeSpan(Time::MAX, Duration::MAX).start() == Time::MAX); + CHECK (TimeSpan(Time::MAX, Duration::MAX).end() == Time::MAX + Duration::MAX); // note: end() can yield value beyond [Time::MIN...Time::MAX] + CHECK (TimeSpan(Time::MAX, Duration::MAX).duration() == Duration::MAX); + CHECK (TimeSpan(Time::MAX, Duration::MAX).conform() == TimeSpan(Time::MIN,Duration::MAX)); + CHECK (TimeSpan(Time::MAX, Offset(FSecs(-1))) == TimeSpan(Time::MAX-Offset(FSecs(1)), FSecs(1))); + CHECK (TimeSpan(Time::MAX, FSecs(5)).start() == Time::MAX); + CHECK (TimeSpan(Time::MAX, FSecs(5)).duration() == Duration(FSecs(5))); + CHECK (TimeSpan(Time::MAX, FSecs(5)).conform() == TimeSpan(Time::MAX-Offset(FSecs(5)), FSecs(5))); } @@ -368,9 +378,9 @@ namespace test{ compareTimeSpan (Time const& org) { TimeSpan span1 (org, org+org); // using the distance between start and end point - TimeSpan span2 (org, Offset(org, Time::ZERO)); // note: the offset is taken absolute, as Duration + TimeSpan span2 (org+org, org); // note: TimeSpan is oriented automatically TimeSpan span3 (org, FSecs(5,2)); // Duration given explicitly, in seconds - TimeSpan span4 (org, FSecs(5,-2)); // again: the Duration is taken absolute + TimeSpan span4 (org, FSecs(5,-2)); // note: fractional seconds taken absolute, as Duration CHECK (span1 == span2); CHECK (span2 == span1); @@ -386,8 +396,7 @@ namespace test{ CHECK (span2 != span4); CHECK (span4 != span2); - // especially note that creating a TimeSpan - // based on offset will take the offset absolute + // note that TimeSpan is oriented at creation CHECK (span1.end() == span2.end()); CHECK (span3.end() == span4.end()); diff --git a/tests/stage/model/zoom-window-test.cpp b/tests/stage/model/zoom-window-test.cpp index c1f6f38bc..6b02e5591 100644 --- a/tests/stage/model/zoom-window-test.cpp +++ b/tests/stage/model/zoom-window-test.cpp @@ -94,6 +94,8 @@ namespace test { safeguard_extremeZoomOut(); safeguard_extremeTimePos(); safeguard_extremeOffset(); + safeguard_verySmall(); + safeguard_veryDeep(); } @@ -523,7 +525,7 @@ namespace test { ZoomWindow win{1}; win.setVisibleDuration(Duration{FSecs(1,25)}); win.setOverallRange(TimeSpan(_t(10), _t(0))); // set an "reversed" overall time range - CHECK (win.overallSpan() == TimeSpan(_t(10), _t(20))); // range has been flipped forward + CHECK (win.overallSpan() == TimeSpan(_t(0), _t(10))); // range has been oriented forward CHECK (win.visible().duration() == Time(40,0)); CHECK (win.px_per_sec() == 25); CHECK (win.pxWidth() == 1); @@ -652,7 +654,7 @@ namespace test { CHECK (win.pxWidth() == 575); - win.setOverallStart(Time::MAX - TimeValue(23)); // preparation for Test-4 : shift canvas to end of time + win.setOverallRange(TimeSpan{Time::MAX, Offset{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}); @@ -784,19 +786,29 @@ namespace test { } - /** @test verify ZoomWindow code can navigate extremal time positions. + /** @test verify ZoomWindow code can navigate extremal time positions, + * thereby observing domain bounds without numeric wrap. */ void safeguard_extremeTimePos() { + ZoomWindow win{559, TimeSpan{Time::MAX, Duration{TimeValue(3)}}}; // setup a very small window clinging to Time::MAX + CHECK (win.visible().duration() == TimeValue(280)); // duration expanded due to MAX_ZOOM limit + CHECK (win.visible().start() == TimeValue(307445734561825580)); // and properly oriented and aligned within domain + CHECK (win.visible().end() == TimeValue(307445734561825860)); + CHECK (win.visible().end() == Time::MAX); + CHECK (win.visible() == win.overallSpan()); + CHECK (win.px_per_sec() == 559_r/280*Time::SCALE); + CHECK (win.px_per_sec() == 13975000_r/7); + CHECK (win.pxWidth() == 559); + // SHOW_EXPR(win.overallSpan()); // SHOW_EXPR(_raw(win.overallSpan().duration())); // SHOW_EXPR(_raw(win.visible().duration())); +// SHOW_EXPR(_raw(win.visible().start())); +// SHOW_EXPR(_raw(win.visible().end())); // SHOW_EXPR(win.px_per_sec()); // SHOW_EXPR(win.pxWidth()); -// CHECK (win.visible() == TimeSpan(_t(0), _t(23))); -// CHECK (win.px_per_sec() == 25); -// CHECK (win.pxWidth() == 575); } @@ -805,6 +817,43 @@ namespace test { void safeguard_extremeOffset() { +// SHOW_EXPR(win.overallSpan()); +// SHOW_EXPR(_raw(win.overallSpan().duration())); +// SHOW_EXPR(_raw(win.visible().duration())); +// SHOW_EXPR(_raw(win.visible().start())); +// SHOW_EXPR(_raw(win.visible().end())); +// SHOW_EXPR(win.px_per_sec()); +// SHOW_EXPR(win.pxWidth()); + } + + + /** @test verify ZoomWindow can handle excessively small windows. + */ + void + safeguard_verySmall() + { +// SHOW_EXPR(win.overallSpan()); +// SHOW_EXPR(_raw(win.overallSpan().duration())); +// SHOW_EXPR(_raw(win.visible().duration())); +// SHOW_EXPR(_raw(win.visible().start())); +// SHOW_EXPR(_raw(win.visible().end())); +// SHOW_EXPR(win.px_per_sec()); +// SHOW_EXPR(win.pxWidth()); + } + + + /** @test verify ZoomWindow and handle extreme zoom-in. + */ + void + safeguard_veryDeep() + { +// SHOW_EXPR(win.overallSpan()); +// SHOW_EXPR(_raw(win.overallSpan().duration())); +// SHOW_EXPR(_raw(win.visible().duration())); +// SHOW_EXPR(_raw(win.visible().start())); +// SHOW_EXPR(_raw(win.visible().end())); +// SHOW_EXPR(win.px_per_sec()); +// SHOW_EXPR(win.pxWidth()); } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 14f856604..00476d992 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -39541,11 +39541,21 @@ - - + + + + - - + + + + + + + + + + @@ -39559,14 +39569,20 @@ - + + + + + + + - + @@ -39653,10 +39669,19 @@ - - + + + + + + + +

+ der Nenner ist limitiert (kleiner Time::SCALE) und nicht toxisch +

+ +
- @@ -39664,7 +39689,7 @@ - + @@ -39709,6 +39734,7 @@ + @@ -39750,7 +39776,7 @@ - + @@ -39812,40 +39838,11 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -39930,6 +39927,20 @@ + + + + + + +

+ ...und das betrachte ich als gutmütig und hinreichend abgesichert... +

+ +
+ + +
@@ -40024,7 +40035,7 @@ - + @@ -40141,7 +40152,7 @@ - + @@ -40192,7 +40203,7 @@ - + @@ -40700,7 +40711,7 @@ - + @@ -40733,7 +40744,7 @@ - + @@ -40875,7 +40886,7 @@ - + @@ -41233,21 +41244,32 @@ - + + + + + + - - + + + + + - - + + + + + - +