Make the eased value a reference and add source code doc to Easing.h

This allows to apply multiple easings to the same variable.
This commit is contained in:
Tobias Loose
2013-12-07 10:05:26 +01:00
parent 2b0f50bb88
commit 4514725aba
5 changed files with 177 additions and 72 deletions

View File

@@ -30,11 +30,10 @@ public:
void changeFilter(int offset);
MTGCard *getActiveCard();
//maintains the current rotation for fluid animations
private:
InOutQuadEasing mScrollOffset; //[-1,1]. defines the current rotation of the cards
InOutQuadEasing mSlide; //[-1,1]. defines, the y-offset of the cards
float mScrollOffset, mSlideOffset;
InOutQuadEasing mScrollEasing;
InOutQuadEasing mSlideEasing;
};
#endif //_CAROUSEL_DECK_VIEW_H_

View File

@@ -1,33 +1,98 @@
#ifndef _EASING_H_
#define _EASING_H_
//see http://www.gizma.com/easing/ for more information
/*! \brief A class for eased floats for use in animations
*
* Animations often defines values a floating point variable
* should have at given times and interpolates between them to
* calculate the value of that variable at any given intermediate
* time step.
*
* The simplest case would be linear interpolation:
* Suppose a float "position" should be a at time = 0 and
* b at time = x. If the current time is y, the value of
* "position" is then a + (b-a)*y/x.
*
* This class defines the interface needed to implement different
* kind of interpolations with a common interface. See
* http://www.gizma.com/easing/ for more information for a few
* examples.
*/
class Easing
{
public:
/*! \brief The value at the start of an animation.
*
* start_value is undefined if no animation is running.
*/
float start_value;
/*! \brief The amount the value should change during the animation.
*
* delta_value is undefined if no animation is running.
*/
float delta_value;
float value;
/*! \brief The current value.
*
* Use this member to read the value or to write the value without
* to animate intermediate values and. Make sure that the easing
* is not used once value is deleted.
*/
float& value;
/*! \brief The duration the animation should take
*
* It is not relevant which unit is used. This value is undefined
* if no animation is running.
*/
float duration;
/*! \brief The accumulated time the animation did run until now.
*
* It is not relevant which unit is used. This values is undefined
* if no animation is running.
*/
float time_acc;
Easing(float val): start_value(val), delta_value(0), value(val), duration(0), time_acc(0)
/*! \brief Sets Easing::float to val and sets the animation as not running.
*
* Make sure that the easing is not used once value is deleted.
*
* \param val The value to ease
*/
Easing(float& val): start_value(val), delta_value(0), value(val), duration(0), time_acc(0)
{
}
/*! \brief Resets the animation to its initial value
*
* This method does set the value to the start value and sets the passed time to 0.
* If there is no animation animation running, the resulting value is undefined.
*/
void reset()
{
value = start_value;
time_acc = 0;
}
/*! \brief Finishes the animation immediately
*
* Sets the value to the animations target value and the passed time to the
* animations duration. If there is no animation running, the behaviour is undefined.
*/
void finish()
{
value = start_value + delta_value;
time_acc = duration;
}
/*! \brief Lets dt time pass
*
* Advances the animation by dt time units and updates the value accordingly.
*
* \val dt The amount of time to jump forward
*/
void update(float dt)
{
if(duration > 0)
@@ -46,8 +111,22 @@ public:
}
}
/*! \brief Calculates the value from all other members.
*
* This method gets implemented by all specific easing classes.
*/
virtual void updateValue() = 0;
/*! \brief Starts the animation.
*
* Starts the interpolation from the current value (now) to
* targetValue (in now + _duration).
*
* If the animation is currently running, it gets replaced.
*
* \param targetValue The value to interpolate to
* \param _duration The duration the interpolation should take
*/
void start(float targetValue, float _duration)
{
start_value = value;
@@ -56,23 +135,53 @@ public:
duration = _duration;
}
/*! \brief Translates the current value and the target value by delta_value
*
* This method is mainly used for trickery. Suppose there is one object in the
* middle of the screen that should move to the top until it is outside of the
* screen and gets replaced by a second one entering the screen from the lower
* side once the first one disappeared. This method can be used to simulate this
* effect with one animation by translating (i.e. moving) the animation from the
* top to the bottom:
*
* Object1 and object2 are the same object: object1 whose y position is bound to value
* To start the transition, use start(SCREEN_HEIGHT, desired time); Once the first
* object left the screen (i.e. object.y < 0), change objects appearance to object2
* and translate the easing by (SCREEN_HEIGHT).
*
* \param delta_value The change in start_value and value
*/
void translate(float delta_value)
{
start_value += delta_value;
value += delta_value;
}
/*! \brief Returns if the passed time exceeds duration.
*
* If ther is no animation running, it is ensured that this is true.
*/
bool finished()
{
return time_acc >= duration;
}
};
/*! \brief This class defines an easing with quadratic acceleration and decceleration.
*/
class InOutQuadEasing : public Easing
{
public:
InOutQuadEasing(float val): Easing(val) {}
/*! \brief Calls Easing::Easing(val).
*
* \see Easing::Easing(float& val)
*/
InOutQuadEasing(float& val): Easing(val) {}
/*! \brief Implements the value calculation.
*
* \see Easing::updateValue()
*/
void updateValue()
{
float time_tmp = (time_acc * 2) / duration;

View File

@@ -27,11 +27,12 @@ public:
MTGCard *getActiveCard();
private:
int mCols; //the number of cols. there is a hidden col to the left and right for prefetching
int mRows; //the number of rows.
InOutQuadEasing mSlide; //[-1,1]. defines the y-offset of the cards
InOutQuadEasing mScrollOffset; //[-1,1]. defines the x-offset of the cards
int mCurrentSelection; //0 <= mCurrentSelection < mCards.size(). defines the current selected and thus upscaled card
int mCols;
int mRows;
float mScrollOffset, mSlideOffset;
InOutQuadEasing mScrollEasing;
InOutQuadEasing mSlideEasing;
int mCurrentSelection;
};
#endif //_GRID_DECK_VIEW_H

View File

@@ -3,7 +3,7 @@
const float CarouselDeckView::slide_animation_duration = 0.6f;
CarouselDeckView::CarouselDeckView() :
DeckView(10), mScrollOffset(0), mSlide(0)
DeckView(10), mScrollOffset(0), mSlideOffset(0), mScrollEasing(mScrollOffset), mSlideEasing(mSlideOffset)
{
}
@@ -12,19 +12,19 @@ CarouselDeckView::~CarouselDeckView()
void CarouselDeckView::UpdateViewState(float dt)
{
if(!mScrollOffset.finished())
if(!mScrollEasing.finished())
{
mScrollOffset.update(dt);
mScrollEasing.update(dt);
if(mScrollOffset.value <= -1.0f)
if(mScrollOffset <= -1.0f)
{
mScrollOffset.translate(1.0f);
mScrollEasing.translate(1.0f);
deck()->prev();
reloadIndexes();
}
else if(mScrollOffset.value >= 1.0f)
else if(mScrollOffset >= 1.0f)
{
mScrollOffset.translate(-1.0f);
mScrollEasing.translate(-1.0f);
deck()->next();
reloadIndexes();
}
@@ -32,25 +32,25 @@ void CarouselDeckView::UpdateViewState(float dt)
dirtyCardPos = true;
}
if(!mSlide.finished())
if(!mSlideEasing.finished())
{
mSlide.update(dt);
mSlideEasing.update(dt);
if(mSlide.value < mSlide.start_value)
if(mSlideOffset < mSlideEasing.start_value)
{
//going downwards
if(mSlide.value < -1.0f)
if(mSlideOffset < -1.0f)
{
mSlide.translate(2.0f);
mSlideEasing.translate(2.0f);
SwitchFilter(1);
}
}
else if(mSlide.value > mSlide.start_value)
else if(mSlideOffset > mSlideEasing.start_value)
{
//upwards
if(mSlide.value > 1.0f)
if(mSlideOffset > 1.0f)
{
mSlide.translate(-2.0f);
mSlideEasing.translate(-2.0f);
SwitchFilter(-1);
}
}
@@ -61,18 +61,19 @@ void CarouselDeckView::UpdateViewState(float dt)
void CarouselDeckView::UpdateCardPosition(CardRep &rep, int index)
{
float rotation = mScrollOffset.value + 8 - index;
float rotation = mScrollOffset + 8 - index;
rep.x = x_center + cos((rotation) * M_PI / 12) * (right_border - x_center);
rep.scale = max_scale / 1.12f * cos((rep.x - x_center) * 1.5f / (right_border - x_center)) + 0.2f * max_scale * cos(
cos((rep.x - x_center) * 0.15f / (right_border - x_center)));
rep.y = (SCREEN_HEIGHT_F) / 2.0f + SCREEN_HEIGHT_F * mSlide.value * (rep.scale + 0.2f);
rep.y = (SCREEN_HEIGHT_F) / 2.0f + SCREEN_HEIGHT_F * mSlideOffset * (rep.scale + 0.2f);
}
void CarouselDeckView::Reset()
{
mScrollOffset = 0;
mSlide = 0;
mSlideEasing.finish();
mScrollEasing.finish();
DeckView::Reset();
}
@@ -96,13 +97,13 @@ void CarouselDeckView::Render()
renderCard(4);
renderCard(0);
if (mScrollOffset.value < 0.5 && mScrollOffset.value > -0.5)
if (mScrollOffset < 0.5 && mScrollOffset > -0.5)
{
renderCard(1);
renderCard(3);
renderCard(2);
}
else if (mScrollOffset.value < -0.5)
else if (mScrollOffset < -0.5)
{
renderCard(3);
renderCard(2);
@@ -122,7 +123,7 @@ MTGCard * CarouselDeckView::Click(int x, int y)
last_user_activity = 0;
//clicked active card, and no animation is running
if(mSlide.finished() && mScrollOffset.finished())
if(mSlideEasing.finished() && mScrollEasing.finished())
{
if(n == 2)
{
@@ -140,7 +141,7 @@ MTGCard * CarouselDeckView::Click(int x, int y)
void CarouselDeckView::changePosition(int offset)
{
mScrollOffset.start(offset, 0.3f*abs(offset));
mScrollEasing.start(offset, 0.3f*abs(offset));
last_user_activity = 0;
}
@@ -149,11 +150,11 @@ void CarouselDeckView::changeFilter(int offset)
{
if(offset < 0)
{
mSlide.start(-2.0f, slide_animation_duration);
mSlideEasing.start(-2.0f, slide_animation_duration);
}
else if(offset > 0)
{
mSlide.start(2.0f, slide_animation_duration);
mSlideEasing.start(2.0f, slide_animation_duration);
}
last_user_activity = 0;
}

View File

@@ -6,7 +6,8 @@ const float GridDeckView::card_scale_small = 0.48f;
const float GridDeckView::card_scale_big = 0.7f;
GridDeckView::GridDeckView()
: DeckView(16), mCols(8), mRows(2), mSlide(0), mScrollOffset(0), mCurrentSelection(-1)
: DeckView(16), mCols(8), mRows(2), mScrollOffset(0), mSlideOffset(0),
mScrollEasing(mScrollOffset), mSlideEasing(mSlideOffset), mCurrentSelection(-1)
{
}
@@ -18,31 +19,33 @@ GridDeckView::~GridDeckView()
void GridDeckView::Reset()
{
mSlide.finish();
mScrollOffset.finish();
mSlideEasing.finish();
mScrollEasing.finish();
mCurrentSelection = 0;
DeckView::Reset();
}
void GridDeckView::UpdateViewState(float dt)
{
if(!mScrollOffset.finished())
if(!mScrollEasing.finished())
{
mScrollOffset.update(dt);
mScrollEasing.update(dt);
if(mScrollOffset.value <= -1.0f)
if(mScrollOffset <= -1.0f)
{
deck()->next();
deck()->next();
mScrollOffset.translate(1.0f);
mScrollEasing.translate(1.0f);
reloadIndexes();
mCurrentSelection = (mCurrentSelection >= 6) ? mCurrentSelection - 2 : -1;
}
else if(mScrollOffset.value >= 1.0f)
else if(mScrollOffset >= 1.0f)
{
deck()->prev();
deck()->prev();
mScrollOffset.translate(-1.0f);
mScrollEasing.translate(-1.0f);
reloadIndexes();
mCurrentSelection = (mCurrentSelection >= 0 && mCurrentSelection < 10) ? mCurrentSelection + 2 : -1;
}
@@ -50,27 +53,19 @@ void GridDeckView::UpdateViewState(float dt)
dirtyCardPos = true;
}
if(!mSlide.finished())
if(!mSlideEasing.finished())
{
mSlide.update(dt);
mSlideEasing.update(dt);
if(mSlide.value < mSlide.start_value)
if(mSlideOffset < -1.0f)
{
//going downwards
if(mSlide.value < -1.0f)
{
mSlide.translate(2.0f);
SwitchFilter(1);
}
mSlideEasing.translate(2.0f);
SwitchFilter(1);
}
else if(mSlide.value > mSlide.start_value)
else if(mSlideOffset > 1.0f)
{
//upwards
if(mSlide.value > 1.0f)
{
mSlide.translate(-2.0f);
SwitchFilter(-1);
}
mSlideEasing.translate(-2.0f);
SwitchFilter(-1);
}
dirtyCardPos = true;
@@ -84,8 +79,8 @@ void GridDeckView::UpdateCardPosition(CardRep &rep, int index)
float colWidth = SCREEN_WIDTH_F / (mCols - 3);
float rowHeight = SCREEN_HEIGHT_F / mRows;
rep.x = (col + mScrollOffset.value) * colWidth - colWidth;
rep.y = row * rowHeight + mSlide.value*SCREEN_HEIGHT + rowHeight/2;
rep.x = (col + mScrollOffset) * colWidth - colWidth;
rep.y = row * rowHeight + mSlideOffset*SCREEN_HEIGHT + rowHeight/2;
if(mCurrentSelection == index)
{
@@ -110,9 +105,9 @@ void GridDeckView::Render()
int firstVisibleCard = 2;
int lastVisibleCard = mCards.size() - 2;
if(!mScrollOffset.finished())
if(!mScrollEasing.finished())
{
if(mScrollOffset.delta_value > 0){
if(mScrollEasing.delta_value > 0){
firstVisibleCard = 0;
}
else
@@ -152,7 +147,7 @@ MTGCard * GridDeckView::Click(int x, int y)
int n = getCardIndexNextTo(x, y);
last_user_activity = 0;
if(mScrollOffset.finished() && mSlide.finished())
if(mScrollEasing.finished() && mSlideEasing.finished())
{ //clicked and no animations running
if(n == mCurrentSelection)
{
@@ -178,7 +173,7 @@ MTGCard * GridDeckView::Click(int x, int y)
void GridDeckView::changePosition(int offset)
{
mScrollOffset.start(-1.0f * offset, scroll_animation_duration * abs(offset));
mScrollEasing.start(-1.0f * offset, scroll_animation_duration * abs(offset));
last_user_activity = 0;
}
@@ -186,11 +181,11 @@ void GridDeckView::changeFilter(int offset)
{
if(offset < 0)
{
mSlide.start(-2.0f, slide_animation_duration);
mSlideEasing.start(-2.0f, slide_animation_duration);
}
else if(offset > 0)
{
mSlide.start(2.0f, slide_animation_duration);
mSlideEasing.start(2.0f, slide_animation_duration);
}
last_user_activity = 0;
}