From c31522c2360c4a67e1e385fd10171e2b4c5b28de Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 10 Dec 2022 04:26:22 +0100 Subject: [PATCH] Timeline: define better internal zoom-out limit The value used previously was too conservative, and prevented ZommWindow from zooming out to the complete Time domain. This was due to missing the Time::SCALE denominator, which increaded the limit by factor 1e6 In fact the code is able to handle even this extremely reduced limit, but doing so seems over the top, since now detox() kicks in on several calculations, leading to rather coarse grained errors. Thus I decided to use a compromise: lower the limit only by factor 1000; with typical screen pixel widths, we can reach the full time domain, while most scaling and zoom calculations can be performed precisely, without detox() kicking in. Obviously this change requires adjusting a lot of the test case expectations, since we can now zoom out maximally. --- src/lib/rational.hpp | 2 +- src/lib/time/timevalue.hpp | 1 - src/stage/model/zoom-window.hpp | 17 +-- tests/stage/model/zoom-window-test.cpp | 59 ++++----- wiki/thinkPad.ichthyo.mm | 158 ++++++++++++++++++++++++- 5 files changed, 197 insertions(+), 40 deletions(-) diff --git a/src/lib/rational.hpp b/src/lib/rational.hpp index 863cdbc8d..909c76539 100644 --- a/src/lib/rational.hpp +++ b/src/lib/rational.hpp @@ -185,7 +185,7 @@ namespace util { f128 frac = f128(r) / den; int64_t res = d*u + int64_t(frac*u * ROUND_ULP); ENSURE (abs (f128(res)/u - rational_cast(Rat{num,den})) <= 1.0/abs(u) - ,"Requantisation error exceeded num=%lu / den=%lu -> res=%lu / quant=%lu" + ,"Requantisation error exceeded num=%li / den=%li -> res=%li / quant=%li" , num, den, res, u); return res; } diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 2fd570c52..926d9ea0e 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -475,7 +475,6 @@ namespace time { : Duration{Offset{timeSpec}} { } - explicit Duration (FSecs const& timeSpan_in_secs) : Duration{Offset{timeSpan_in_secs}} { } diff --git a/src/stage/model/zoom-window.hpp b/src/stage/model/zoom-window.hpp index 7d2a19a49..2fd869df2 100644 --- a/src/stage/model/zoom-window.hpp +++ b/src/stage/model/zoom-window.hpp @@ -706,13 +706,14 @@ namespace model { return px1 + px2 + pxr; } - /** window size beyond that limit would yield - * numerically dangerous zoom factors */ + /** window size beyond that limit would lead to + * numerically dangerous zoom factors (pixel/duration) */ static FSecs maxSaneWinExtension (uint pxWidth) { - return FSecs{LIM_HAZARD * pxWidth, Time::SCALE}; - } + return min (FSecs{LIM_HAZARD * pxWidth, 1000}, MAX_TIMESPAN); + } // Note: denominator 1000 is additional safety margin + // wouldn't be necessary, but makes detox(largeTime) more precise static Rat establishMetric (uint pxWidth, Time startWin, Time afterWin) @@ -763,13 +764,13 @@ namespace model { dur = min (dur, MAX_TIMESPAN); dur = max (dur, MICRO_TICK); // prevent window going void dur = detox (dur); // prevent integer wrap in time conversion - TimeVar timeDur{dur}; + TimeVar timeDur{Duration{dur}}; // prefer bias towards increased window instead of increased metric if (not isMicroGridAligned (dur)) timeDur = timeDur + TimeValue(1); // resize window relative to anchor point placeWindowRelativeToAnchor (dur); - establishWindowDuration (timeDur); + establishWindowDuration (Duration{timeDur}); // re-check metric to maintain precise pxWidth px_per_sec_ = conformMetricToWindow (pxWidth); ENSURE (_FSecs(afterWin_-startWin_) < MAX_TIMESPAN); @@ -799,7 +800,7 @@ namespace model { conformWindowToCanvas() { FSecs dur{afterWin_-startWin_}; - REQUIRE (dur < MAX_TIMESPAN); + REQUIRE (dur <= MAX_TIMESPAN); REQUIRE (Time::MIN <= startWin_); REQUIRE (afterWin_ <= Time::MAX); startAll_ = max (startAll_, Time::MIN); @@ -989,7 +990,7 @@ namespace model { } void - establishWindowDuration (TimeVar duration) + establishWindowDuration (Duration duration) { if (startWin_<= Time::MAX - duration) afterWin_ = startWin_ + duration; diff --git a/tests/stage/model/zoom-window-test.cpp b/tests/stage/model/zoom-window-test.cpp index 5737c5205..fe92b69ce 100644 --- a/tests/stage/model/zoom-window-test.cpp +++ b/tests/stage/model/zoom-window-test.cpp @@ -696,60 +696,65 @@ namespace test { win.setMetric (bruteZoom); // zoom out beyond what is possible and to a toxic factor CHECK (_raw(win.overallSpan().duration()) == 614891469123651720); // canvas size not changed - CHECK (_raw(win.visible().duration()) == 3298534883328); // window was expanded, + CHECK (_raw(win.visible().duration()) == 3298534883328000); // window was expanded, CHECK (_raw(win.visible().duration()) < int64_t{1}<<60 ); // ...but not as much as demanded - CHECK (_raw(win.visible().duration()) == 3 * LIM_HAZARD); // In fact it was capped at a built-in limit based on pixel size, + CHECK (_raw(win.visible().duration()) == 3* LIM_HAZARD*1000); // In fact it was capped at a built-in limit based on pixel size, // to prevent formation of dangerous numbers within metric calculations CHECK (win.visible().start() == - win.visible().end()); // window has been expanded symmetrically to existing position CHECK (win.px_per_sec() > bruteZoom); // the actual zoom factor also reflects the applied limitation, - CHECK (win.px_per_sec() == 15625_r/17179869184); // to ensure the denominator does not exceed LIM_HAZARD - CHECK (win.px_per_sec() == 1000000_r/LIM_HAZARD); + CHECK (win.px_per_sec() == 125_r/137438953472); // to ensure the denominator does not exceed LIM_HAZARD + CHECK (win.px_per_sec() == 1000_r/LIM_HAZARD); CHECK (win.px_per_sec() == 3 / _FSecs(win.visible().duration())); // and this value also conforms with the pixel size and window duration CHECK (win.pxWidth() == 3); /*--Test-3-----------*/ win.setMetric (5_r/std::numeric_limits::max()); // same limiting applies to even more nasty values - CHECK (_raw(win.visible().duration()) == 3298534883328); // still unchanged at limit - CHECK (win.px_per_sec() == 15625_r/17179869184); + CHECK (_raw(win.visible().duration()) == 3298534883328000); // still unchanged at limit + CHECK (win.px_per_sec() == 125_r/137438953472); CHECK (win.pxWidth() == 3); /*--Test-4-----------*/ - win.setMetric (1000001_r/LIM_HAZARD); // but zooming in more than that limit will be honored - CHECK (_raw(win.visible().duration()) == 3298531584796); // ...window now slightly reduced in size - CHECK (_raw(win.visible().duration()) < 3 * LIM_HAZARD); - CHECK (win.px_per_sec() = 750000_r/824632896199); // ...yet effective zoom factor was still marginally adjusted for safety - CHECK (win.px_per_sec() > 1000000_r/LIM_HAZARD); - CHECK (win.px_per_sec() > 1000001_r/LIM_HAZARD); // (this is what was requested) - CHECK (win.px_per_sec() < 1000002_r/LIM_HAZARD); // (thus result was slightly adjusted upwards) + win.setMetric (1001_r/LIM_HAZARD); // but zooming in more than that limit will be honored + CHECK (_raw(win.visible().duration()) == 3295239643684000); // ...window now slightly reduced in size + CHECK (_raw(win.visible().duration()) < 3 * LIM_HAZARD*1000); + CHECK (win.px_per_sec() == 750_r/823809910921); // ...yet effective zoom factor was still marginally adjusted for safety + CHECK (win.px_per_sec() > 1000_r/LIM_HAZARD); + CHECK (win.px_per_sec() > 1001_r/LIM_HAZARD); // (this is what was requested) + CHECK (win.px_per_sec() < 1002_r/LIM_HAZARD); // (thus result was slightly adjusted upwards) CHECK (win.pxWidth() == 3); /*--Test-5-----------*/ win.calibrateExtension (1'000'000'000); // implicit drastic zoom-out by increasing the number of pixels CHECK (win.pxWidth() < 1'000'000'000); // however: this number is capped at a fixed maximum CHECK (win.pxWidth() == MAX_PX_WIDTH); // (which „should be enough“ for the time being...) - CHECK (win.px_per_sec() == 508631_r/559239974093); // the zoom metric has been adapted, but to a sanitised value + CHECK (win.px_per_sec() == 16062_r/98763149723); // the zoom metric has been adapted, but to a sanitised value - CHECK (_raw(win.overallSpan().duration()) == 614891469123651720); // overall canvas duration not changed - CHECK (_raw(win.visible().duration()) == 109951052826533333); // window duration now drastically increased - CHECK (win.overallSpan().end() == TimeValue{307445734561825860}); // window is somewhere within canvas - CHECK (win.visible().end() == TimeValue{109949403560740935}); // but was expanded to the right, - CHECK (win.visible().start() == TimeValue{ -1649265792398}); // ... retaining the start value + CHECK (_raw(win.overallSpan().duration()) == 614891469123651720); // overall canvas duration not changed + CHECK (_raw(win.visible().duration()) == 614891469123651720); // window duration now expanded to the maximum possible value + CHECK (win.overallSpan().end() == TimeValue{ 307445734561825860}); // window now spans the complete time domain + CHECK (win.visible().end() == TimeValue{ 307445734561825860}); + CHECK (win.visible().start() == TimeValue{-307445734561825860}); // Note: these parameters build up to really »poisonous« values.... - CHECK (MAX_PX_WIDTH / _FSecs(win.visible().duration()) == 100000000000_r/109951052826533333); + CHECK (MAX_PX_WIDTH / _FSecs(win.visible().duration()) == 2500000000_r/15372286728091293); + CHECK (MAX_PX_WIDTH * 1000000_r/614891469123651720 == 2500000000_r/15372286728091293); CHECK (win.px_per_sec() * _FSecs(win.visible().duration()) < 0); // we can't even calculate the resulting pxWidth() naively CHECK (rational_cast(win.px_per_sec()) // ...while effectively these values are still correct - * rational_cast(_FSecs(win.visible().duration())) == 100000.914f); - CHECK (rational_cast(100000000000_r/109951052826533333) == 9.09495611e-07f); // theoretical value - CHECK (rational_cast(win.px_per_sec()) == 9.09503967e-07f); // value actually chosen - CHECK (win.px_per_sec() == 508631_r/559239974093); + * rational_cast(_FSecs(win.visible().duration())) == 100000.727f); + CHECK (rational_cast(MAX_PX_WIDTH*1000000_r/614891469123651720) == 1.62630329e-07f); // theoretical value + CHECK (rational_cast(win.px_per_sec()) == 1.62631508e-07f); // value actually chosen + CHECK (win.px_per_sec() == 16062_r/98763149723); /*--Test-6-----------*/ win.setMetric (bruteZoom); // And now put one on top by requesting excessive zoom-out: CHECK (_raw(win.overallSpan().duration()) == 614891469123651720); // overall canvas duration not changed - CHECK (_raw(win.visible().duration()) == 109951162777600000); // window duration only slightly increased - CHECK (508631_r/559239974093 > 15625_r/17179869184); // zoom factor slightly reduced - CHECK (win.px_per_sec() == 15625_r/17179869184); // and now hitting again the minimum limit + SHOW_EXPR(_raw(win.overallSpan().duration())) + SHOW_EXPR(_raw(win.visible().duration())) + CHECK (_raw(win.visible().duration()) == 614891469123640625); // window duration was slightly decreased -- WHY?? (TODO) + CHECK (16062_r/98763149723 > 200000_r/1229782938247); // zoom factor numerically slightly reduced + SHOW_EXPR(win.px_per_sec()) + CHECK (win.px_per_sec() == 200000_r/1229782938247); // and now hitting again the minimum limit + SHOW_EXPR(MAX_PX_WIDTH /(614891469123651720_r/1000000)) CHECK (win.pxWidth() == MAX_PX_WIDTH); // pixel count unchanged at maximum } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index a4ffeeda8..19c16b614 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -40522,21 +40522,173 @@ - + - + - + + + + + + + + + + + + + + + + + + + + + + + +

+ ZoomWindow: established size or metric misses expectation by more than 1px. 3px != 0.279620 expected pixel. +

+ +
+ + + + + + + + +

+ da die Metrik ja limitiert wurde und damit per definitionem auch sauber +

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ Grund sind die expliziten Limitierungen in dieser Funktion +

+ +
+ + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +