void ScrollView::startAutoScroll(const Vec2& deltaMove, float timeInSec, bool attenuated)
{
    Vec2 adjustedDeltaMove = flattenVectorByDirection(deltaMove);

    _autoScrolling = true;
    _autoScrollTargetDelta = adjustedDeltaMove;
    _autoScrollAttenuate = attenuated;
    _autoScrollStartPosition = _innerContainer->getPosition();
    _autoScrollTotalTime = timeInSec;
    _autoScrollAccumulatedTime = 0;
    _autoScrollBraking = false;
    _autoScrollBrakingStartPosition = Vec2::ZERO;

    // If the destination is also out of boundary of same side, start brake from beginning.
    Vec2 currentOutOfBoundary = getHowMuchOutOfBoundary();
    if (!fltEqualZero(currentOutOfBoundary))
    {
        _autoScrollCurrentlyOutOfBoundary = true;
        Vec2 afterOutOfBoundary = getHowMuchOutOfBoundary(adjustedDeltaMove);
        if(currentOutOfBoundary.x * afterOutOfBoundary.x > 0 || currentOutOfBoundary.y * afterOutOfBoundary.y > 0)
        {
            _autoScrollBraking = true;
        }
    }
}
void ScrollView::processAutoScrolling(float deltaTime)
{
    // Make auto scroll shorter if it needs to deaccelerate.
    float brakingFactor = (isNecessaryAutoScrollBrake() ? OUT_OF_BOUNDARY_BREAKING_FACTOR : 1);
    
    // Elapsed time
    _autoScrollAccumulatedTime += deltaTime * (1 / brakingFactor);
    
    // Calculate the progress percentage
    float percentage = MIN(1, _autoScrollAccumulatedTime / _autoScrollTotalTime);
    if(_autoScrollAttenuate)
    {
        // Use quintic(5th degree) polynomial
        percentage = tweenfunc::quintEaseOut(percentage);
    }
    
    // Calculate the new position
    Vec2 newPosition = _autoScrollStartPosition + (_autoScrollTargetDelta * percentage);
    bool reachedEnd = std::abs(percentage - 1) <= this->getAutoScrollStopEpsilon();
    
    if (reachedEnd)
    {
        newPosition = _autoScrollStartPosition + _autoScrollTargetDelta;
    }

    if(_bounceEnabled)
    {
        // The new position is adjusted if out of boundary
        newPosition = _autoScrollBrakingStartPosition + (newPosition - _autoScrollBrakingStartPosition) * brakingFactor;
    }
    else
    {
        // Don't let go out of boundary
        Vec2 moveDelta = newPosition - getInnerContainerPosition();
        Vec2 outOfBoundary = getHowMuchOutOfBoundary(moveDelta);
        if (!fltEqualZero(outOfBoundary))
        {
            newPosition += outOfBoundary;
            reachedEnd = true;
        }
    }

    // Finish auto scroll if it ended
    if(reachedEnd)
    {
        _autoScrolling = false;
        dispatchEvent(SCROLLVIEW_EVENT_AUTOSCROLL_ENDED, EventType::AUTOSCROLL_ENDED);
    }

    moveInnerContainer(newPosition - getInnerContainerPosition(), reachedEnd);
}
bool ScrollView::startBounceBackIfNeeded()
{
    if (!_bounceEnabled)
    {
        return false;
    }
    Vec2 bounceBackAmount = getHowMuchOutOfBoundary();
    if(fltEqualZero(bounceBackAmount))
    {
        return false;
    }

    startAutoScroll(bounceBackAmount, BOUNCE_BACK_DURATION, true);
    return true;
}
bool ScrollView::isOutOfBoundary()
{
    return !fltEqualZero(getHowMuchOutOfBoundary());
}