lumiera_/src/lib/time/mutation.cpp

372 lines
11 KiB
C++
Raw Normal View History

/*
Mutation - changing and adjusting time values
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
Copyright (C)
2011, Hermann Vosseler <Ichthyostega@web.de>
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
  **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
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version. See the file COPYING for further details.
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
* *****************************************************************/
2012-12-02 01:54:02 +01:00
/** @file mutation.cpp
** A collection of concrete forms of time mutation.
** This compilation unit defines some "mutators" based on explicitly given time values,
** to be applied to time durations, time spans and quantised time values. The intended
** usage is to build these "mutators" inline through use of the corresponding static
** builder functions defined in class Mutation. All the mutators defined here are
** "polymorphic value objects", i.e. they can be treated like simple time values, while
** actually hiding the concrete implementation and just exposing the #Mutation interface.
**
** @remarks these mutators are for casual use at places where you'd intuitively just want
** set a simple more or less hard wired value. The benefit of this seemingly complicated
** solution is that all the tricky corner cases for grid aligned time values will be
** treated automatically. Incidentally, there is another, quite different use case
** for time mutations, when it comes to receive continuous and ongoing changes to
** a time value (e.g. a cursor). This separate use case is handled by time::Control
**
** @see time::Control
** @see lib::PolymorphicValue
** @see TimeMutation_test
** @see TimeControl_test
**
*/
#include "lib/error.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/time/timequant.hpp"
#include "lib/time/timecode.hpp"
#include "lib/time/mutation.hpp"
#include "lib/util-quant.hpp"
namespace error = lumiera::error;
namespace lib {
namespace time {
LUMIERA_ERROR_DEFINE (INVALID_MUTATION, "Changing a time value in this way was not designated");
Mutation::~Mutation() { } // emit VTable here....
2012-12-02 01:54:02 +01:00
/* Warning: all the classes defined here must be of size below MUTATION_IMPL_SIZE */
2012-12-02 01:54:02 +01:00
/**
* concrete time value mutation:
* impose fixed new start time.
*/
class SetNewStartTimeMutation
: public ClonableMutation
{
TimeValue newTime_;
virtual void
change (Duration&) const
{
throw error::Logic ("mutating the start point of a pure Duration doesn't make sense"
, LUMIERA_ERROR_INVALID_MUTATION);
}
virtual void
change (TimeSpan& target) const
{
imposeChange (target, newTime_);
}
/** @note the re-quantisation happens automatically
* when the (changed) QuTime is materialised */
virtual void
change (QuTime& target) const
{
imposeChange (target, newTime_);
}
public:
explicit
SetNewStartTimeMutation (TimeValue t)
: newTime_(t)
{ }
};
2012-12-02 01:54:02 +01:00
/**
* concrete time value mutation:
2011-04-25 18:51:50 +02:00
* set a new overall duration for an extended timespan.
*/
class SetNewDuration
: public ClonableMutation
{
Duration changedDuration_;
virtual void
change (Duration& target) const
{
imposeChange (target, changedDuration_);
}
virtual void
change (TimeSpan& target) const
{
imposeChange (target.duration(), changedDuration_);
}
/** @note the re-quantisation happens automatically
* when the (changed) QuTime is materialised */
virtual void
change (QuTime&) const
{
throw error::Logic ("mutating the duration of a (quantised) time point doesn't make sense"
, LUMIERA_ERROR_INVALID_MUTATION);
}
public:
explicit
SetNewDuration (Duration dur)
: changedDuration_(dur)
{ }
};
2012-12-02 01:54:02 +01:00
/**
2011-04-25 18:51:50 +02:00
* concrete time value mutation:
* adjust the given time entity by an offset amount.
*/
class ImposeOffsetMutation
: public ClonableMutation
{
Offset adjustment_;
virtual void
change (Duration& target) const
{
imposeChange (target, adjustment_);
2011-04-25 18:51:50 +02:00
}
virtual void
change (TimeSpan& target) const
{
imposeChange (target, adjustment_);
2011-04-25 18:51:50 +02:00
}
/** @note the re-quantisation happens automatically
* when the (changed) QuTime is materialised */
virtual void
change (QuTime& target) const
{
imposeChange (target, adjustment_);
2011-04-25 18:51:50 +02:00
}
public:
explicit
ImposeOffsetMutation (Offset adj)
: adjustment_(adj)
{ }
};
2012-12-02 01:54:02 +01:00
/**
* concrete time value mutation:
* make the grid aligned time value explicit,
* and impose the resulting value to the given
* time points (or start points).
*/
class MaterialiseIntoTarget
: public SetNewStartTimeMutation
{
public:
explicit
MaterialiseIntoTarget (QuTime const& quant)
: SetNewStartTimeMutation (PQuant(quant)->materialise(quant))
{ }
};
2012-12-02 01:54:02 +01:00
/**
* concrete time value mutation:
* nudge target value by the given number of 'steps',
* relative to the given grid.
*/
class NudgeMutation
: public ImposeOffsetMutation
{
static Offset
materialiseGridPoint (PQuant const& grid, int steps)
{
REQUIRE (grid);
return Offset(grid->timeOf(0), grid->timeOf(steps));
}
public:
NudgeMutation (int relativeSteps, PQuant const& grid)
: ImposeOffsetMutation(materialiseGridPoint (grid,relativeSteps))
{ }
};
2012-12-02 01:54:02 +01:00
/**
* concrete time value mutation:
* nudge based on a implicit grid, which is
* either a quantised target value's own grid,
* or a \em natural grid.
* @note currently the natural grid is hard wired,
* just interpreting the step parameter as
* offset in seconds.
* @see mutation#imposeChange (TimeValue, int)
* @see mutation#imposeChange (QuTime, int)
*/
class NaturalNudgeMutation
: public ClonableMutation
{
int steps_;
virtual void
change (Duration& target) const
{
imposeChange (target, steps_);
}
virtual void
change (TimeSpan& target) const
{
imposeChange (target, steps_);
}
/** @note special treatment: use the quantised time's own grid */
virtual void
change (QuTime& target) const
{
imposeChange (target, steps_);
}
public:
explicit
NaturalNudgeMutation (int relativeSteps)
: steps_(relativeSteps)
{ }
};
/** Convenience factory to yield a simple Mutation changing the absolute start time.
* This whole procedure might look quite inefficient, but actually most of the
* abstractions are removed at runtime, leaving only a single indirection
* through the VTable of the Mutation Interface.
* @throw error::Logic when attempting to change the (non existent) start time of a Duration
* @return EncapsulatedMutation, which is an "polymorphic value object",
* actually carrying an embedded Mutation subclass with the new start time
*/
EncapsulatedMutation
Mutation::changeTime (Time newStartTime)
{
return EncapsulatedMutation::build<SetNewStartTimeMutation> (newStartTime);
}
/** Convenience factory: simple Mutation to adjust the duration or length of a timespan
* @throw error::Logic when attempting to change the "duration" of a quantised time point
* @return EncapsulatedMutation, carrying the new duration value to impose
*/
EncapsulatedMutation
Mutation::changeDuration (Duration changedDur)
{
return EncapsulatedMutation::build<SetNewDuration> (changedDur);
}
2011-04-25 18:51:50 +02:00
/** Convenience factory: simple Mutation to adjust the duration or length of a timespan
* @throw error::Logic when attempting to change the "duration" of a quantised time point
* @return EncapsulatedMutation, carrying the new duration value to impose
*/
EncapsulatedMutation
Mutation::adjust (Offset change)
{
return EncapsulatedMutation::build<ImposeOffsetMutation> (change);
}
/** Convenience factory: materialise the given quantised time into an explicit fixed
* internal time value, according to the underlying time grid; impose the resulting
* value then as new time point or start point to the target
* @note same as materialising and then invoking #changeTime
*/
EncapsulatedMutation
Mutation::materialise (QuTime const& gridAlignedTime)
{
return EncapsulatedMutation::build<MaterialiseIntoTarget> (gridAlignedTime);
}
/** build a time mutation to \em nudge the target time value by an offset,
* defined as number of steps on an implicit nudge grid. If the target is an
* continuous (not quantised) time value or duration, an internal 'default nudge grid'
* will be used to calculate the offset value. Typically, this grid counts in seconds.
* To the contrary, when the target is a quantised value, it will be aligned to the
* grid point relative to the current value's next grid point, measured in number
* of steps. This includes \em materialising the internal time to the exact grid
* position. If especially the adjustment is zero, the internal value will
* be changed to literally equal the current value's next grid point.
*/
EncapsulatedMutation
Mutation::nudge (int adjustment)
{
return EncapsulatedMutation::build<NaturalNudgeMutation> (adjustment);
}
/** build a time mutation to \em nudge the target time value; the nudge time grid
* is specified explicitly here, instead of using a global or 'natural' nudge grid.
* In case the target itself is a quantised time value, a chaining of the two
* grids will happen: first, the nudge grid is used to get an offset value,
* according to the number of steps, then this offset is applied to the
* raw value underlying the quantised target. If this resulting target
* value later will be cast into any kind of time code or materialised
* otherwise, the quantised value's own grid will apply as well,
* resulting in the net result of two quantisation operations
* being applied in sequence.
* @param adjustment number of grid steps to apply as offset
* @param grid reference to a concrete grid instance
* @note there is a variant of this function, using just a
* symbolic name to refer to the grid, allowing to nudge
* based on a grid known to exist somewhere in the session.
* Using this approach involves the Advice system and thus
* requires linking against \c liblumieracommon.so
* @see #nudge(int,Symbol)
*/
EncapsulatedMutation
Mutation::nudge (int adjustment, PQuant const& grid)
{
return EncapsulatedMutation::build<NudgeMutation> (adjustment, grid);
}
}} // lib::time
2012-12-02 01:54:02 +01:00