/** * Copyright (c) 2021 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "timeline.h" void _timelineActionDeltaUpdateStart( timeline_t *timeline, timelineaction_t *action, uint8_t i ) { if(action->data == NULL) return; *((float *)action->data) = timeline->initials[i]; } void _timelineActionDeltaUpdateDuration( timeline_t *timeline, timelineaction_t *action, uint8_t i ) { float n; if(action->data == NULL) return; n = timeline->easings[i]( timelineActionGetTimeRaw(timeline, action) ); *((float *)action->data) = timeline->initials[i] + (timeline->deltas[i] * n); } void _timelineActionDeltaUpdateEnd( timeline_t *timeline, timelineaction_t *action, uint8_t i ) { if(action->data == NULL) return; *((float *)action->data) = timeline->initials[i] + timeline->deltas[i]; } void timelineInit(timeline_t *timeline) { timeline->current = 0; timeline->diff = 0; timeline->previous = 0; timeline->user = 0; timeline->actionCount = 0; } void timelineUpdate(timeline_t *timeline, float delta) { uint8_t i; timelineaction_t *action; float full; timeline->diff = delta; timeline->previous = timeline->current; timeline->current = timeline->current + delta; // Find all actions that would have started or ended in this timespan. for(i = 0; i < timeline->actionCount; i++) { action = timeline->actions +i; // Has the action started yet? if(action->start > timeline->current) continue; // Did we start this frame? if(action->start > timeline->previous && action->onStart != NULL) { action->onStart(timeline, action, i); } // Durations of 0 only fire starts, never ends or durations. if(action->duration == 0) continue; // Is the end still in the future? Durations in negatives go forever full = action->start+action->duration; if(action->duration < 0 || full > timeline->current) { if(action->onDuration != NULL) action->onDuration(timeline, action, i); } else if(full > timeline->previous) {// Did we end this frame? if(action->onEnd != NULL) action->onEnd(timeline, action, i); if(action->loop) action->start = timeline->current; } } } bool timelineIsFinished(timeline_t *timeline) { uint8_t i; timelineaction_t *action; for(i = 0; i < timeline->actionCount; i++) { action = timeline->actions +i; if(action->start > timeline->current) return false; if(action->duration < 0) return false; if(action->duration == 0) continue; if(action->start+action->duration > timeline->current) return false; } return true; } timelineaction_t * timelineAddAction(timeline_t *timeline, float start, float duration ) { timelineaction_t *action = timeline->actions + (timeline->actionCount++); action->loop = false; action->start = start, action->duration = duration; action->onStart = action->onEnd = action->onDuration = NULL; return action; } timelineaction_t * timelineAddDeltaAction(timeline_t *timeline, float start, float duration, float initial, float delta, easefunction_t *easing, float *destination ) { timelineaction_t *action; timeline->initials[timeline->actionCount] = initial; timeline->deltas[timeline->actionCount] = delta; timeline->easings[timeline->actionCount] = ( easing == NULL ? &easeLinear : easing ); action = timelineAddAction(timeline, start, duration); action->data = destination; action->onStart = &_timelineActionDeltaUpdateStart; action->onDuration = &_timelineActionDeltaUpdateDuration; action->onEnd = &_timelineActionDeltaUpdateEnd; return action; } timelineaction_t * timelineAddDeltaActionTo(timeline_t *timeline, float start, float duration, float initial, float end, easefunction_t *easing, float *destination ) { return timelineAddDeltaAction( timeline, start, duration, initial, end-initial, easing, destination ); } float timelineActionGetTimeRaw(timeline_t *timeline, timelineaction_t *action) { return (timeline->current - action->start) / action->duration; } float timelineActionGetTime(timeline_t *tl, timelineaction_t *at) { return mathClamp(timelineActionGetTimeRaw(tl, at), 0, 1); } void timelineClear(timeline_t *timeline) { timeline->actionCount = 0; }