/*------------------------------------------------------------------------- NeoPixelAnimator provides animation timing support. Written by Michael C. Miller. I invest time and resources providing this open source code, please support me by dontating (see https://github.com/Makuna/NeoPixelBus) ------------------------------------------------------------------------- This file is part of the Makuna/NeoPixelBus library. NeoPixelBus is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. NeoPixelBus is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with NeoPixel. If not, see . -------------------------------------------------------------------------*/ #include "NeoPixelBus.h" #include "NeoPixelAnimator.h" NeoPixelAnimator::NeoPixelAnimator(uint16_t countAnimations, uint16_t timeScale) : _countAnimations(countAnimations), _animationLastTick(0), _activeAnimations(0), _isRunning(true) { setTimeScale(timeScale); _animations = new AnimationContext[_countAnimations]; } NeoPixelAnimator::~NeoPixelAnimator() { delete[] _animations; } bool NeoPixelAnimator::NextAvailableAnimation(uint16_t* indexAvailable, uint16_t indexStart) { if (indexStart >= _countAnimations) { // last one indexStart = _countAnimations - 1; } uint16_t next = indexStart; do { if (!IsAnimationActive(next)) { if (indexAvailable) { *indexAvailable = next; } return true; } next = (next + 1) % _countAnimations; } while (next != indexStart); return false; } void NeoPixelAnimator::StartAnimation(uint16_t indexAnimation, uint16_t duration, AnimUpdateCallback animUpdate) { if (indexAnimation >= _countAnimations || animUpdate == NULL) { return; } if (_activeAnimations == 0) { _animationLastTick = millis(); } StopAnimation(indexAnimation); // all animations must have at least non zero duration, otherwise // they are considered stopped if (duration == 0) { duration = 1; } _animations[indexAnimation].StartAnimation(duration, animUpdate); _activeAnimations++; } void NeoPixelAnimator::StopAnimation(uint16_t indexAnimation) { if (indexAnimation >= _countAnimations) { return; } if (IsAnimationActive(indexAnimation)) { _activeAnimations--; _animations[indexAnimation].StopAnimation(); } } void NeoPixelAnimator::StopAll() { for (uint16_t indexAnimation = 0; indexAnimation < _countAnimations; ++indexAnimation) { _animations[indexAnimation].StopAnimation(); } _activeAnimations = 0; } void NeoPixelAnimator::UpdateAnimations() { if (_isRunning) { uint32_t currentTick = millis(); uint32_t delta = currentTick - _animationLastTick; if (delta >= _timeScale) { AnimationContext* pAnim; delta /= _timeScale; // scale delta into animation time for (uint16_t iAnim = 0; iAnim < _countAnimations; iAnim++) { pAnim = &_animations[iAnim]; AnimUpdateCallback fnUpdate = pAnim->_fnCallback; AnimationParam param; param.index = iAnim; if (pAnim->_remaining > delta) { param.state = (pAnim->_remaining == pAnim->_duration) ? AnimationState_Started : AnimationState_Progress; param.progress = pAnim->CurrentProgress(); fnUpdate(param); pAnim->_remaining -= delta; } else if (pAnim->_remaining > 0) { param.state = AnimationState_Completed; param.progress = 1.0f; _activeAnimations--; pAnim->StopAnimation(); fnUpdate(param); } } _animationLastTick = currentTick; } } } void NeoPixelAnimator::ChangeAnimationDuration(uint16_t indexAnimation, uint16_t newDuration) { if (indexAnimation >= _countAnimations) { return; } AnimationContext* pAnim = &_animations[indexAnimation]; // calc the current animation progress float progress = pAnim->CurrentProgress(); // keep progress in range just in case if (progress < 0.0f) { progress = 0.0f; } else if (progress > 1.0f) { progress = 1.0f; } // change the duration pAnim->_duration = newDuration; // _remaining time must also be reset after a duration change; // use the progress to recalculate it pAnim->_remaining = uint16_t(pAnim->_duration * (1.0f - progress)); }