123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- /**
- * The MIT License (MIT)
- *
- * Copyright (c) 2016 by Daniel Eichhorn
- * Copyright (c) 2016 by Fabrice Weinberg
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- */
- #include "OLEDDisplayUi.h"
- OLEDDisplayUi::OLEDDisplayUi(OLEDDisplay *display) {
- this->display = display;
- }
- void OLEDDisplayUi::init() {
- this->display->init();
- }
- void OLEDDisplayUi::setTargetFPS(uint8_t fps){
- float oldInterval = this->updateInterval;
- this->updateInterval = ((float) 1.0 / (float) fps) * 1000;
- // Calculate new ticksPerFrame
- float changeRatio = oldInterval / (float) this->updateInterval;
- this->ticksPerFrame *= changeRatio;
- this->ticksPerTransition *= changeRatio;
- }
- // -/------ Automatic controll ------\-
- void OLEDDisplayUi::enableAutoTransition(){
- this->autoTransition = true;
- }
- void OLEDDisplayUi::disableAutoTransition(){
- this->autoTransition = false;
- }
- void OLEDDisplayUi::setAutoTransitionForwards(){
- this->state.frameTransitionDirection = 1;
- this->lastTransitionDirection = 1;
- }
- void OLEDDisplayUi::setAutoTransitionBackwards(){
- this->state.frameTransitionDirection = -1;
- this->lastTransitionDirection = -1;
- }
- void OLEDDisplayUi::setTimePerFrame(uint16_t time){
- this->ticksPerFrame = (int) ( (float) time / (float) updateInterval);
- }
- void OLEDDisplayUi::setTimePerTransition(uint16_t time){
- this->ticksPerTransition = (int) ( (float) time / (float) updateInterval);
- }
- // -/------ Customize indicator position and style -------\-
- void OLEDDisplayUi::enableIndicator(){
- this->state.isIndicatorDrawen = true;
- }
- void OLEDDisplayUi::disableIndicator(){
- this->state.isIndicatorDrawen = false;
- }
- void OLEDDisplayUi::enableAllIndicators(){
- this->shouldDrawIndicators = true;
- }
- void OLEDDisplayUi::disableAllIndicators(){
- this->shouldDrawIndicators = false;
- }
- void OLEDDisplayUi::setIndicatorPosition(IndicatorPosition pos) {
- this->indicatorPosition = pos;
- }
- void OLEDDisplayUi::setIndicatorDirection(IndicatorDirection dir) {
- this->indicatorDirection = dir;
- }
- void OLEDDisplayUi::setActiveSymbol(const char* symbol) {
- this->activeSymbol = symbol;
- }
- void OLEDDisplayUi::setInactiveSymbol(const char* symbol) {
- this->inactiveSymbol = symbol;
- }
- // -/----- Frame settings -----\-
- void OLEDDisplayUi::setFrameAnimation(AnimationDirection dir) {
- this->frameAnimationDirection = dir;
- }
- void OLEDDisplayUi::setFrames(FrameCallback* frameFunctions, uint8_t frameCount) {
- this->frameFunctions = frameFunctions;
- this->frameCount = frameCount;
- this->resetState();
- }
- // -/----- Overlays ------\-
- void OLEDDisplayUi::setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount){
- this->overlayFunctions = overlayFunctions;
- this->overlayCount = overlayCount;
- }
- // -/----- Loading Process -----\-
- void OLEDDisplayUi::setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction) {
- this->loadingDrawFunction = loadingDrawFunction;
- }
- void OLEDDisplayUi::runLoadingProcess(LoadingStage* stages, uint8_t stagesCount) {
- uint8_t progress = 0;
- uint8_t increment = 100 / stagesCount;
- for (uint8_t i = 0; i < stagesCount; i++) {
- display->clear();
- this->loadingDrawFunction(this->display, &stages[i], progress);
- display->display();
- stages[i].callback();
- progress += increment;
- yield();
- }
- display->clear();
- this->loadingDrawFunction(this->display, &stages[stagesCount-1], progress);
- display->display();
- delay(150);
- }
- // -/----- Manuel control -----\-
- void OLEDDisplayUi::nextFrame() {
- if (this->state.frameState != IN_TRANSITION) {
- this->state.manuelControll = true;
- this->state.frameState = IN_TRANSITION;
- this->state.ticksSinceLastStateSwitch = 0;
- this->lastTransitionDirection = this->state.frameTransitionDirection;
- this->state.frameTransitionDirection = 1;
- }
- }
- void OLEDDisplayUi::previousFrame() {
- if (this->state.frameState != IN_TRANSITION) {
- this->state.manuelControll = true;
- this->state.frameState = IN_TRANSITION;
- this->state.ticksSinceLastStateSwitch = 0;
- this->lastTransitionDirection = this->state.frameTransitionDirection;
- this->state.frameTransitionDirection = -1;
- }
- }
- void OLEDDisplayUi::switchToFrame(uint8_t frame) {
- if (frame >= this->frameCount) return;
- this->state.ticksSinceLastStateSwitch = 0;
- if (frame == this->state.currentFrame) return;
- this->state.frameState = FIXED;
- this->state.currentFrame = frame;
- this->state.isIndicatorDrawen = true;
- }
- void OLEDDisplayUi::transitionToFrame(uint8_t frame) {
- if (frame >= this->frameCount) return;
- this->state.ticksSinceLastStateSwitch = 0;
- if (frame == this->state.currentFrame) return;
- this->nextFrameNumber = frame;
- this->lastTransitionDirection = this->state.frameTransitionDirection;
- this->state.manuelControll = true;
- this->state.frameState = IN_TRANSITION;
- this->state.frameTransitionDirection = frame < this->state.currentFrame ? -1 : 1;
- }
- // -/----- State information -----\-
- OLEDDisplayUiState* OLEDDisplayUi::getUiState(){
- return &this->state;
- }
- int8_t OLEDDisplayUi::update(){
- long frameStart = millis();
- int8_t timeBudget = this->updateInterval - (frameStart - this->state.lastUpdate);
- if ( timeBudget <= 0) {
- // Implement frame skipping to ensure time budget is keept
- if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil(-timeBudget / this->updateInterval);
- this->state.lastUpdate = frameStart;
- this->tick();
- }
- return this->updateInterval - (millis() - frameStart);
- }
- void OLEDDisplayUi::tick() {
- this->state.ticksSinceLastStateSwitch++;
- switch (this->state.frameState) {
- case IN_TRANSITION:
- if (this->state.ticksSinceLastStateSwitch >= this->ticksPerTransition){
- this->state.frameState = FIXED;
- this->state.currentFrame = getNextFrameNumber();
- this->state.ticksSinceLastStateSwitch = 0;
- this->nextFrameNumber = -1;
- }
- break;
- case FIXED:
- // Revert manuelControll
- if (this->state.manuelControll) {
- this->state.frameTransitionDirection = this->lastTransitionDirection;
- this->state.manuelControll = false;
- }
- if (this->state.ticksSinceLastStateSwitch >= this->ticksPerFrame){
- if (this->autoTransition){
- this->state.frameState = IN_TRANSITION;
- }
- this->state.ticksSinceLastStateSwitch = 0;
- }
- break;
- }
- this->display->clear();
- this->drawFrame();
- if (shouldDrawIndicators) {
- this->drawIndicator();
- }
- this->drawOverlays();
- this->display->display();
- }
- void OLEDDisplayUi::resetState() {
- this->state.lastUpdate = 0;
- this->state.ticksSinceLastStateSwitch = 0;
- this->state.frameState = FIXED;
- this->state.currentFrame = 0;
- this->state.isIndicatorDrawen = true;
- }
- void OLEDDisplayUi::drawFrame(){
- switch (this->state.frameState){
- case IN_TRANSITION: {
- float progress = (float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition;
- int16_t x, y, x1, y1;
- switch(this->frameAnimationDirection){
- case SLIDE_LEFT:
- x = -128 * progress;
- y = 0;
- x1 = x + 128;
- y1 = 0;
- break;
- case SLIDE_RIGHT:
- x = 128 * progress;
- y = 0;
- x1 = x - 128;
- y1 = 0;
- break;
- case SLIDE_UP:
- x = 0;
- y = -64 * progress;
- x1 = 0;
- y1 = y + 64;
- break;
- case SLIDE_DOWN:
- x = 0;
- y = 64 * progress;
- x1 = 0;
- y1 = y - 64;
- break;
- }
- // Invert animation if direction is reversed.
- int8_t dir = this->state.frameTransitionDirection >= 0 ? 1 : -1;
- x *= dir; y *= dir; x1 *= dir; y1 *= dir;
- bool drawenCurrentFrame;
- // Prope each frameFunction for the indicator Drawen state
- this->enableIndicator();
- (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, x, y);
- drawenCurrentFrame = this->state.isIndicatorDrawen;
- this->enableIndicator();
- (this->frameFunctions[this->getNextFrameNumber()])(this->display, &this->state, x1, y1);
- // Build up the indicatorDrawState
- if (drawenCurrentFrame && !this->state.isIndicatorDrawen) {
- // Drawen now but not next
- this->indicatorDrawState = 2;
- } else if (!drawenCurrentFrame && this->state.isIndicatorDrawen) {
- // Not drawen now but next
- this->indicatorDrawState = 1;
- } else if (!drawenCurrentFrame && !this->state.isIndicatorDrawen) {
- // Not drawen in both frames
- this->indicatorDrawState = 3;
- }
- // If the indicator isn't draw in the current frame
- // reflect it in state.isIndicatorDrawen
- if (!drawenCurrentFrame) this->state.isIndicatorDrawen = false;
- break;
- }
- case FIXED:
- // Always assume that the indicator is drawn!
- // And set indicatorDrawState to "not known yet"
- this->indicatorDrawState = 0;
- this->enableIndicator();
- (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, 0, 0);
- break;
- }
- }
- void OLEDDisplayUi::drawIndicator() {
- // Only draw if the indicator is invisible
- // for both frames or
- // the indiactor is shown and we are IN_TRANSITION
- if (this->indicatorDrawState == 3 || (!this->state.isIndicatorDrawen && this->state.frameState != IN_TRANSITION)) {
- return;
- }
- uint8_t posOfHighlightFrame;
- float indicatorFadeProgress = 0;
- // if the indicator needs to be slided in we want to
- // highlight the next frame in the transition
- uint8_t frameToHighlight = this->indicatorDrawState == 1 ? this->getNextFrameNumber() : this->state.currentFrame;
- // Calculate the frame that needs to be highlighted
- // based on the Direction the indiactor is drawn
- switch (this->indicatorDirection){
- case LEFT_RIGHT:
- posOfHighlightFrame = frameToHighlight;
- break;
- case RIGHT_LEFT:
- posOfHighlightFrame = this->frameCount - frameToHighlight;
- break;
- }
- switch (this->indicatorDrawState) {
- case 1: // Indicator was not drawn in this frame but will be in next
- // Slide IN
- indicatorFadeProgress = 1 - ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition);
- break;
- case 2: // Indicator was drawn in this frame but not in next
- // Slide OUT
- indicatorFadeProgress = ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition);
- break;
- }
- uint16_t frameStartPos = (12 * frameCount / 2);
- const char *image;
- uint16_t x,y;
- for (byte i = 0; i < this->frameCount; i++) {
- switch (this->indicatorPosition){
- case TOP:
- y = 0 - (8 * indicatorFadeProgress);
- x = 64 - frameStartPos + 12 * i;
- break;
- case BOTTOM:
- y = 56 + (8 * indicatorFadeProgress);
- x = 64 - frameStartPos + 12 * i;
- break;
- case RIGHT:
- x = 120 + (8 * indicatorFadeProgress);
- y = 32 - frameStartPos + 2 + 12 * i;
- break;
- case LEFT:
- x = 0 - (8 * indicatorFadeProgress);
- y = 32 - frameStartPos + 2 + 12 * i;
- break;
- }
- if (posOfHighlightFrame == i) {
- image = this->activeSymbol;
- } else {
- image = this->inactiveSymbol;
- }
- this->display->drawFastImage(x, y, 8, 8, image);
- }
- }
- void OLEDDisplayUi::drawOverlays() {
- for (uint8_t i=0;i<this->overlayCount;i++){
- (this->overlayFunctions[i])(this->display, &this->state);
- }
- }
- uint8_t OLEDDisplayUi::getNextFrameNumber(){
- if (this->nextFrameNumber != -1) return this->nextFrameNumber;
- return (this->state.currentFrame + this->frameCount + this->state.frameTransitionDirection) % this->frameCount;
- }
|