Timeline: ZoomWindow implementation draft
implement the first test case: nudge the zoom factor ⟹ scale factor doubled ⟹ visible window reduced to half size ⟹ visible window placed in the middle of the overall range
This commit is contained in:
parent
b3fe6e16c6
commit
7145d0d9ce
6 changed files with 255 additions and 92 deletions
|
|
@ -102,7 +102,7 @@ extern "C" { /* ===================== C interface ======================== */
|
|||
* as zero. Each interval includes its lower bound, but excludes its upper bound.
|
||||
* @param grid spacing of the grid intervals, measured in GAVL_TIME_SCALE
|
||||
* @return number of the grid interval containing the given time.
|
||||
* @warning the resulting value is limited to (Time::Min, Time::MAX)
|
||||
* @warning the resulting value is limited to (Time::Min, Time::MAX)
|
||||
*/
|
||||
int64_t
|
||||
lumiera_quantise_frames (gavl_time_t time, gavl_time_t origin, gavl_time_t grid);
|
||||
|
|
@ -112,12 +112,12 @@ lumiera_quantise_frames_fps (gavl_time_t time, gavl_time_t origin, uint framerat
|
|||
|
||||
/**
|
||||
* Similar to #lumiera_quantise_frames, but returns a grid aligned \em time value
|
||||
* @return time of start of the grid interval containing the given 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
|
||||
* clipped, because the result, relative to origin, needs to be <= Time::MAX
|
||||
*/
|
||||
gavl_time_t
|
||||
lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
* *****************************************************/
|
||||
|
||||
|
||||
/** @file time.cpp
|
||||
/** @file time.cpp
|
||||
** Lumiera time handling core implementation unit.
|
||||
** This translation unit generates code for the Lumiera internal time wrapper,
|
||||
** based on gavl_time_t, associated constants, marker classes for the derived
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
**
|
||||
** Client code includes either time.h (for basics and conversion functions)
|
||||
** or timevalue.hpp (for the time entities), timequant.hpp for grid aligned
|
||||
** time values or timecode.hpp
|
||||
** time values or timecode.hpp
|
||||
**
|
||||
** @see Time
|
||||
** @see TimeValue
|
||||
|
|
@ -301,7 +301,7 @@ lumiera_tmpbuf_print_time (gavl_time_t time)
|
|||
|
||||
char *buffer = lumiera_tmpbuf_snprintf(64, "%s%01d:%02d:%02d.%03d",
|
||||
negative ? "-" : "", hours, minutes, seconds, milliseconds);
|
||||
|
||||
|
||||
ENSURE(buffer != NULL);
|
||||
return buffer;
|
||||
}
|
||||
|
|
@ -352,7 +352,7 @@ namespace { // implementation: basic frame quantisation....
|
|||
const int64_t limit_den = std::numeric_limits<gavl_time_t>::max() / framerate_divisor;
|
||||
const int64_t microScale {lib::time::TimeValue::SCALE};
|
||||
|
||||
// protect against numeric overflow
|
||||
// protect against numeric overflow
|
||||
if (abs(time) < limit_num && microScale < limit_den)
|
||||
{
|
||||
// safe to calculate "time * framerate"
|
||||
|
|
@ -393,7 +393,7 @@ lumiera_quantise_time (gavl_time_t time, gavl_time_t origin, gavl_time_t grid)
|
|||
gavl_time_t
|
||||
lumiera_time_of_gridpoint (int64_t nr, gavl_time_t origin, gavl_time_t grid)
|
||||
{
|
||||
gavl_time_t offset = nr * grid;
|
||||
gavl_time_t offset = nr * grid;
|
||||
return origin + offset;
|
||||
}
|
||||
|
||||
|
|
@ -462,12 +462,12 @@ namespace { // implementation helper
|
|||
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;
|
||||
|
||||
|
||||
|
||||
/** reverse the drop-frame calculation
|
||||
* @param time absolute time value in micro ticks
|
||||
* @param time absolute time value in micro ticks
|
||||
* @return the absolute frame number using NTSC drop-frame encoding
|
||||
* @todo I doubt this works correct for negative times!!
|
||||
* @todo I doubt this works correct for negative times!!
|
||||
*/
|
||||
inline int64_t
|
||||
calculate_drop_frame_number (gavl_time_t time)
|
||||
|
|
@ -479,7 +479,7 @@ namespace { // implementation helper
|
|||
|
||||
// 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:
|
||||
// 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;
|
||||
|
|
@ -523,6 +523,6 @@ lumiera_build_time_ntsc_drop (uint frames, uint secs, uint mins, uint hours)
|
|||
gavl_time_t 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
|
||||
result += 1; // without this adjustment the frame number
|
||||
return result; // would turn out off by -1 on back conversion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,14 +23,47 @@
|
|||
/** @file timevalue.hpp
|
||||
** a family of time value like entities and their relationships.
|
||||
** This is the foundation for the Lumiera time handling framework. On the implementation
|
||||
** level, time values are represented as 64bit integer values \c gavl_time_t. But for the
|
||||
** level, time values are represented as 64bit integer values `gavl_time_t`. But for the
|
||||
** actual use, we create several kinds of time "values", based on their logical properties.
|
||||
** These time values are considered to be fixed (immutable) values, which may only be
|
||||
** created through some well defined construction paths, and any time based calculation
|
||||
** created through some limited construction paths, and any time based calculation
|
||||
** is forced to go through our time calculation library. This is prerequisite for
|
||||
** the definition of <i>frame aligned</i> time values and time code representation
|
||||
** the definition of _frame aligned_ time values and time code representation
|
||||
** implemented as display format based on these frame quantised time values.
|
||||
**
|
||||
** # Time entities
|
||||
**
|
||||
** The value types defined in this header represent time points and time intervals
|
||||
** based on an internal time scale (µs ticks) and not related to any known fixed time
|
||||
** zone or time base; rather they are interpreted in usage context, and the only way
|
||||
** to retrieve such a value is by formatting it into a time code format.
|
||||
**
|
||||
** The lib::time::TimeValue serves as foundation for all further time calculations;
|
||||
** in fact it is implemented as a single 64bit µ-tick value (`gavl_time_t`). The
|
||||
** further time entities are implemented as value objects (without virtual functions):
|
||||
** - lib::time::Time represents a time instant and is the reference for any usage
|
||||
** - lib::time::TimeVar is a mutable time variable and can be used for calculations
|
||||
** - lib::time::Offset can be used to express a positive or negative shift on time scale
|
||||
** - lib::time::Duration represents the extension or an amount of time
|
||||
** - lib::time::TimeSpan represents a distinct interval, with start time and duration
|
||||
** - lib::time::FrameRate can be used to mark a number to denote a frames-per-second spec
|
||||
** - lib::time::FSecs is a rational number to represent seconds or fractions thereof
|
||||
**
|
||||
** # Manipulating time values
|
||||
**
|
||||
** Time values are conceived as fixed, immutable entities, similar to numbers; you can't
|
||||
** just change the number two, and likewise, two seconds are two seconds. However, for
|
||||
** many use cases we have to combine time values to perform calculations
|
||||
** - Time entities can be combined with operators, to form new time entities
|
||||
** - the TimeVar can be used as accumulator or variable for ongoing calculations
|
||||
** - since TimeSpan, Duration (and the grid-aligned, "quantised" flavours) will often
|
||||
** represent some time-like property or entity, e.g. the temporal specification of
|
||||
** a media Clip with start and duration, there is the concept of an explicit *mutation*,
|
||||
** which is _accepted_ by these entities. Notably the lib::time::Control can be attached
|
||||
** to these entities, and can then receive manipulations (nudging, offset); moreover it
|
||||
** is possible to attach as listener to such a "controller" and be notified by any
|
||||
** manipulation; this setup is the base for running time display, playback cursors etc.
|
||||
**
|
||||
** @see time.h basic time calculation library functions
|
||||
** @see timequant.hpp
|
||||
** @see TimeValue_test
|
||||
|
|
@ -75,7 +108,7 @@ namespace time {
|
|||
* @note clients should prefer to use Time instances,
|
||||
* which explicitly denote an Lumiera internal
|
||||
* time value and are easier to use.
|
||||
* @see TimeVar when full arithmetics are required
|
||||
* @see TimeVar when full arithmetics are required
|
||||
*/
|
||||
class TimeValue
|
||||
: boost::totally_ordered<TimeValue,
|
||||
|
|
@ -107,7 +140,7 @@ namespace time {
|
|||
static gavl_time_t limited (gavl_time_t raw);
|
||||
|
||||
|
||||
explicit
|
||||
explicit
|
||||
TimeValue (gavl_time_t val=0) ///< time given in µ ticks here
|
||||
: t_(limited (val))
|
||||
{ }
|
||||
|
|
@ -134,58 +167,6 @@ namespace time {
|
|||
|
||||
|
||||
|
||||
/** a mutable time value,
|
||||
* behaving like a plain number,
|
||||
* allowing copy and re-accessing
|
||||
* @note supports scaling by a factor,
|
||||
* which \em deliberately is chosen
|
||||
* as int, not gavl_time_t, because the
|
||||
* multiplying of times is meaningless.
|
||||
*/
|
||||
class TimeVar
|
||||
: public TimeValue
|
||||
, boost::additive<TimeVar,
|
||||
boost::additive<TimeVar, TimeValue,
|
||||
boost::multipliable<TimeVar, int>
|
||||
> >
|
||||
{
|
||||
public:
|
||||
TimeVar (TimeValue const& time = TimeValue())
|
||||
: TimeValue(time)
|
||||
{ }
|
||||
|
||||
// Allowing copy and assignment
|
||||
TimeVar (TimeVar const& o)
|
||||
: TimeValue(o)
|
||||
{ }
|
||||
|
||||
TimeVar&
|
||||
operator= (TimeValue const& o)
|
||||
{
|
||||
t_ = TimeVar(o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Support mixing with plain long int arithmetics
|
||||
operator gavl_time_t () const { return t_; }
|
||||
|
||||
// Supporting additive
|
||||
TimeVar& operator+= (TimeVar const& tx) { t_ += tx.t_; return *this; }
|
||||
TimeVar& operator-= (TimeVar const& tx) { t_ -= tx.t_; return *this; }
|
||||
|
||||
// Supporting multiplication with integral factor
|
||||
TimeVar& operator*= (int64_t fact) { t_ *= fact; return *this; }
|
||||
|
||||
// Supporting sign flip
|
||||
TimeVar operator- () const { return TimeVar(*this)*=-1; }
|
||||
|
||||
// baseclass TimeValue is already totally_ordered
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -208,7 +189,65 @@ namespace time {
|
|||
typedef boost::rational<int64_t> FSecs;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
/** a mutable time value,
|
||||
* behaving like a plain number,
|
||||
* allowing copy and re-accessing
|
||||
* @note supports scaling by a factor,
|
||||
* which _deliberately_ is chosen
|
||||
* as int, not gavl_time_t, because the
|
||||
* multiplying of times is meaningless.
|
||||
*/
|
||||
class TimeVar
|
||||
: public TimeValue
|
||||
, boost::additive<TimeVar,
|
||||
boost::additive<TimeVar, TimeValue,
|
||||
boost::multipliable<TimeVar, int>
|
||||
> >
|
||||
{
|
||||
public:
|
||||
TimeVar (TimeValue const& time = TimeValue())
|
||||
: TimeValue(time)
|
||||
{ }
|
||||
|
||||
/** Allow to pick up precise fractional seconds
|
||||
* @warning truncating fractional µ-ticks */
|
||||
TimeVar (FSecs const&);
|
||||
|
||||
/// Allowing copy and assignment
|
||||
TimeVar (TimeVar const& o)
|
||||
: TimeValue(o)
|
||||
{ }
|
||||
|
||||
TimeVar&
|
||||
operator= (TimeValue const& o)
|
||||
{
|
||||
t_ = TimeVar(o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Support mixing with plain long int arithmetics
|
||||
operator gavl_time_t() const { return t_; }
|
||||
/// Support for micro-tick precise time arithmetics
|
||||
operator FSecs() const { return FSecs{t_, TimeValue::SCALE}; }
|
||||
|
||||
/// Supporting additive
|
||||
TimeVar& operator+= (TimeVar const& tx) { t_ += tx.t_; return *this; }
|
||||
TimeVar& operator-= (TimeVar const& tx) { t_ -= tx.t_; return *this; }
|
||||
|
||||
/// Supporting multiplication with integral factor
|
||||
TimeVar& operator*= (int64_t fact) { t_ *= fact; return *this; }
|
||||
|
||||
/// Supporting sign flip
|
||||
TimeVar operator- () const { return TimeVar(*this)*=-1; }
|
||||
|
||||
// baseclass TimeValue is already totally_ordered
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**********************************************************//**
|
||||
* Lumiera's internal time value datatype.
|
||||
* This is a TimeValue, but now more specifically denoting
|
||||
* a point in time, measured in reference to an internal
|
||||
|
|
@ -216,7 +255,7 @@ namespace time {
|
|||
*
|
||||
* Lumiera Time provides some limited capabilities for
|
||||
* direct manipulation; Time values can be created directly
|
||||
* from \c (ms,sec,min,hour) specification and there is an
|
||||
* from `(ms,sec,min,hour)` specification and there is an
|
||||
* string representation intended for internal use (reporting
|
||||
* and debugging). Any real output, formatting and persistent
|
||||
* storage should be based on the (quantised) timecode
|
||||
|
|
@ -291,14 +330,14 @@ namespace time {
|
|||
* but derived classes allow some limited mutation
|
||||
* through special API calls */
|
||||
Offset&
|
||||
operator= (Offset const& o)
|
||||
operator= (Offset const& o)
|
||||
{
|
||||
TimeValue::operator= (o);
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit
|
||||
explicit
|
||||
Offset (TimeValue const& distance =Time::ZERO)
|
||||
: TimeValue(distance)
|
||||
{ }
|
||||
|
|
@ -367,7 +406,7 @@ namespace time {
|
|||
inline Offset
|
||||
Offset::operator- () const
|
||||
{
|
||||
return -1 * (*this);
|
||||
return -1 * (*this);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -377,8 +416,8 @@ namespace time {
|
|||
* Duration is the internal Lumiera time metric.
|
||||
* It is an absolute (positive) value, but can be
|
||||
* promoted from an offset. While Duration generally
|
||||
* is treated as immutable value, there is the
|
||||
* possibility to send a \em Mutation message.
|
||||
* is treated as immutable value, there is the
|
||||
* possibility to send a _Mutation message_.
|
||||
*/
|
||||
class Duration
|
||||
: public TimeValue
|
||||
|
|
@ -446,7 +485,7 @@ namespace time {
|
|||
inline Offset
|
||||
Duration::operator- () const
|
||||
{
|
||||
return -1 * (*this);
|
||||
return -1 * (*this);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -500,13 +539,13 @@ namespace time {
|
|||
|
||||
|
||||
Duration&
|
||||
duration()
|
||||
duration()
|
||||
{
|
||||
return dur_;
|
||||
}
|
||||
|
||||
Duration
|
||||
duration() const
|
||||
duration() const
|
||||
{
|
||||
return dur_;
|
||||
}
|
||||
|
|
@ -607,11 +646,16 @@ namespace time {
|
|||
inline gavl_time_t
|
||||
TimeValue::limited (gavl_time_t raw)
|
||||
{
|
||||
return raw > Time::MAX? Time::MAX.t_
|
||||
: raw < Time::MIN? Time::MIN.t_
|
||||
return raw > Time::MAX? Time::MAX.t_
|
||||
: raw < Time::MIN? Time::MIN.t_
|
||||
: raw;
|
||||
}
|
||||
|
||||
inline
|
||||
TimeVar::TimeVar (FSecs const& fractionalSeconds)
|
||||
: TimeVar{Time(fractionalSeconds)}
|
||||
{ }
|
||||
|
||||
inline
|
||||
Duration::Duration (TimeSpan const& interval)
|
||||
: TimeValue(interval.duration())
|
||||
|
|
@ -645,7 +689,7 @@ namespace time {
|
|||
|
||||
|
||||
namespace util {
|
||||
|
||||
|
||||
inline bool
|
||||
isnil (lib::time::Duration const& dur)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,14 +22,17 @@
|
|||
|
||||
|
||||
/** @file zoom-window.hpp
|
||||
** Abstraction: a multi-dimensional extract from model space into screen coordinates.
|
||||
** This is a generic component to represent and handle the zooming and positioning of
|
||||
** views within an underlying model space. This model space is conceived to be two fold:
|
||||
** Abstraction: the current zoom- and navigation state of a view, possibly in multiple
|
||||
** dimensions. This is a generic component to represent and handle the zooming and
|
||||
** positioning of views within an underlying model space. This model space is conceived
|
||||
** to be two fold:
|
||||
** - it is a place or excerpt within the model topology (e.g. the n-th track in the fork)
|
||||
** - it has a temporal extension within a larger temporal frame (e.g. some seconds within
|
||||
** the timeline)
|
||||
** This component is called »Zoom Window«, since it represents a window-like local visible
|
||||
** interval, embedded into a larger time span covering the whole timeline.
|
||||
** interval, embedded into a larger time span covering a complete timeline.
|
||||
** @note as of 10/2022 this component is in an early stage of development and just used
|
||||
** to coordinate the horizontal extension of the timeline view.
|
||||
**
|
||||
** # Rationale
|
||||
**
|
||||
|
|
@ -98,6 +101,10 @@ namespace model {
|
|||
using lib::time::FSecs;
|
||||
using lib::time::Time;
|
||||
|
||||
namespace {
|
||||
/** the deepest zoom is to use 2px per micro-tick */
|
||||
const uint ZOOM_MAX_RESOLUTION = 2 * TimeValue::SCALE;
|
||||
}
|
||||
|
||||
/**
|
||||
* A component to ensure uniform handling of zoom scale
|
||||
|
|
@ -105,6 +112,13 @@ namespace model {
|
|||
* the mutator functions are validated and harmonised to
|
||||
* meet the internal invariants; a change listener is
|
||||
* possibly notified to pick up the new settings.
|
||||
*
|
||||
* A ZoomWindow...
|
||||
* - is a #visible TimeSpan
|
||||
* - which is completely inside an #overalSpan
|
||||
* - and is rendered at a scale factor #px_per_sec
|
||||
* - 0 < px_per_sec <= ZOOM_MAX_RESOLUTION
|
||||
* - zoom operations are applied around an #anchorPoint
|
||||
*/
|
||||
class ZoomWindow
|
||||
: util::NonCopyable
|
||||
|
|
@ -152,7 +166,12 @@ namespace model {
|
|||
void
|
||||
nudgeMetric (int steps)
|
||||
{
|
||||
UNIMPLEMENTED ("nudgeMetric");
|
||||
uint changedScale =
|
||||
steps > 0 ? px_per_sec_ << steps
|
||||
: px_per_sec_ >> -steps;
|
||||
if (0 < changedScale
|
||||
and changedScale <= ZOOM_MAX_RESOLUTION)
|
||||
mutateScale (changedScale);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -239,7 +258,39 @@ namespace model {
|
|||
void
|
||||
mutateScale (uint px_per_sec)
|
||||
{
|
||||
UNIMPLEMENTED ("change scale factor, validate and adjust all params");
|
||||
if (px_per_sec == 0) px_per_sec = 1;
|
||||
if (px_per_sec == px_per_sec_) return;
|
||||
|
||||
FSecs changeFactor{px_per_sec, px_per_sec_};
|
||||
FSecs dur{afterWin_ - startWin_};
|
||||
dur /= changeFactor;
|
||||
if (dur > FSecs{afterAll_ - startAll_})
|
||||
{// limit to the overall timespan...
|
||||
px_per_sec_ = adjustedScale (startAll_,afterAll_, startWin_,afterWin_);
|
||||
startWin_ = startAll_;
|
||||
afterWin_ = afterAll_;
|
||||
}
|
||||
else
|
||||
{
|
||||
TimeVar start{anchorPoint() - dur*relativeAnchor()};
|
||||
if (start < startAll_)
|
||||
start = startAll_;
|
||||
TimeVar after{start + dur};
|
||||
if (after > afterAll_)
|
||||
{
|
||||
after = afterAll_;
|
||||
start = afterAll_ - dur;
|
||||
}
|
||||
ASSERT (after-start <= afterAll_-startAll_);
|
||||
|
||||
if (start == startWin_ and after == afterWin_)
|
||||
return; // nothing changed effectively
|
||||
|
||||
px_per_sec_ = adjustedScale (start,after, startWin_,afterWin_);
|
||||
startWin_ = start;
|
||||
afterWin_ = after;
|
||||
}
|
||||
fireChangeNotification();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -247,6 +298,68 @@ namespace model {
|
|||
{
|
||||
UNIMPLEMENTED ("change visible duration, validate and adjust all params");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adjust the display scale such as to match the given changed time interval
|
||||
* @param startNew changed start point
|
||||
* @param afterNew changed end point
|
||||
* @param startOld previous start point
|
||||
* @param afterOld previous end point
|
||||
* @return adapted scale factor in pixel per second, rounded half up to the next pixel.
|
||||
*/
|
||||
uint
|
||||
adjustedScale (TimeVar startNew, TimeVar afterNew, TimeVar startOld, TimeVar afterOld)
|
||||
{
|
||||
REQUIRE (startOld < afterOld);
|
||||
FSecs factor = FSecs{afterNew - startNew} / FSecs{afterOld - startOld};
|
||||
return boost::rational_cast<uint>(px_per_sec_ / factor + 1/2); // rounding half pixels
|
||||
}
|
||||
|
||||
/**
|
||||
* The anchor point or centre for zooming operations applied to the visible window
|
||||
* @return where the visible window should currently be anchored
|
||||
* @remark this point can sometimes be outside the current visible window,
|
||||
* but any further zooming/scaling/scrolling operation should bring it back
|
||||
* into sight. Moreover, the function #relativeAnchor() defines the position
|
||||
* where this anchor point _should_ be placed relative to the visible window.
|
||||
* @todo 10/2022 we use a numerical rule currently, but that could be contextual state,
|
||||
* like e.g. the current position of the play head or edit cursor or mouse.
|
||||
*/
|
||||
FSecs
|
||||
anchorPoint() const
|
||||
{
|
||||
return startWin_ + FSecs{afterWin_-startWin_} * relativeAnchor();
|
||||
}
|
||||
|
||||
/**
|
||||
* define at which proportion to the visible window's duration the anchor should be placed
|
||||
* @return a fraction 0 ... 1, where 0 means at start and 1 means after end.
|
||||
* @note as of 10/2022 we use a numerical rule to place the anchor point in accordance
|
||||
* to the current visible window's position within the overall timeline; if it's
|
||||
* close to the beginning, the anchor point is also rather to the beginning...
|
||||
*/
|
||||
FSecs
|
||||
relativeAnchor() const
|
||||
{
|
||||
// the visible window itself has to fit in, which reduces the action range
|
||||
FSecs possibleRange = (afterAll_-startAll_) - (afterWin_-startWin_);
|
||||
if (possibleRange == 0) // if there is no room for scrolling...
|
||||
return FSecs{1,2}; // then anchor zooming in the middle
|
||||
|
||||
// use a 3rd degree parabola to favour positions in the middle
|
||||
FSecs posFactor = FSecs{startWin_-startAll_} / possibleRange;
|
||||
posFactor = (2*posFactor - 1); // -1 ... +1
|
||||
posFactor = posFactor*posFactor*posFactor; // -1 ... +1 but accelerating towards boundraries
|
||||
posFactor = (posFactor + 1) / 2; // 0 ... 1
|
||||
return posFactor;
|
||||
}
|
||||
|
||||
void
|
||||
fireChangeNotification()
|
||||
{
|
||||
TODO("really fire...");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ namespace test {
|
|||
|
||||
zoomWin.nudgeMetric(+1);
|
||||
CHECK (zoomWin.px_per_sec() == 50);
|
||||
CHECK (zoomWin.visible() == TimeSpan(Time(FSecs(23,4)), Time(FSecs(23,2))));
|
||||
CHECK (zoomWin.visible() == TimeSpan(Time(FSecs(23,4)), FSecs(23,2)));
|
||||
CHECK (zoomWin.overallSpan() == TimeSpan(Time::ZERO, Time(FSecs(23))));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38379,8 +38379,14 @@
|
|||
</html></richcontent>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1666966669487" ID="ID_1477573565" MODIFIED="1666967820569" TEXT="testgetrieben entwickelt">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1666966669487" ID="ID_1477573565" MODIFIED="1667093086439" TEXT="testgetrieben entwickelt">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1667093097050" ID="ID_1662709264" MODIFIED="1667093103756" TEXT="verify_simpleUsage">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1667093106907" ID="ID_1255657275" MODIFIED="1667093118644" TEXT="nudge Zoom-Faktor"/>
|
||||
<node CREATED="1667093120846" ID="ID_1405458968" MODIFIED="1667093134771" TEXT="⟹ doppelte Auflösung"/>
|
||||
<node CREATED="1667093136007" ID="ID_1309995367" MODIFIED="1667093153233" TEXT="⟹ visible Window liegt in der Mitte und hat hable Länge"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1666913274942" ID="ID_4743528" MODIFIED="1666913286194" TEXT="Design überprüfen">
|
||||
|
|
|
|||
Loading…
Reference in a new issue