diff --git a/projects/mtg/include/CarouselDeckView.h b/projects/mtg/include/CarouselDeckView.h index c85848c88..59d7fd0f8 100644 --- a/projects/mtg/include/CarouselDeckView.h +++ b/projects/mtg/include/CarouselDeckView.h @@ -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_ diff --git a/projects/mtg/include/Easing.h b/projects/mtg/include/Easing.h index e12b751a4..507b0a26a 100644 --- a/projects/mtg/include/Easing.h +++ b/projects/mtg/include/Easing.h @@ -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; diff --git a/projects/mtg/include/GridDeckView.h b/projects/mtg/include/GridDeckView.h index b25945637..1e59d4079 100644 --- a/projects/mtg/include/GridDeckView.h +++ b/projects/mtg/include/GridDeckView.h @@ -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 diff --git a/projects/mtg/src/CarouselDeckView.cpp b/projects/mtg/src/CarouselDeckView.cpp index c72312d9b..5e0a5b63b 100644 --- a/projects/mtg/src/CarouselDeckView.cpp +++ b/projects/mtg/src/CarouselDeckView.cpp @@ -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; } diff --git a/projects/mtg/src/GridDeckView.cpp b/projects/mtg/src/GridDeckView.cpp index 3ae150365..633359b77 100644 --- a/projects/mtg/src/GridDeckView.cpp +++ b/projects/mtg/src/GridDeckView.cpp @@ -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; }