clean-up: relocate frame quantisation operations

The `FixedFrameQuantiser` relied on three functions from the raw-time handling library.
Since this (and NTSC drop-frame) are the only usages, these functions
can be relocated into the implemntation translation unit `lib/time/quantiser.cpp`

On closer inspection, this reveals some room for improvements:
Instead of relying on raw-computation functions written in C,
we could rather revert the dependency and express these computations
in terms of our Time-entities, which are written in C++, are much more
systematic and provide consistency checks and protection against numeric
overflow, all integrated with linear arithmetic and concise notation.

After performing these rearrangements,
most of the functions can be collapsed into ''almost nothing''.

This was taken as opportunity to re-check and improve the remaining
implementation core of the `FixedFrameQuantiser` -- the handling of
extreme corner cases can be much improved, now representing the
"grid-local time" as `Offset`, which doubles the possible value range.

The reworked unit test shows that, with this change, now the limitation
happens prior to quantisation, meaning that we always get a grid-aligned
result, even in the most extreme corner cases.
This commit is contained in:
Fischlurch 2025-05-28 23:49:01 +02:00
parent 255fc82a1b
commit 27089550d2
7 changed files with 401 additions and 291 deletions

View file

@ -78,15 +78,6 @@ raw_time_64
lumiera_framecount_to_time (uint64_t frameCount, lib::time::FrameRate const& fps);
/**
* Calculates the duration of one frame in Lumiera time units.
* @param framerate underlying framerate as rational number
* @throw error::Logic on zero framerate
*/
raw_time_64
lumiera_frame_duration (lib::time::FrameRate const& fps);
@ -94,42 +85,6 @@ extern "C" { /* ===================== C interface ======================== */
#endif
/**
* Quantise the given time into a fixed grid, relative to the origin.
* The time grid used for quantisation is comprised of equally spaced intervals,
* rooted at the given origin. The interval starting with the origin is numbered
* as zero. Each interval includes its lower bound, but excludes its upper bound.
* @param grid spacing of the grid intervals, measured in TimeValue::Scale (µ-ticks)
* @return number of the grid interval containing the given time.
* @warning the resulting value is limited to (Time::Min, Time::MAX)
*/
int64_t
lumiera_quantise_frames (raw_time_64 time, raw_time_64 origin, raw_time_64 grid);
int64_t
lumiera_quantise_frames_fps (raw_time_64 time, raw_time_64 origin, uint framerate);
/**
* Similar to #lumiera_quantise_frames, but returns a grid aligned _relative time_.
* @return time of start of the grid interval containing the given time,
* but measured relative to the origin
* @warning because the resulting value needs to be limited to fit into a 64bit long,
* the addressable time range can be considerably reduced. For example, if
* origin = Time::MIN, then all original time values above zero will be
* clipped, because the result, relative to origin, needs to be <= Time::MAX
*/
raw_time_64
lumiera_quantise_time (raw_time_64 time, raw_time_64 origin, raw_time_64 grid);
/**
* Calculate time of a grid point (frame start)
* @param nr index number of the grid point (0 is at origin)
* @param grid spacing of the grid intervals, measured in TimeValue::Scale (µ-ticks)
* @return time point (frame start) on the Lumiera internal time scale
*/
raw_time_64
lumiera_time_of_gridpoint (int64_t nr, raw_time_64 origin, raw_time_64 grid);
/**
* Build a time value by summing up the given components.
* @param millis number of milliseconds

View file

@ -62,7 +62,7 @@ namespace time {
virtual FrameCnt gridPoint (TimeValue const& raw) const =0;
virtual TimeValue gridLocal (TimeValue const& raw) const =0;
virtual Offset gridLocal (TimeValue const& raw) const =0;
virtual TimeValue timeOf (FrameCnt gridPoint) const =0;
virtual TimeValue timeOf (FSecs gridTime, int=0) const =0;
};

View file

@ -2,7 +2,8 @@
Quantiser - aligning time values to a time grid
Copyright (C)
2010, Hermann Vosseler <Ichthyostega@web.de>
2010, Stefan Kangas <skangas@skangas.se>
2010-2025, Hermann Vosseler <Ichthyostega@web.de>
  **Lumiera** is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
@ -20,11 +21,9 @@
#include "lib/time/quantiser.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/time/timequant.hpp"
#include "lib/time.h"
#include "lib/util-quant.hpp"
#include "lib/rational.hpp"
#include <boost/rational.hpp>
using boost::rational_cast;
using std::string;
@ -33,6 +32,63 @@ namespace error = lumiera::error;
namespace lib {
namespace time {
namespace { // implementation: basic frame quantisation....
/**
* Grid-align the raw time to a frame grid, which can be fractional.
* @note for extreme values of framerate and framerate_divisor, the
* computation is approximative and will be µs-aligned.
*/
inline int64_t
calculate_quantisation (raw_time_64 time, uint64_t framerate, uint64_t framerate_divisor=1)
{
REQUIRE (framerate);
REQUIRE (framerate_divisor);
const uint64_t MAXRANGE = std::numeric_limits<raw_time_64>::max();
const int64_t limit_num = MAXRANGE / framerate;
const int64_t limit_den = MAXRANGE / framerate_divisor;
const int64_t microScale {lib::time::TimeValue::SCALE};
// protect against numeric overflow
if (abs(time) < limit_num and microScale < limit_den)
{
// safe to calculate "time * framerate"
return util::floordiv (time*int64_t(framerate), microScale*int64_t(framerate_divisor));
}
else
{
// direct calculation will overflow;
// thus approximate the grid spacing by re-quantisation into µ-tick scale
// (since in the end we will alias to the microScale anyway)...
const uint64_t LIM{MAXRANGE/microScale};
if (framerate > LIM or framerate_divisor > LIM)
{
framerate = util::reQuant (framerate,framerate_divisor, LIM);
framerate_divisor = LIM;
}
// Note : frameDuration ≡ microScale / (framerate/framerate_divisor)
raw_time_64 frameDuration = util::reQuant (framerate_divisor,framerate, microScale);
return util::floordiv (time,frameDuration);
}
}
}
/**
* Quantise the given time into a fixed grid, relative to the origin.
* The time grid used for quantisation is comprised of equally spaced intervals,
* rooted at the given origin. The interval starting with the origin is numbered
* as zero. Each interval includes its lower bound, but excludes its upper bound.
* @param grid spacing of the grid intervals, measured in TimeValue::Scale (µ-ticks)
* @return number of the grid interval containing the given time.
*/
int64_t
FixedFrameQuantiser::grid_aligned (TimeValue const& time, TimeValue const& grid)
{
return util::floordiv (_raw(time), _raw(grid));
}
PQuant
getDefaultGridFallback()
@ -51,7 +107,7 @@ namespace time {
/* Note: the ctor QuTime(TimeValue, Symbol) and the static function
* PQuant Quantiser::retrieve (Symbol) are defined in common-services.cpp
* To use this special convenience shortcuts, you need to link against liblumieracore.so
* This allows to use the Advice-system to retrieve grid definitions directly from the session
* This allows to use the Advice-system to retrieve grid definitions directly from the session
*/
@ -86,7 +142,7 @@ namespace time {
/** convenience shortcut: \em materialise a raw time value
* based on this grid or time axis, but returning a raw time value.
* based on this grid or time axis, but returning a raw time value.
* Implemented as combination of the #gridPoint and #timeOf operations,
* i.e. we quantise into this scale, but transform the result back onto
* the global raw time value scale.
@ -111,50 +167,52 @@ namespace time {
* origin and framerate stored within this quantiser.
* @warning returned frame count might exceed the valid
* range when converting back into a TimeValue.
* @see #lumiera_quantise_frames
* @see FixedFrameQuantiser::grid_aligned
*/
FrameCnt
FixedFrameQuantiser::gridPoint (TimeValue const& rawTime) const
{
return lumiera_quantise_frames (_raw(rawTime), _raw(origin_), _raw(raster_));
return grid_aligned (Offset{origin_, rawTime}, raster_);
}
/** transform into the local time scale grid aligned.
* The actual calculation first determines the number
* of the grid interval containing the given rawTime,
* then followed by multiplying this interval number
* The actual calculation first determines the number of the grid interval
* containing the given rawTime, followed by multiplying this interval number
* with the grid spacing.
* @return time of the start point of the grid interval
* containing the rawTime, _relative to the origin_
* of the time scale used by this quantiser.
* @warning returned time values are limited by the
* valid range of lumiera::Time
* @see #lumiera_quantise_time
* @return time of the start point of the grid interval containing the rawTime,
* _relative to the origin_ of the time scale used by this quantiser.
* @warning because the resulting value needs to be limited to fit into a 64bit long,
* the addressable time range can be considerably reduced. For example, if
* origin = Time::MIN, then all original time values above zero will be
* clipped, because the result, relative to origin, needs to be <= Time::MAX
* @see FixedFrameQuantiser::grid_aligned
*/
TimeValue
Offset
FixedFrameQuantiser::gridLocal (TimeValue const& rawTime) const
{
return TimeValue (lumiera_quantise_time (_raw(rawTime), _raw(origin_), _raw(raster_)));
return raster_ * grid_aligned (Offset{origin_, rawTime}
,raster_);
}
/** calculate time value of a grid interval (frame) start point
* @return time point measured in Lumiera internal time
* @param gridPoint index number of the grid point (0 is at origin)
* @return time point measured in Lumiera internal time
* @warning returned time values are limited by the
* valid range of lumiera::Time
*/
TimeValue
FixedFrameQuantiser::timeOf (FrameCnt gridPoint) const
{
return TimeValue (lumiera_time_of_gridpoint (gridPoint, _raw(origin_), _raw(raster_)));
return origin_ + gridPoint * raster_;
}
/** calculate time coordinates of a time spec relative to this quantised time scale
* @param gridTime seconds relative to the origin of this scale
* @param gridOffset additional offset in grid intervals (frames)
* @return time point measured in Lumiera internal time
* @return time point measured in Lumiera internal time
* @warning returned time values are limited by the
* valid range of lumiera::Time
*/
@ -174,5 +232,66 @@ namespace time {
}} // lib::time
/* ===== NTSC drop-frame conversions ===== */
namespace { // conversion parameters
const uint FRAMES_PER_10min = 10*60 * 30000/1001;
const uint FRAMES_PER_1min = 1*60 * 30000/1001;
const uint DISCREPANCY = (1*60 * 30) - FRAMES_PER_1min;
}
/**
* @remark This function reverses building the drop-frame timecode,
* and thus maps a time into consecutive frame numbers
* at NTSC framerate (i.e. without gaps)
* @param timecode represented as time value in µ-ticks
* @return the absolute frame number as addressed by NTSC drop-frame
* @todo 2011 I doubt this works correct for negative times!!
*/
int64_t
calculate_ntsc_drop_frame_number (raw_time_64 time)
{
int64_t frameNr = calculate_quantisation (time, 30000, 1001);
// partition into 10 minute segments
lldiv_t tenMinFrames = lldiv (frameNr, FRAMES_PER_10min);
// ensure the drop-frame incidents happen at full minutes;
// at start of each 10-minute segment *no* drop incident happens,
// thus we need to correct discrepancy between nominal/real framerate once:
int64_t remainingMinutes = (tenMinFrames.rem - DISCREPANCY) / FRAMES_PER_1min;
int64_t dropIncidents = (10-1) * tenMinFrames.quot + remainingMinutes;
return frameNr + 2*dropIncidents;
}
/**
* @remark This is the mapping function to translate NTSC drop frame
* timecode specification into an actual time, with the necessary
* skip events every 1.-9. minute, thereby allocating 108 frames
* less per hour, than would be required for full 30 fps.
* @return raw time value on a µ-tick scale
*/
raw_time_64
build_time_from_ntsc_drop_frame (uint frames, uint secs, uint mins, uint hours)
{
int64_t total_mins = 60 * hours + mins;
int64_t total_frames = 30*60*60 * hours
+ 30*60 * mins
+ 30 * secs
+ frames
- 2 * (total_mins - total_mins / 10);
raw_time_64 result = _raw (Offset{total_frames, FrameRate::NTSC});
if (0 != result) // compensate for truncating down on conversion
result += 1; // without this adjustment the frame number
return result; // would turn out off by -1 on back conversion
}
}} // lib::time

View file

@ -114,7 +114,7 @@ namespace time {
//------Grid-API----------------------------------------------
virtual FrameCnt gridPoint (TimeValue const& raw) const =0;
virtual TimeValue gridLocal (TimeValue const& raw) const =0;
virtual Offset gridLocal (TimeValue const& raw) const =0;
virtual TimeValue timeOf (FrameCnt gridPoint) const =0;
virtual TimeValue timeOf (FSecs, int =0) const =0;
};
@ -142,11 +142,13 @@ namespace time {
FixedFrameQuantiser (FrameRate const& frames_per_second, TimeValue referencePoint =TimeValue(0));
FixedFrameQuantiser (Duration const& frame_duration, TimeValue referencePoint =TimeValue(0));
FrameCnt gridPoint (TimeValue const&) const;
TimeValue gridLocal (TimeValue const&) const;
TimeValue timeOf (FrameCnt gridPoint) const;
TimeValue timeOf (FSecs, int =0) const;
FrameCnt gridPoint (TimeValue const&) const override;
Offset gridLocal (TimeValue const&) const override;
TimeValue timeOf (FrameCnt gridPoint) const override;
TimeValue timeOf (FSecs, int =0) const override;
private:
static int64_t grid_aligned (TimeValue const&, TimeValue const&);
};

View file

@ -402,81 +402,8 @@ lumiera_framecount_to_time (uint64_t frameCount, FrameRate const& fps)
return rational_cast<raw_time_64> (lib::time::TimeValue::SCALE * frameCount / framerate);
}
raw_time_64
lumiera_frame_duration (FrameRate const& fps)
{
if (!fps)
throw error::Logic ("Impossible to quantise to an zero spaced frame grid"
, error::LUMIERA_ERROR_BOTTOM_VALUE);
FSecs duration = 1 / fps;
return lumiera_rational_to_time (duration);
}
namespace { // implementation: basic frame quantisation....
inline int64_t
calculate_quantisation (raw_time_64 time, raw_time_64 origin, raw_time_64 grid)
{
time -= origin;
return floordiv (time,grid);
}
inline int64_t
calculate_quantisation (raw_time_64 time, raw_time_64 origin, uint framerate, uint framerate_divisor=1)
{
REQUIRE (framerate);
REQUIRE (framerate_divisor);
const int64_t limit_num = std::numeric_limits<raw_time_64>::max() / framerate;
const int64_t limit_den = std::numeric_limits<raw_time_64>::max() / framerate_divisor;
const int64_t microScale {lib::time::TimeValue::SCALE};
// protect against numeric overflow
if (abs(time) < limit_num and microScale < limit_den)
{
// safe to calculate "time * framerate"
time -= origin;
return floordiv (time*framerate, microScale*framerate_divisor);
}
else
{
// direct calculation will overflow.
// use the less precise method instead...
raw_time_64 frameDuration = microScale / framerate; // truncated to µs
return calculate_quantisation (time,origin, frameDuration);
}
}
}
int64_t
lumiera_quantise_frames (raw_time_64 time, raw_time_64 origin, raw_time_64 grid)
{
return calculate_quantisation (time, origin, grid);
}
int64_t
lumiera_quantise_frames_fps (raw_time_64 time, raw_time_64 origin, uint framerate)
{
return calculate_quantisation (time, origin, framerate);
}
raw_time_64
lumiera_quantise_time (raw_time_64 time, raw_time_64 origin, raw_time_64 grid)
{
int64_t count = calculate_quantisation (time, origin, grid);
raw_time_64 alignedTime = count * grid;
return alignedTime;
}
raw_time_64
lumiera_time_of_gridpoint (int64_t nr, raw_time_64 origin, raw_time_64 grid)
{
raw_time_64 offset = nr * grid;
return origin + offset;
}
raw_time_64
@ -536,66 +463,6 @@ lumiera_time_frames (raw_time_64 time, uint fps)
namespace lib {
namespace time { ////////////////////////////////////////////////////////////////////////////////////////////TICKET #1259 : move all calculation functions into a C++ namespace
/* ===== NTSC drop-frame conversions ===== */
namespace { // conversion parameters
const uint FRAMES_PER_10min = 10*60 * 30000/1001;
const uint FRAMES_PER_1min = 1*60 * 30000/1001;
const uint DISCREPANCY = (1*60 * 30) - FRAMES_PER_1min;
}
/**
* @remark This function reverses building the drop-frame timecode,
* and thus maps a time into consecutive frame numbers
* at NTSC framerate (i.e. without gaps)
* @param timecode represented as time value in µ-ticks
* @return the absolute frame number as addressed by NTSC drop-frame
* @todo 2011 I doubt this works correct for negative times!!
*/
int64_t
calculate_ntsc_drop_frame_number (raw_time_64 time)
{
int64_t frameNr = calculate_quantisation (time, 0, 30000, 1001);
// partition into 10 minute segments
lldiv_t tenMinFrames = lldiv (frameNr, FRAMES_PER_10min);
// ensure the drop-frame incidents happen at full minutes;
// at start of each 10-minute segment *no* drop incident happens,
// thus we need to correct discrepancy between nominal/real framerate once:
int64_t remainingMinutes = (tenMinFrames.rem - DISCREPANCY) / FRAMES_PER_1min;
int64_t dropIncidents = (10-1) * tenMinFrames.quot + remainingMinutes;
return frameNr + 2*dropIncidents;
}
/**
* @remark This is the mapping function to translate NTSC drop frame
* timecode specification into an actual time, with the necessary
* skip events every 1.-9. minute, thereby allocating 108 frames
* less per hour, than would be required for full 30 fps.
* @return raw time value on a µ-tick scale
*/
raw_time_64
build_time_from_ntsc_drop_frame (uint frames, uint secs, uint mins, uint hours)
{
uint64_t total_mins = 60 * hours + mins;
uint64_t total_frames = 30*60*60 * hours
+ 30*60 * mins
+ 30 * secs
+ frames
- 2 * (total_mins - total_mins / 10);
raw_time_64 result = lumiera_framecount_to_time (total_frames, FrameRate::NTSC);
if (0 != result) // compensate for truncating down on conversion
result += 1; // without this adjustment the frame number
return result; // would turn out off by -1 on back conversion
}
}} // lib::time

View file

@ -21,6 +21,7 @@
#include "lib/time/quantiser.hpp"
#include "lib/random.hpp"
#include "lib/util.hpp"
#include "lib/test/diagnostic-output.hpp"
using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE;
using util::isnil;
@ -152,91 +153,80 @@ namespace test{
void
coverQuantisationCornerCases()
{
// For this test we exploit the limits of the time system
Time SUB_MIN{-Duration::MAX};
Time SUP_MAX{ Duration::MAX};
// origin at lower end of the time range
FixedFrameQuantiser case1 (1, Time::MIN);
CHECK (secs(0) == case1.gridLocal(Time::MIN ));
CHECK (secs(0) == case1.gridLocal(Time::MIN +TimeValue(1) ));
CHECK (secs(1) == case1.gridLocal(Time::MIN +secs(1) ));
CHECK (Time::MAX -secs(1) > case1.gridLocal( secs(-1) ));
CHECK (Time::MAX -secs(1) <= case1.gridLocal( secs (0) ));
CHECK (Time::MAX > case1.gridLocal( secs (0) ));
CHECK (Time::MAX == case1.gridLocal( secs(+1) ));
CHECK (Time::MAX == case1.gridLocal( secs(+2) ));
FixedFrameQuantiser case1 (1, SUB_MIN);
CHECK (secs(0) == case1.gridLocal(SUB_MIN ));
CHECK (secs(0) == case1.gridLocal(SUB_MIN +TimeValue(1) ));
CHECK (secs(1) == case1.gridLocal(SUB_MIN +secs(1) ));
CHECK (SUP_MAX -secs(1) > case1.gridLocal( secs(-1) ));
CHECK (SUP_MAX -secs(1) <= case1.gridLocal( secs (0) ));
CHECK (SUP_MAX > case1.gridLocal( secs (0) ));
CHECK (SUP_MAX > case1.gridLocal( secs(+1) ));
CHECK (SUP_MAX > case1.gridLocal( secs(+2) ));
TimeValue largestPoint = case1.gridLocal(secs (0));
CHECK (largestPoint == case1.gridLocal( secs(+1) ));
CHECK (largestPoint == case1.gridLocal( secs(+2) ));
CHECK (largestPoint < SUP_MAX);
CHECK (largestPoint == Offset{secs(1)} * case1.gridPoint(secs(0)));
// origin at upper end of the time range
FixedFrameQuantiser case2 (1, Time::MAX);
CHECK (secs( 0) == case2.gridLocal(Time::MAX ));
CHECK (secs(-1) == case2.gridLocal(Time::MAX -TimeValue(1) )); // note: next lower frame
CHECK (secs(-1) == case2.gridLocal(Time::MAX -secs(1) )); // i.e. the same as a whole frame down
CHECK (Time::MIN +secs(1) < case2.gridLocal( secs(+2) ));
CHECK (Time::MIN +secs(1) >= case2.gridLocal( secs(+1) ));
CHECK (Time::MIN < case2.gridLocal( secs(+1) ));
CHECK (Time::MIN == case2.gridLocal( secs( 0) )); // note: because of downward truncating,
CHECK (Time::MIN == case2.gridLocal( secs(-1) )); // resulting values will already exceed
CHECK (Time::MIN == case2.gridLocal( secs(-2) )); // allowed range and thus will be clipped
FixedFrameQuantiser case2 (1, SUP_MAX);
CHECK (secs( 0) == case2.gridLocal(SUP_MAX ));
CHECK (secs(-1) == case2.gridLocal(SUP_MAX -TimeValue(1) )); // note: next lower frame
CHECK (secs(-1) == case2.gridLocal(SUP_MAX -secs(1) )); // i.e. the same as a whole frame down
CHECK (SUB_MIN +secs(1) < case2.gridLocal( secs(+2) ));
CHECK (SUB_MIN +secs(1) >= case2.gridLocal( secs(+1) ));
CHECK (SUB_MIN < case2.gridLocal( secs(+1) ));
CHECK (SUB_MIN == case2.gridLocal( secs( 0) )); // note: because of downward truncating,
CHECK (SUB_MIN == case2.gridLocal( secs(-1) )); // resulting values will already exceed
CHECK (SUB_MIN == case2.gridLocal( secs(-2) )); // allowed range and thus will be clipped
// use very large frame with size of half the time range
Duration hugeFrame(Time::MAX);
Duration hugeFrame(SUP_MAX);
FixedFrameQuantiser case3 (hugeFrame);
CHECK (Time::MIN == case3.gridLocal(Time::MIN ));
CHECK (Time::MIN == case3.gridLocal(Time::MIN +TimeValue(1) ));
CHECK (Time::MIN == case3.gridLocal( secs(-1) ));
CHECK (TimeValue(0) == case3.gridLocal( secs( 0) ));
CHECK (TimeValue(0) == case3.gridLocal( secs(+1) ));
CHECK (TimeValue(0) == case3.gridLocal(Time::MAX -TimeValue(1) ));
CHECK (Time::MAX == case3.gridLocal(Time::MAX ));
CHECK (SUB_MIN == case3.gridLocal(SUB_MIN ));
CHECK (SUB_MIN == case3.gridLocal(SUB_MIN +TimeValue(1) ));
CHECK (SUB_MIN == case3.gridLocal( secs(-1) ));
CHECK (TimeValue(0) == case3.gridLocal( secs( 0) ));
CHECK (TimeValue(0) == case3.gridLocal( secs(+1) ));
CHECK (TimeValue(0) == case3.gridLocal(SUP_MAX -TimeValue(1) ));
CHECK (SUP_MAX == case3.gridLocal(SUP_MAX ));
// now displacing this grid by +1sec....
FixedFrameQuantiser case4 (hugeFrame, secs(1));
CHECK (Time::MIN == case4.gridLocal(Time::MIN ));
CHECK (Time::MIN == case4.gridLocal(Time::MIN +TimeValue(1) )); // clipped...
CHECK (Time::MIN == case4.gridLocal(Time::MIN +secs(1) )); // but now exact (unclipped)
CHECK (Time::MIN == case4.gridLocal( secs(-1) ));
CHECK (Time::MIN == case4.gridLocal( secs( 0) ));
CHECK (TimeValue(0) == case4.gridLocal( secs(+1) )); //.....now exactly the frame number zero
CHECK (TimeValue(0) == case4.gridLocal(Time::MAX -TimeValue(1) ));
CHECK (TimeValue(0) == case4.gridLocal(Time::MAX )); //.......still truncated down to frame #0
CHECK (SUB_MIN == case4.gridLocal(SUB_MIN ));
CHECK (SUB_MIN == case4.gridLocal(SUB_MIN +TimeValue(1) )); // clipped...
CHECK (SUB_MIN == case4.gridLocal(SUB_MIN +secs(1) )); // but now exact (unclipped)
CHECK (SUB_MIN == case4.gridLocal( secs(-1) ));
CHECK (SUB_MIN == case4.gridLocal( secs( 0) ));
CHECK (TimeValue(0) == case4.gridLocal( secs(+1) )); //.....now exactly the frame number zero
CHECK (TimeValue(0) == case4.gridLocal(SUP_MAX -TimeValue(1) ));
// think big...
Duration superHuge{secs(12345) + hugeFrame};
Duration extraHuge{2*hugeFrame};
CHECK (extraHuge == Duration::MAX);
TimeVar excess{SUP_MAX +secs(1)}; // this is a *loophole* to slide by the limitation of Time values
CHECK (SUP_MAX < excess);
CHECK (Duration{excess} < excess); // ...yet as soon as we construct another entity, the limitation applies
CHECK (Duration{excess} == SUP_MAX);
// Time::MAX < superHuge < Duration::Max is possible, but we can accommodate only one
FixedFrameQuantiser case5 (superHuge);
CHECK (TimeValue(0) == case5.gridLocal(Time::MAX ));
CHECK (TimeValue(0) == case5.gridLocal(Time::MAX -TimeValue(1) ));
CHECK (TimeValue(0) == case5.gridLocal( secs( 1) ));
CHECK (TimeValue(0) == case5.gridLocal( secs( 0) ));
CHECK (Time::MIN == case5.gridLocal( secs(-1) ));
CHECK (Time::MIN == case5.gridLocal(Time::MIN +TimeValue(1) ));
CHECK (Time::MIN == case5.gridLocal(Time::MIN ));
// now with offset
FixedFrameQuantiser case6 (superHuge, Time::MAX-secs(1));
CHECK (TimeValue(0) == case6.gridLocal(Time::MAX ));
CHECK (TimeValue(0) == case6.gridLocal(Time::MAX -TimeValue(1) ));
CHECK (TimeValue(0) == case6.gridLocal(Time::MAX -secs(1) ));
CHECK (Time::MIN == case6.gridLocal(Time::MAX -secs(2) ));
CHECK (Time::MIN == case6.gridLocal( secs( 1) ));
CHECK (Time::MIN == case6.gridLocal( secs(-12345) ));
CHECK (Time::MIN == case6.gridLocal( secs(-12345-1) ));
CHECK (Time::MIN == case6.gridLocal( secs(-12345-2) )); // this would be one frame lower, but is clipped
CHECK (Time::MIN == case6.gridLocal(Time::MIN +TimeValue(1) ));
CHECK (Time::MIN == case6.gridLocal(Time::MIN )); // same... unable to represent time points before Time::MIN
CHECK (SUP_MAX == case4.gridLocal(excess )); // Thus, more by accident, the next higher grid point can be computed
// maximum frame size is spanning the full time range
FixedFrameQuantiser case7 (extraHuge, Time::MIN+secs(1));
CHECK (TimeValue(0) == case7.gridLocal(Time::MAX )); // rounded down one frame, i.e. to origin
CHECK (TimeValue(0) == case7.gridLocal( secs( 0) ));
CHECK (TimeValue(0) == case7.gridLocal(Time::MIN+secs(2) ));
CHECK (TimeValue(0) == case7.gridLocal(Time::MIN+secs(1) )); // exactly at origin
CHECK (Time::MIN == case7.gridLocal(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);
CHECK (secs(1) == case4.timeOf(0));
CHECK (excess == case4.timeOf(1)); // The same loophole also allows to generate this next higher grid point
CHECK (excess == case4.timeOf(2)); // ...while the next after next will limited in computation
FixedFrameQuantiser broken (Duration::MAX, SUP_MAX); // Can drive this loophole to the extreme...
CHECK (secs(0) == broken.timeOf(-1)); // since there is leeway by one order of magnitude
CHECK (SUP_MAX == broken.timeOf(0));
CHECK (SUP_MAX+SUP_MAX > Duration::MAX);
CHECK (SUP_MAX+SUP_MAX == broken.timeOf(1));
CHECK (SUP_MAX+SUP_MAX == broken.timeOf(2));
// frame sizes below the time micro grid get trapped
long subAtomic = 2*TimeValue::SCALE; // too small for this universe...
VERIFY_ERROR (BOTTOM_VALUE, FixedFrameQuantiser quark(subAtomic) );

View file

@ -162938,7 +162938,8 @@ Since then others have made contributions, see the log for the history.</font></
<node CREATED="1748351475601" ID="ID_828981584" MODIFIED="1748351475601" TEXT="lumiera_time_seconds"/>
<node CREATED="1748351482281" ID="ID_76044701" MODIFIED="1748351482281" TEXT="lumiera_time_millis"/>
</node>
<node CREATED="1748351146392" ID="ID_1900378289" MODIFIED="1748351155511" TEXT="nur C-Test">
<node CREATED="1748351146392" ID="ID_1900378289" MODIFIED="1748389490642" TEXT="nur C-Test">
<icon BUILTIN="idea"/>
<node CREATED="1748351152077" MODIFIED="1748351152077" TEXT="lumiera_quantise_frames_fps"/>
<node CREATED="1748351210265" ID="ID_980580438" MODIFIED="1748351210265" TEXT="lumiera_build_time_fps"/>
<node CREATED="1748351564782" ID="ID_1914445290" MODIFIED="1748351569194" TEXT="lumiera_time_frames"/>
@ -162966,8 +162967,8 @@ Since then others have made contributions, see the log for the history.</font></
<node CREATED="1748351911358" MODIFIED="1748351911358" TEXT="lumiera_time_ntsc_drop_hours"/>
<node CREATED="1748351925746" MODIFIED="1748351925746" TEXT="lumiera_tmpbuf_print_time"/>
</node>
<node CREATED="1748351446052" ID="ID_620111210" MODIFIED="1748351456006" TEXT="nur Timecode-Widget (+ C-Test)"/>
<node CREATED="1748350835257" ID="ID_337045682" MODIFIED="1748351145534" TEXT="keine Verwendung">
<node CREATED="1748350835257" ID="ID_337045682" MODIFIED="1748389483435" TEXT="keine Verwendung">
<icon BUILTIN="idea"/>
<node CREATED="1748350857513" ID="ID_1512877366" MODIFIED="1748350857513" TEXT="lumiera_frame_duration"/>
</node>
</node>
@ -162980,7 +162981,9 @@ Since then others have made contributions, see the log for the history.</font></
</node>
<node CREATED="1748350997721" ID="ID_266616127" MODIFIED="1748351028774" TEXT="und au&#xdf;erdem...">
<node CREATED="1748351029770" ID="ID_324213134" MODIFIED="1748351034557" TEXT="im Timecode-Widget"/>
<node CREATED="1748351035274" ID="ID_1640433443" MODIFIED="1748351046306" TEXT="im C-Test f&#xfc;r die Time-Library"/>
<node CREATED="1748351035274" ID="ID_1640433443" MODIFIED="1748389509129" TEXT="im C-Test f&#xfc;r die Time-Library">
<icon BUILTIN="back"/>
</node>
</node>
<node CREATED="1748352902464" ID="ID_790522662" MODIFIED="1748352905396" TEXT="Probleme">
<node CREATED="1748352906772" ID="ID_1144464670" MODIFIED="1748354044614" TEXT="die Dezimator-Funktionen">
@ -163100,15 +163103,27 @@ Since then others have made contributions, see the log for the history.</font></
</node>
</node>
</node>
<node CREATED="1748358751369" ID="ID_920187834" MODIFIED="1748358756276" TEXT="Weg zur Aufl&#xf6;sung">
<node CREATED="1748358771454" ID="ID_810358375" MODIFIED="1748358805918" TEXT="alle Verwendungen der Komponenten-Accessoren beseitigen"/>
<node CREATED="1748358858563" ID="ID_567453270" MODIFIED="1748358892312" TEXT="den Basis-Test neu schreiben, mit einer Literal-Expectation"/>
<node CREATED="1748358817067" ID="ID_557771496" MODIFIED="1748358830620" TEXT="Dezimation in Komponenten stets direkt berechnen"/>
<node CREATED="1748358907068" ID="ID_918217691" MODIFIED="1748358919181" TEXT="die restlichen Funktionen zur Implementierung"/>
<node CREATED="1748358931317" ID="ID_1847347457" MODIFIED="1748358944202" TEXT="Drop-Frame-Algo separat dokumentieren"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1748358751369" ID="ID_920187834" MODIFIED="1748389559916" TEXT="Weg zur Aufl&#xf6;sung">
<icon BUILTIN="yes"/>
<node COLOR="#338800" CREATED="1748358771454" ID="ID_810358375" MODIFIED="1748389566617" TEXT="alle Verwendungen der Komponenten-Accessoren beseitigen">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1748358858563" ID="ID_567453270" MODIFIED="1748389568085" TEXT="den Basis-Test neu schreiben, mit einer Literal-Expectation">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1748358817067" ID="ID_557771496" MODIFIED="1748389571906" TEXT="Dezimation in Komponenten stets direkt berechnen">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1748358907068" ID="ID_918217691" MODIFIED="1748389575707" TEXT="die restlichen Funktionen zur Implementierung">
<icon BUILTIN="flag-yellow"/>
</node>
<node COLOR="#338800" CREATED="1748358931317" ID="ID_1847347457" MODIFIED="1748389574283" TEXT="Drop-Frame-Algo separat dokumentieren">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1748350717103" ID="ID_1477694076" MODIFIED="1748350722786" TEXT="Aufr&#xe4;umen">
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1748350717103" ID="ID_1477694076" MODIFIED="1748479104036" TEXT="Aufr&#xe4;umen">
<icon BUILTIN="pencil"/>
<node COLOR="#5b280f" CREATED="1748362636897" ID="ID_793532606" MODIFIED="1748362658768" TEXT="man k&#xf6;nnte versuchen, H:M:S fertig zu implementieren">
<icon BUILTIN="stop-sign"/>
<node CREATED="1748362660474" ID="ID_1457327168" MODIFIED="1748362804911" TEXT="das w&#xe4;re aber &#xbb;premature&#xab;">
@ -163199,7 +163214,10 @@ Since then others have made contributions, see the log for the history.</font></
<icon BUILTIN="button_ok"/>
<node CREATED="1748383746542" ID="ID_1117132995" MODIFIED="1748383761481" TEXT="umziehen in Namespace lib::time"/>
<node CREATED="1748383717910" ID="ID_6099644" MODIFIED="1748383730804" TEXT="umziehen in Header lib/time/dropframe.hpp"/>
<node CREATED="1748383731428" ID="ID_1472070007" MODIFIED="1748383743307" TEXT="Implementierung verbleibt in lib/time/time.cpp"/>
<node COLOR="#5b280f" CREATED="1748383731428" ID="ID_1472070007" MODIFIED="1748442507691" TEXT="Implementierung verbleibt in lib/time/time.cpp">
<arrowlink COLOR="#6361b7" DESTINATION="ID_1724729549" ENDARROW="Default" ENDINCLINATION="1281;74;" ID="Arrow_ID_444441914" STARTARROW="None" STARTINCLINATION="771;44;"/>
<icon BUILTIN="button_cancel"/>
</node>
<node CREATED="1748383780122" ID="ID_170667006" MODIFIED="1748383790175" TEXT="nur die eigentliche Berechnungslogik erhalten">
<node CREATED="1748383816257" ID="ID_1470396784" MODIFIED="1748384512960" TEXT="int64_t calculate_ntsc_drop_frame_number (raw_time_64 timecode)"/>
<node CREATED="1748383835599" ID="ID_328152526" MODIFIED="1748385988435" TEXT="raw_time_64 build_time_from_ntsc_drop_frame (frames, secs, mins, hours)"/>
@ -163231,6 +163249,165 @@ Since then others have made contributions, see the log for the history.</font></
<node COLOR="#435e98" CREATED="1748388278014" ID="ID_275417741" MODIFIED="1748388508019" TEXT="lumiera_tmpbuf_print_time (raw_time_64 time)"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1748436006749" ID="ID_439524098" MODIFIED="1748479104035" TEXT="verbleibende Funktionen verteilen">
<icon BUILTIN="pencil"/>
<node CREATED="1748436021050" ID="ID_1426888385" MODIFIED="1748436030803" TEXT="generische HIlfsfunktionen">
<node CREATED="1748436257559" ID="ID_1140529126" MODIFIED="1748436260804" TEXT="lumiera_rational_to_time">
<node CREATED="1748436293884" ID="ID_534358506" MODIFIED="1748436301463" TEXT="technisch kniffelig">
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1748436263298" ID="ID_1571065618" MODIFIED="1748436277061" TEXT="k&#xf6;nnte Basis f&#xfc;r eine RSec-Klasse werden">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1748436280279" ID="ID_1020376151" MODIFIED="1748436289210" TEXT="bisher nur intern von den Time-Entit&#xe4;ten verwendet"/>
</node>
<node CREATED="1748436314956" ID="ID_135965943" MODIFIED="1748436317043" TEXT="lumiera_framecount_to_time">
<node CREATED="1748436317043" ID="ID_1149266151" MODIFIED="1748436333277" TEXT="eigentlich einfach &#x2014; aber tricky"/>
<node CREATED="1748436335001" ID="ID_21451369" MODIFIED="1748436352201" TEXT="auch verwendet von NTSC Drop-frame">
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1748436444348" ID="ID_37784063" MODIFIED="1748436473941" TEXT="verwendet eine FrameRate als Argument &#x27f9; geh&#xf6;rt in Klasse FrameRate"/>
<node CREATED="1748436579524" ID="ID_47238779" MODIFIED="1748436664963" TEXT="ist bereits Baiss-Implementierung des Konstrukturs Offset{cnt, rate}">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1748436604184" ID="ID_1226697297" MODIFIED="1748436673076" TEXT="FrameCount::duration()">
<icon BUILTIN="back"/>
</node>
</node>
<node CREATED="1748437205734" ID="ID_1510698290" MODIFIED="1748437216165" TEXT="lumiera_build_time">
<node CREATED="1748437218033" ID="ID_1983858379" MODIFIED="1748437242305" TEXT="effektiv ist das der Konstrutor Time(ms,s,min,h)"/>
<node CREATED="1748437245750" ID="ID_1962316549" MODIFIED="1748437254099" TEXT="sonst jetzt keine Verwendung mehr">
<icon BUILTIN="ksmiletris"/>
</node>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1748436687122" ID="ID_498539691" MODIFIED="1748436742026" STYLE="bubble" TEXT="k&#xf6;nnen &#xbb;wegdiskutiert werden&#xab; &#x27f6; Time-Entit&#xe4;ten">
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="yes"/>
</node>
</node>
<node COLOR="#338800" CREATED="1748436052160" ID="ID_332930192" MODIFIED="1748479008869" TEXT="Zeit-Quantisierung">
<icon BUILTIN="button_ok"/>
<node CREATED="1748436068486" ID="ID_236415016" MODIFIED="1748451420529" TEXT="lumiera_quantise_frames">
<arrowlink COLOR="#4e8ead" DESTINATION="ID_830480651" ENDARROW="Default" ENDINCLINATION="153;-10;" ID="Arrow_ID_875820569" STARTARROW="None" STARTINCLINATION="503;54;"/>
</node>
<node CREATED="1748436074681" ID="ID_867414214" MODIFIED="1748436074681" TEXT="lumiera_quantise_time"/>
<node CREATED="1748436080004" ID="ID_308010594" MODIFIED="1748436080004" TEXT="lumiera_time_of_gridpoint"/>
<node BACKGROUND_COLOR="#b6c8ba" COLOR="#435e98" CREATED="1748436089328" HGAP="24" ID="ID_1017613960" MODIFIED="1748478999996" STYLE="bubble" TEXT="reine Implementierung &#x27f6; quantiser.cpp" VSHIFT="13">
<icon BUILTIN="yes"/>
<node CREATED="1748441710788" ID="ID_929357775" MODIFIED="1748441736850" STYLE="fork" TEXT="sogar umformulieren in Time-Entities">
<font NAME="SansSerif" SIZE="10"/>
</node>
<node CREATED="1748441710788" ID="ID_1159666529" MODIFIED="1748441755465" STYLE="fork" TEXT="als private static in FixedFrameQuantiser">
<font NAME="SansSerif" SIZE="10"/>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1748441710788" ID="ID_830480651" MODIFIED="1748451436980" STYLE="fork" TEXT="grid_aligned() wird sogar zum Implementierungs-Kern">
<arrowlink COLOR="#7aabe4" DESTINATION="ID_762146581" ENDARROW="Default" ENDINCLINATION="-166;9;" ID="Arrow_ID_160880569" STARTARROW="None" STARTINCLINATION="53;127;"/>
<linktarget COLOR="#4e8ead" DESTINATION="ID_830480651" ENDARROW="Default" ENDINCLINATION="153;-10;" ID="Arrow_ID_875820569" SOURCE="ID_236415016" STARTARROW="None" STARTINCLINATION="503;54;"/>
<font NAME="SansSerif" SIZE="10"/>
<node COLOR="#434698" CREATED="1748451451319" HGAP="25" ID="ID_439737233" MODIFIED="1748451501391" TEXT="viel besser &#x27f8; klarer &#x2227; mit limit-check" VSHIFT="10">
<font NAME="SansSerif" SIZE="11"/>
<icon BUILTIN="idea"/>
</node>
</node>
</node>
<node CREATED="1748442444116" ID="ID_1724729549" MODIFIED="1748442507692" TEXT="Drop-Frame-Implementierung ebenfalls umziehen">
<linktarget COLOR="#6361b7" DESTINATION="ID_1724729549" ENDARROW="Default" ENDINCLINATION="1281;74;" ID="Arrow_ID_444441914" SOURCE="ID_1472070007" STARTARROW="None" STARTINCLINATION="771;44;"/>
<icon BUILTIN="yes"/>
<node CREATED="1748443770239" HGAP="27" ID="ID_900416096" MODIFIED="1748443794005" TEXT="verwendet erweiterte Variante von grid_aligned" VSHIFT="37">
<icon BUILTIN="info"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1748443795974" ID="ID_1475460893" MODIFIED="1748443808643" TEXT="AUA: in den wenigen Zeilen stecken zwei Bugs">
<icon BUILTIN="broken-line"/>
</node>
<node CREATED="1748443855397" ID="ID_1688939667" MODIFIED="1748443866719" TEXT="ziehe erst mal den Origin-Offset raus"/>
<node COLOR="#5b280f" CREATED="1748444585733" ID="ID_1996155038" MODIFIED="1748468854134" TEXT="k&#xf6;nnte stets die erweiterte Variante verwenden">
<icon BUILTIN="button_cancel"/>
<node CREATED="1748468860305" ID="ID_1750630451" MODIFIED="1748468870864" TEXT="ist nicht sinnvoll"/>
<node CREATED="1748468871585" ID="ID_1915828426" MODIFIED="1748468899785" TEXT="der einfache Standardfall braucht keine Framerate"/>
<node CREATED="1748468900420" ID="ID_1493748826" MODIFIED="1748468930405" TEXT="und ist auch nicht von Overflows bedroht">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Zeiten sind bereits &#181;-Tick-quantisiert, d.h. eine einfache Division &#252;ber ein Grid ist stets im Integer-Value range und ohne Fehler ausf&#252;hrbar
</p>
</body>
</html>
</richcontent>
</node>
</node>
<node COLOR="#338800" CREATED="1748449969608" ID="ID_762146581" MODIFIED="1748468816509" TEXT="diese aber neu schreiben &#x27f5; util::reQuant nutzen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
und zwar wegen der Gefahr numerischer Overflows; die eingebaute Limitierung der Lumiera-Time ist <i>nicht ausreichend:</i>&#160;denn die Quantisierung mu&#223; die Framerate durch das &#181;-Grid dividieren, also den Z&#228;hler mal 10^6 nehmen
</p>
</body>
</html>
</richcontent>
<linktarget COLOR="#7aabe4" DESTINATION="ID_762146581" ENDARROW="Default" ENDINCLINATION="-166;9;" ID="Arrow_ID_160880569" SOURCE="ID_830480651" STARTARROW="None" STARTINCLINATION="53;127;"/>
</node>
<node COLOR="#435e98" CREATED="1748452649267" FOLDED="true" ID="ID_1766809889" MODIFIED="1748478967926" TEXT="test Failures">
<icon BUILTIN="broken-line"/>
<node CREATED="1748452670184" ID="ID_124809807" MODIFIED="1748452679547" TEXT="QuantiserBasics_test (und diverse andere)"/>
<node CREATED="1748454238651" ID="ID_433970184" MODIFIED="1748454264048" TEXT="habe beim Umbau Framerate und Grid-Duration verwechselt"/>
<node CREATED="1748454328068" ID="ID_555997242" MODIFIED="1748468961571" TEXT="QuantiserBasics_test : Grenzfall-Bedingung nicht mehr erf&#xfc;llt">
<node CREATED="1748470251934" ID="ID_1257197599" MODIFIED="1748470264939" TEXT="Limitierungen sind nun signifikant anders"/>
<node CREATED="1748470265887" ID="ID_1915675840" MODIFIED="1748471065332" TEXT="wir geben nun effektiv einen Offset heraus">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...und das macht durchaus Sinn, grade wegen der starken Limitierung, die ich bisher in diesem Fall demonstriert habe; au&#223;erdem ist diese grid-local-Time ohnehin etwas sonderbar, und auch daher ist es sinnvoll, diese als Offset (gegen&#252;ber dem Origin) zu modellieren. Sofern der Benutzer in eine normale Lumiera-Time speichert, sind wir wieder zur&#252;ck bei den alten Limitierungen, aber man kann eben mit diesem Offset auch weiterrechnen, und ihn z.B. zum Origin dazuaddieren (und w&#252;rde dann im Beispiel wieder bei ganz kleinen Zeiten ankommen)
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1748470276454" ID="ID_1578018466" MODIFIED="1748470869371" TEXT="und dieser ist limitiert auf &#xb1; Duration::MAX"/>
<node CREATED="1748470870415" ID="ID_1224045565" MODIFIED="1748470884416" TEXT="&#x27f9; Grid-API entspreichend anpassen"/>
<node CREATED="1748472727015" ID="ID_762218159" MODIFIED="1748472735878" TEXT="mu&#xdf; dann den Test umschreiben">
<node CREATED="1748472736947" ID="ID_1167982787" MODIFIED="1748472745825" TEXT="m&#xf6;chte aber das Prinzip der Demonstration beibehalten"/>
<node CREATED="1748472746714" ID="ID_1982316622" MODIFIED="1748472860999" TEXT="mu&#xdf; daf&#xfc;r Hilfskonstanten einf&#xfc;hren">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...und die Grenzen ausreizen, die das Zeit-Framework (bewu&#223;t) erlaubt, wenn man die Typisierung geschickt ausnutzt: denn ein TimeValue wird aus einer anderen Zeit-Enit&#228;t (absichtlich) ohne weiteren Bounds-Check &#252;bernommen. Es sind mithin durchaus TimeValue m&#246;glich, die gr&#246;&#223;er sind als Time::MAX
</p>
</body>
</html>
</richcontent>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1748478811956" ID="ID_1509469989" MODIFIED="1748478819810" TEXT="Time SUB_MIN{-Duration::MAX};"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1748478811956" ID="ID_315980503" MODIFIED="1748478819809" TEXT="Time SUP_MAX{ Duration::MAX};"/>
</node>
<node CREATED="1748472909503" ID="ID_1458117682" MODIFIED="1748472936823" TEXT="die Limitierung passiert nun bereits beim Berchnen des Offset zum Origin"/>
<node CREATED="1748472937481" ID="ID_1805019467" MODIFIED="1748472956687" TEXT="das ist viel besser so &#x27f9; denn damit liegt jedes Ergebnis stets auf dem Grid">
<icon BUILTIN="ksmiletris"/>
</node>
</node>
<node BACKGROUND_COLOR="#e9d3ae" COLOR="#a50125" CREATED="1748478829786" ID="ID_1611930745" MODIFIED="1748478859145" TEXT="es gibt hier eine Art L&#xfc;cke im Sicherheitsnetz">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1748478862377" ID="ID_591567262" MODIFIED="1748478874096" TEXT="TimeVar &#xfc;bernimmt eine andere Zeit-Entit&#xe4;t"/>
<node CREATED="1748478874828" ID="ID_1454599373" MODIFIED="1748478883719" TEXT="und macht direkte Integer-Arithmetik"/>
<node CREATED="1748478884351" ID="ID_1695859043" MODIFIED="1748478892386" TEXT="erst im n&#xe4;chsten Konstruktur wird wieder gelimited"/>
<node CREATED="1748478896305" ID="ID_381040546" MODIFIED="1748478930393" TEXT="&#x27f9; das f&#xfc;hrt dazu, da&#xdf; die Berechnungen Duration::MAX * 2 erreichen k&#xf6;nnen"/>
<node CREATED="1748478931881" ID="ID_1630974569" MODIFIED="1748478955275" TEXT="(aber nicht zuletzt deshalb bin ich um den Faktor 10 unter die tats&#xe4;chlichen Grenzen gegangen"/>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
</node>