2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "VisibleEffect.h"
22 #include "GUIInfoManager.h"
23 #include "utils/log.h"
24 #include "addons/Skin.h" // for the effect time adjustments
25 #include "utils/StringUtils.h"
27 #include "utils/XBMCTinyXML.h"
31 CAnimEffect::CAnimEffect(const TiXmlElement *node, EFFECT_TYPE effect)
35 m_delay = m_length = 0;
40 if (TIXML_SUCCESS == node->QueryFloatAttribute("time", &temp)) m_length = (unsigned int)(temp * g_SkinInfo->GetEffectsSlowdown());
41 if (TIXML_SUCCESS == node->QueryFloatAttribute("delay", &temp)) m_delay = (unsigned int)(temp * g_SkinInfo->GetEffectsSlowdown());
43 m_pTweener = GetTweener(node);
46 CAnimEffect::CAnimEffect(unsigned int delay, unsigned int length, EFFECT_TYPE effect)
51 m_pTweener = boost::shared_ptr<Tweener>(new LinearTweener());
54 CAnimEffect::~CAnimEffect()
58 CAnimEffect::CAnimEffect(const CAnimEffect &src)
64 CAnimEffect& CAnimEffect::operator=(const CAnimEffect &src)
66 if (&src == this) return *this;
68 m_matrix = src.m_matrix;
69 m_effect = src.m_effect;
70 m_length = src.m_length;
71 m_delay = src.m_delay;
73 m_pTweener = src.m_pTweener;
77 void CAnimEffect::Calculate(unsigned int time, const CPoint ¢er)
79 assert(m_delay + m_length);
80 // calculate offset and tweening
81 float offset = 0.0f; // delayed forward, or finished reverse
82 if (time >= m_delay && time < m_delay + m_length)
83 offset = (float)(time - m_delay) / m_length;
84 else if (time >= m_delay + m_length)
87 offset = m_pTweener->Tween(offset, 0.0f, 1.0f, 1.0f);
88 // and apply the effect
89 ApplyEffect(offset, center);
92 void CAnimEffect::ApplyState(ANIMATION_STATE state, const CPoint ¢er)
94 float offset = (state == ANIM_STATE_APPLIED) ? 1.0f : 0.0f;
95 ApplyEffect(offset, center);
98 boost::shared_ptr<Tweener> CAnimEffect::GetTweener(const TiXmlElement *pAnimationNode)
100 boost::shared_ptr<Tweener> m_pTweener;
101 const char *tween = pAnimationNode->Attribute("tween");
104 if (strcmpi(tween, "linear")==0)
105 m_pTweener = boost::shared_ptr<Tweener>(new LinearTweener());
106 else if (strcmpi(tween, "quadratic")==0)
107 m_pTweener = boost::shared_ptr<Tweener>(new QuadTweener());
108 else if (strcmpi(tween, "cubic")==0)
109 m_pTweener = boost::shared_ptr<Tweener>(new CubicTweener());
110 else if (strcmpi(tween, "sine")==0)
111 m_pTweener = boost::shared_ptr<Tweener>(new SineTweener());
112 else if (strcmpi(tween, "back")==0)
113 m_pTweener = boost::shared_ptr<Tweener>(new BackTweener());
114 else if (strcmpi(tween, "circle")==0)
115 m_pTweener = boost::shared_ptr<Tweener>(new CircleTweener());
116 else if (strcmpi(tween, "bounce")==0)
117 m_pTweener = boost::shared_ptr<Tweener>(new BounceTweener());
118 else if (strcmpi(tween, "elastic")==0)
119 m_pTweener = boost::shared_ptr<Tweener>(new ElasticTweener());
121 const char *easing = pAnimationNode->Attribute("easing");
122 if (m_pTweener && easing)
124 if (strcmpi(easing, "in")==0)
125 m_pTweener->SetEasing(EASE_IN);
126 else if (strcmpi(easing, "out")==0)
127 m_pTweener->SetEasing(EASE_OUT);
128 else if (strcmpi(easing, "inout")==0)
129 m_pTweener->SetEasing(EASE_INOUT);
134 pAnimationNode->QueryFloatAttribute("acceleration", &accel);
137 { // no tweener is specified - use a linear tweener
138 // or quadratic if we have acceleration
141 m_pTweener = boost::shared_ptr<Tweener>(new QuadTweener(accel));
142 m_pTweener->SetEasing(EASE_IN);
145 m_pTweener = boost::shared_ptr<Tweener>(new LinearTweener());
151 CFadeEffect::CFadeEffect(const TiXmlElement *node, bool reverseDefaults) : CAnimEffect(node, EFFECT_TYPE_FADE)
154 { // out effect defaults
155 m_startAlpha = 100.0f;
159 { // in effect defaults
163 node->QueryFloatAttribute("start", &m_startAlpha);
164 node->QueryFloatAttribute("end", &m_endAlpha);
165 if (m_startAlpha > 100.0f) m_startAlpha = 100.0f;
166 if (m_endAlpha > 100.0f) m_endAlpha = 100.0f;
167 if (m_startAlpha < 0) m_startAlpha = 0;
168 if (m_endAlpha < 0) m_endAlpha = 0;
171 CFadeEffect::CFadeEffect(float start, float end, unsigned int delay, unsigned int length) : CAnimEffect(delay, length, EFFECT_TYPE_FADE)
173 m_startAlpha = start;
177 void CFadeEffect::ApplyEffect(float offset, const CPoint ¢er)
179 m_matrix.SetFader(((m_endAlpha - m_startAlpha) * offset + m_startAlpha) * 0.01f);
182 CSlideEffect::CSlideEffect(const TiXmlElement *node) : CAnimEffect(node, EFFECT_TYPE_SLIDE)
184 m_startX = m_endX = 0;
185 m_startY = m_endY = 0;
186 const char *startPos = node->Attribute("start");
189 vector<CStdString> commaSeparated;
190 StringUtils::SplitString(startPos, ",", commaSeparated);
191 if (commaSeparated.size() > 1)
192 m_startY = (float)atof(commaSeparated[1].c_str());
193 m_startX = (float)atof(commaSeparated[0].c_str());
195 const char *endPos = node->Attribute("end");
198 vector<CStdString> commaSeparated;
199 StringUtils::SplitString(endPos, ",", commaSeparated);
200 if (commaSeparated.size() > 1)
201 m_endY = (float)atof(commaSeparated[1].c_str());
202 m_endX = (float)atof(commaSeparated[0].c_str());
206 void CSlideEffect::ApplyEffect(float offset, const CPoint ¢er)
208 m_matrix.SetTranslation((m_endX - m_startX)*offset + m_startX, (m_endY - m_startY)*offset + m_startY, 0);
211 CRotateEffect::CRotateEffect(const TiXmlElement *node, EFFECT_TYPE effect) : CAnimEffect(node, effect)
213 m_startAngle = m_endAngle = 0;
214 m_autoCenter = false;
215 node->QueryFloatAttribute("start", &m_startAngle);
216 node->QueryFloatAttribute("end", &m_endAngle);
218 // convert to a negative to account for our reversed Y axis (Needed for X and Z ???)
222 const char *centerPos = node->Attribute("center");
225 if (strcmpi(centerPos, "auto") == 0)
229 vector<CStdString> commaSeparated;
230 StringUtils::SplitString(centerPos, ",", commaSeparated);
231 if (commaSeparated.size() > 1)
232 m_center.y = (float)atof(commaSeparated[1].c_str());
233 m_center.x = (float)atof(commaSeparated[0].c_str());
238 void CRotateEffect::ApplyEffect(float offset, const CPoint ¢er)
240 static const float degree_to_radian = 0.01745329252f;
243 if (m_effect == EFFECT_TYPE_ROTATE_X)
244 m_matrix.SetXRotation(((m_endAngle - m_startAngle)*offset + m_startAngle) * degree_to_radian, m_center.x, m_center.y, 1.0f);
245 else if (m_effect == EFFECT_TYPE_ROTATE_Y)
246 m_matrix.SetYRotation(((m_endAngle - m_startAngle)*offset + m_startAngle) * degree_to_radian, m_center.x, m_center.y, 1.0f);
247 else if (m_effect == EFFECT_TYPE_ROTATE_Z) // note coordinate aspect ratio is not generally square in the XY plane, so correct for it.
248 m_matrix.SetZRotation(((m_endAngle - m_startAngle)*offset + m_startAngle) * degree_to_radian, m_center.x, m_center.y, g_graphicsContext.GetScalingPixelRatio());
251 CZoomEffect::CZoomEffect(const TiXmlElement *node, const CRect &rect) : CAnimEffect(node, EFFECT_TYPE_ZOOM), m_center(CPoint(0,0))
254 m_startX = m_startY = 100;
255 m_endX = m_endY = 100;
256 m_autoCenter = false;
258 float startPosX = rect.x1;
259 float startPosY = rect.y1;
260 float endPosX = rect.x1;
261 float endPosY = rect.y1;
263 float width = max(rect.Width(), 0.001f);
264 float height = max(rect.Height(),0.001f);
266 const char *start = node->Attribute("start");
269 CStdStringArray params;
270 StringUtils::SplitString(start, ",", params);
271 if (params.size() == 1)
273 m_startX = (float)atof(params[0].c_str());
276 else if (params.size() == 2)
278 m_startX = (float)atof(params[0].c_str());
279 m_startY = (float)atof(params[1].c_str());
281 else if (params.size() == 4)
282 { // format is start="x,y,width,height"
283 // use width and height from our rect to calculate our sizing
284 startPosX = (float)atof(params[0].c_str());
285 startPosY = (float)atof(params[1].c_str());
286 m_startX = (float)atof(params[2].c_str());
287 m_startY = (float)atof(params[3].c_str());
288 m_startX *= 100.0f / width;
289 m_startY *= 100.0f / height;
292 const char *end = node->Attribute("end");
295 CStdStringArray params;
296 StringUtils::SplitString(end, ",", params);
297 if (params.size() == 1)
299 m_endX = (float)atof(params[0].c_str());
302 else if (params.size() == 2)
304 m_endX = (float)atof(params[0].c_str());
305 m_endY = (float)atof(params[1].c_str());
307 else if (params.size() == 4)
308 { // format is start="x,y,width,height"
309 // use width and height from our rect to calculate our sizing
310 endPosX = (float)atof(params[0].c_str());
311 endPosY = (float)atof(params[1].c_str());
312 m_endX = (float)atof(params[2].c_str());
313 m_endY = (float)atof(params[3].c_str());
314 m_endX *= 100.0f / width;
315 m_endY *= 100.0f / height;
318 const char *centerPos = node->Attribute("center");
321 if (strcmpi(centerPos, "auto") == 0)
325 vector<CStdString> commaSeparated;
326 StringUtils::SplitString(centerPos, ",", commaSeparated);
327 if (commaSeparated.size() > 1)
328 m_center.y = (float)atof(commaSeparated[1].c_str());
329 m_center.x = (float)atof(commaSeparated[0].c_str());
333 { // no center specified
334 // calculate the center position...
337 float scale = m_endX / m_startX;
339 m_center.x = (endPosX - scale*startPosX) / (1 - scale);
343 float scale = m_endY / m_startY;
345 m_center.y = (endPosY - scale*startPosY) / (1 - scale);
350 void CZoomEffect::ApplyEffect(float offset, const CPoint ¢er)
354 float scaleX = ((m_endX - m_startX)*offset + m_startX) * 0.01f;
355 float scaleY = ((m_endY - m_startY)*offset + m_startY) * 0.01f;
356 m_matrix.SetScaler(scaleX, scaleY, m_center.x, m_center.y);
359 CAnimation::CAnimation()
361 m_type = ANIM_TYPE_NONE;
364 m_repeatAnim = ANIM_REPEAT_NONE;
365 m_currentState = ANIM_STATE_NONE;
366 m_currentProcess = ANIM_PROCESS_NONE;
367 m_queuedProcess = ANIM_PROCESS_NONE;
368 m_lastCondition = false;
375 CAnimation::CAnimation(const CAnimation &src)
380 CAnimation::~CAnimation()
382 for (unsigned int i = 0; i < m_effects.size(); i++)
387 CAnimation &CAnimation::operator =(const CAnimation &src)
389 if (this == &src) return *this; // same
391 m_reversible = src.m_reversible;
392 m_condition = src.m_condition; // TODO: register/unregister
393 m_repeatAnim = src.m_repeatAnim;
394 m_lastCondition = src.m_lastCondition;
395 m_queuedProcess = src.m_queuedProcess;
396 m_currentProcess = src.m_currentProcess;
397 m_currentState = src.m_currentState;
398 m_start = src.m_start;
399 m_length = src.m_length;
400 m_delay = src.m_delay;
401 m_amount = src.m_amount;
402 // clear all our effects
403 for (unsigned int i = 0; i < m_effects.size(); i++)
406 // and assign the others across
407 for (unsigned int i = 0; i < src.m_effects.size(); i++)
409 CAnimEffect *newEffect = NULL;
410 if (src.m_effects[i]->GetType() == CAnimEffect::EFFECT_TYPE_FADE)
411 newEffect = new CFadeEffect(*(CFadeEffect *)src.m_effects[i]);
412 else if (src.m_effects[i]->GetType() == CAnimEffect::EFFECT_TYPE_ZOOM)
413 newEffect = new CZoomEffect(*(CZoomEffect *)src.m_effects[i]);
414 else if (src.m_effects[i]->GetType() == CAnimEffect::EFFECT_TYPE_SLIDE)
415 newEffect = new CSlideEffect(*(CSlideEffect *)src.m_effects[i]);
416 else if (src.m_effects[i]->GetType() == CAnimEffect::EFFECT_TYPE_ROTATE_X ||
417 src.m_effects[i]->GetType() == CAnimEffect::EFFECT_TYPE_ROTATE_Y ||
418 src.m_effects[i]->GetType() == CAnimEffect::EFFECT_TYPE_ROTATE_Z)
419 newEffect = new CRotateEffect(*(CRotateEffect *)src.m_effects[i]);
421 m_effects.push_back(newEffect);
426 void CAnimation::Animate(unsigned int time, bool startAnim)
428 // First start any queued animations
429 if (m_queuedProcess == ANIM_PROCESS_NORMAL)
431 if (m_currentProcess == ANIM_PROCESS_REVERSE)
432 m_start = time - m_amount; // reverse direction of animation
435 m_currentProcess = ANIM_PROCESS_NORMAL;
437 else if (m_queuedProcess == ANIM_PROCESS_REVERSE)
439 if (m_currentProcess == ANIM_PROCESS_NORMAL)
440 m_start = time - (m_length - m_amount); // reverse direction of animation
441 else if (m_currentProcess == ANIM_PROCESS_NONE)
443 m_currentProcess = ANIM_PROCESS_REVERSE;
445 // reset the queued state once we've rendered to ensure allocation has occured
446 if (startAnim || m_queuedProcess == ANIM_PROCESS_REVERSE)
447 m_queuedProcess = ANIM_PROCESS_NONE;
449 // Update our animation process
450 if (m_currentProcess == ANIM_PROCESS_NORMAL)
452 if (time - m_start < m_delay)
455 m_currentState = ANIM_STATE_DELAYED;
457 else if (time - m_start < m_length + m_delay)
459 m_amount = time - m_start - m_delay;
460 m_currentState = ANIM_STATE_IN_PROCESS;
465 if (m_repeatAnim == ANIM_REPEAT_PULSE && m_lastCondition)
466 { // pulsed anims auto-reverse
467 m_currentProcess = ANIM_PROCESS_REVERSE;
470 else if (m_repeatAnim == ANIM_REPEAT_LOOP && m_lastCondition)
471 { // looped anims start over
476 m_currentState = ANIM_STATE_APPLIED;
479 else if (m_currentProcess == ANIM_PROCESS_REVERSE)
481 if (time - m_start < m_length)
483 m_amount = m_length - (time - m_start);
484 m_currentState = ANIM_STATE_IN_PROCESS;
489 if (m_repeatAnim == ANIM_REPEAT_PULSE && m_lastCondition)
490 { // pulsed anims auto-reverse
491 m_currentProcess = ANIM_PROCESS_NORMAL;
495 m_currentState = ANIM_STATE_APPLIED;
500 void CAnimation::ResetAnimation()
502 m_queuedProcess = ANIM_PROCESS_NONE;
503 m_currentProcess = ANIM_PROCESS_NONE;
504 m_currentState = ANIM_STATE_NONE;
507 void CAnimation::ApplyAnimation()
509 m_queuedProcess = ANIM_PROCESS_NONE;
510 if (m_repeatAnim == ANIM_REPEAT_PULSE)
511 { // pulsed anims auto-reverse
513 m_currentProcess = ANIM_PROCESS_REVERSE;
514 m_currentState = ANIM_STATE_IN_PROCESS;
516 else if (m_repeatAnim == ANIM_REPEAT_LOOP)
517 { // looped anims start over
519 m_currentProcess = ANIM_PROCESS_NORMAL;
520 m_currentState = ANIM_STATE_IN_PROCESS;
523 { // set normal process, so that Calculate() knows that we're finishing for zero length effects
524 // it will be reset in RenderAnimation()
525 m_currentProcess = ANIM_PROCESS_NORMAL;
526 m_currentState = ANIM_STATE_APPLIED;
532 void CAnimation::Calculate(const CPoint ¢er)
534 for (unsigned int i = 0; i < m_effects.size(); i++)
536 CAnimEffect *effect = m_effects[i];
537 if (effect->GetLength())
538 effect->Calculate(m_delay + m_amount, center);
540 { // effect has length zero, so either apply complete
541 if (m_currentProcess == ANIM_PROCESS_NORMAL)
542 effect->ApplyState(ANIM_STATE_APPLIED, center);
544 effect->ApplyState(ANIM_STATE_NONE, center);
549 void CAnimation::RenderAnimation(TransformMatrix &matrix, const CPoint ¢er)
551 if (m_currentProcess != ANIM_PROCESS_NONE)
553 // If we have finished an animation, reset the animation state
554 // We do this here (rather than in Animate()) as we need the
555 // currentProcess information in the UpdateStates() function of the
556 // window and control classes.
557 if (m_currentState == ANIM_STATE_APPLIED)
559 m_currentProcess = ANIM_PROCESS_NONE;
560 m_queuedProcess = ANIM_PROCESS_NONE;
562 if (m_currentState != ANIM_STATE_NONE)
564 for (unsigned int i = 0; i < m_effects.size(); i++)
565 matrix *= m_effects[i]->GetTransform();
569 void CAnimation::QueueAnimation(ANIMATION_PROCESS process)
571 m_queuedProcess = process;
574 CAnimation CAnimation::CreateFader(float start, float end, unsigned int delay, unsigned int length, ANIMATION_TYPE type)
578 anim.AddEffect(new CFadeEffect(start, end, delay, length));
582 bool CAnimation::CheckCondition()
584 return !m_condition || g_infoManager.GetBoolValue(m_condition);
587 void CAnimation::UpdateCondition(const CGUIListItem *item)
589 bool condition = g_infoManager.GetBoolValue(m_condition, item);
590 if (condition && !m_lastCondition)
591 QueueAnimation(ANIM_PROCESS_NORMAL);
592 else if (!condition && m_lastCondition)
595 QueueAnimation(ANIM_PROCESS_REVERSE);
599 m_lastCondition = condition;
602 void CAnimation::SetInitialCondition()
604 m_lastCondition = g_infoManager.GetBoolValue(m_condition);
611 void CAnimation::Create(const TiXmlElement *node, const CRect &rect, int context)
613 if (!node || !node->FirstChild())
616 // conditions and reversibility
617 const char *condition = node->Attribute("condition");
619 m_condition = g_infoManager.Register(condition, context);
620 const char *reverse = node->Attribute("reversible");
621 if (reverse && strcmpi(reverse, "false") == 0)
622 m_reversible = false;
624 const TiXmlElement *effect = node->FirstChildElement("effect");
626 CStdString type = node->FirstChild()->Value();
627 m_type = ANIM_TYPE_CONDITIONAL;
628 if (effect) // new layout
629 type = node->Attribute("type");
631 if (StringUtils::StartsWithNoCase(type, "visible")) m_type = ANIM_TYPE_VISIBLE;
632 else if (type.Equals("hidden")) m_type = ANIM_TYPE_HIDDEN;
633 else if (type.Equals("focus")) m_type = ANIM_TYPE_FOCUS;
634 else if (type.Equals("unfocus")) m_type = ANIM_TYPE_UNFOCUS;
635 else if (type.Equals("windowopen")) m_type = ANIM_TYPE_WINDOW_OPEN;
636 else if (type.Equals("windowclose")) m_type = ANIM_TYPE_WINDOW_CLOSE;
638 if (m_type == ANIM_TYPE_CONDITIONAL)
642 CLog::Log(LOGERROR, "Control has invalid animation type (no condition or no type)");
646 // pulsed or loop animations
647 const char *pulse = node->Attribute("pulse");
648 if (pulse && strcmpi(pulse, "true") == 0)
649 m_repeatAnim = ANIM_REPEAT_PULSE;
650 const char *loop = node->Attribute("loop");
651 if (loop && strcmpi(loop, "true") == 0)
652 m_repeatAnim = ANIM_REPEAT_LOOP;
655 m_delay = 0xffffffff;
658 // <animation effect="fade" start="0" end="100" delay="10" time="2000" condition="blahdiblah" reversible="false">focus</animation>
659 CStdString type = node->Attribute("effect");
660 AddEffect(type, node, rect);
664 // <animation type="focus" condition="blahdiblah" reversible="false">
665 // <effect type="fade" start="0" end="100" delay="10" time="2000" />
668 CStdString type = effect->Attribute("type");
669 AddEffect(type, effect, rect);
670 effect = effect->NextSiblingElement("effect");
674 void CAnimation::AddEffect(const CStdString &type, const TiXmlElement *node, const CRect &rect)
676 CAnimEffect *effect = NULL;
677 if (type.Equals("fade"))
678 effect = new CFadeEffect(node, m_type < 0);
679 else if (type.Equals("slide"))
680 effect = new CSlideEffect(node);
681 else if (type.Equals("rotate"))
682 effect = new CRotateEffect(node, CAnimEffect::EFFECT_TYPE_ROTATE_Z);
683 else if (type.Equals("rotatey"))
684 effect = new CRotateEffect(node, CAnimEffect::EFFECT_TYPE_ROTATE_Y);
685 else if (type.Equals("rotatex"))
686 effect = new CRotateEffect(node, CAnimEffect::EFFECT_TYPE_ROTATE_X);
687 else if (type.Equals("zoom"))
688 effect = new CZoomEffect(node, rect);
694 void CAnimation::AddEffect(CAnimEffect *effect)
696 m_effects.push_back(effect);
697 // our delay is the minimum of all the effect delays
698 if (effect->GetDelay() < m_delay)
699 m_delay = effect->GetDelay();
700 // our length is the maximum of all the effect lengths
701 if (effect->GetLength() > m_delay + m_length)
702 m_length = effect->GetLength() - m_delay;
705 CScroller::CScroller(unsigned int duration /* = 200 */, boost::shared_ptr<Tweener> tweener /* = NULL */)
711 m_hasResumePoint = false;
713 m_duration = duration > 0 ? duration : 1;
714 m_pTweener = tweener;
717 CScroller::CScroller(const CScroller& right)
723 CScroller& CScroller::operator=(const CScroller &right)
725 if (&right == this) return *this;
727 m_scrollValue = right.m_scrollValue;
728 m_delta = right.m_delta;
729 m_startTime = right.m_startTime;
730 m_startPosition = right.m_startPosition;
731 m_hasResumePoint = right.m_hasResumePoint;
732 m_lastTime = right.m_lastTime;
733 m_duration = right.m_duration;
734 m_pTweener = right.m_pTweener;
738 CScroller::~CScroller()
742 void CScroller::ScrollTo(float endPos)
744 float delta = endPos - m_scrollValue;
745 // if there is scrolling running in same direction - set resume point
746 m_hasResumePoint = m_delta != 0 && delta * m_delta > 0 && m_pTweener ? m_pTweener->HasResumePoint() : 0;
749 m_startPosition = m_scrollValue;
750 m_startTime = m_lastTime;
753 float CScroller::Tween(float progress)
757 if (m_hasResumePoint) // tweener with in_and_out easing
759 // time linear transformation (y = a*x + b): 0 -> resumePoint and 1 -> 1
760 // resumePoint = a * 0 + b and 1 = a * 1 + b
761 // a = 1 - resumePoint , b = resumePoint
762 // our resume point is 0.5
764 progress = 0.5f * progress + 0.5f;
765 // tweener value linear transformation (y = a*x + b): resumePointValue -> 0 and 1 -> 1
766 // 0 = a * resumePointValue and 1 = a * 1 + b
767 // a = 1 / ( 1 - resumePointValue) , b = -resumePointValue / (1 - resumePointValue)
768 // we assume resumePointValue = Tween(resumePoint) = Tween(0.5) = 0.5
769 // (this is true for tweener with in_and_out easing - it's rotationally symmetric about (0.5,0.5) continuous function)
771 return (2 * m_pTweener->Tween(progress, 0, 1, 1) - 1);
774 return m_pTweener->Tween(progress, 0, 1, 1);
780 bool CScroller::Update(unsigned int time)
785 if (time - m_startTime >= m_duration) // we are finished
787 m_scrollValue = m_startPosition + m_delta;
789 m_hasResumePoint = false;
794 m_scrollValue = m_startPosition + Tween((float)(time - m_startTime) / m_duration) * m_delta;