Program Listing for File section.h

Return to documentation for file (src/core/section.h)

/*
    Section.cpp - Class for controlling multiple Pixels.
*/

#ifndef SECTION_H
#define SECTION_H

#include <stdint.h>
#include "../animation/animation.h"
#include "../animation/animationtype.h"
#include "../canvas/canvas.h"
#include "colors.h"
#include "maestro.h"
#include "point.h"
#include "pixel.h"
#include "../utility.h"

namespace PixelMaestro {
    class Canvas;

    class Animation;

    class Maestro;

    class Section {

        public:
            struct Scroll {
                uint16_t interval_x = 0;
                uint16_t interval_y = 0;

                bool reverse_x = false;
                bool reverse_y = false;

                Timer* timer_x = nullptr;
                Timer* timer_y = nullptr;

                uint16_t step_x = 0;
                uint16_t step_y = 0;

                void remove_timer_x() {
                    delete timer_x;
                    timer_x = nullptr;
                }

                void remove_timer_y() {
                    delete timer_y;
                    timer_y = nullptr;
                }

                void set(uint16_t refresh_interval, Point* dimensions, uint16_t interval_x, uint16_t interval_y, bool reverse_x = false, bool reverse_y = false) {
                    this->interval_x = interval_x;
                    this->interval_y = interval_y;
                    this->reverse_x = reverse_x;
                    this->reverse_y = reverse_y;
                    /*
                     * Calculate step counts.
                     * Using the scroll interval, we need to determine how to change the offset values on each refresh.
                     *
                     * If the interval is low (Section scrolls quickly), each update moves the image > 1 pixel.
                     * In this case, we simply adjust the offset by the number of pixels it needs to move per refresh.
                     *
                     * If the interval is high (Section scrolls slowly), each update moves the image < 1 pixel.
                     * In this case, we calculate the amount of time until the offset moves 1 pixel.
                     * If this time is larger than the Maestro's refresh interval, scrolling won't occur until at least the next refresh.
                     */

                    /*
                     * Calculate the x-axis step count.
                     *
                     * Divide the x interval by the Maestro's refresh rate, then divide the Section's x-axis size by the result.
                     * This gives you the number of pixels to move over per refresh.
                     */
                    float x = dimensions->x / (float)(interval_x / (float)refresh_interval);
                    // If x is less than 1 pixel, calculate the amount of time until the Section scrolls by 1 pixel.
                    if (x > 0 && x < 1) {
                        uint16_t interval = (1 / x) * refresh_interval;
                        if (timer_x) {
                            timer_x->set_interval(interval);
                        }
                        else {
                            timer_x = new Timer(interval);
                        }
                    }
                    // x is greater than 1 pixel, so use x as our step amount.
                    else {
                        remove_timer_x();
                        step_x = x;
                    }

                    float y = dimensions->y / (float)(interval_y / (float)refresh_interval);
                    if (y > 0 && y < 1) {
                        uint16_t interval = (1 / y) * refresh_interval;
                        if (timer_y) {
                            timer_y->set_interval(interval);
                        }
                        else {
                            timer_y = new Timer(interval);
                        }
                    }
                    else {
                        remove_timer_y();
                        step_y = y;
                    }
                }

                ~Scroll() {
                    remove_timer_x();
                    remove_timer_y();
                }
            };

            struct Layer {
                Section* section = nullptr;

                Colors::MixMode mix_mode = Colors::MixMode::None;

                uint8_t alpha;

                Layer(Section& parent, Colors::MixMode mix_mode, uint8_t alpha = 0) {
                    this->section = new Section(parent.get_dimensions().x, parent.get_dimensions().y, &parent);
                    this->section->set_maestro(parent.get_maestro());
                    this->mix_mode = mix_mode;
                    this->alpha = alpha;
                }

                ~Layer() {
                    delete this->section;
                }
            };

            struct Mirror {
                bool x = false;
                bool y = false;

                uint16_t mid_x = 0;
                uint16_t mid_y = 0;

                void set(bool x, bool y, const Point& dimensions) {
                    this->x = x;
                    this->y = y;

                    this->mid_x = dimensions.x / static_cast<float>(2);
                    this->mid_y = dimensions.y / static_cast<float>(2);
                }
            };

            Section();
            Section(uint16_t x, uint16_t y, Section* parent = nullptr);
            ~Section();
            Animation* get_animation() const;
            uint8_t get_brightness() const;
            Canvas* get_canvas() const;
            Point& get_dimensions() const;
            Section::Layer* get_layer() const;
            Maestro& get_maestro() const;
            Mirror* get_mirror() const;
            Point& get_offset();
            Section* get_parent_section() const;
            Pixel& get_pixel(uint16_t x, uint16_t y) const;
            Colors::RGB get_pixel_color(uint16_t x, uint16_t y, Colors::RGB* base_color = nullptr);
            Scroll* get_scroll() const;
            bool get_wrap() const;
            void remove_animation(bool clear_pixels);
            void remove_canvas();
            void remove_layer();
            void remove_scroll();
            Animation& set_animation(AnimationType animation_type, bool preserve_settings = true);
            void set_brightness(uint8_t brightness);
            Canvas& set_canvas(uint16_t num_frames = 1);
            void set_dimensions(uint16_t x, uint16_t y);
            Layer& set_layer(Colors::MixMode mix_mode = Colors::MixMode::Alpha, uint8_t alpha = 128);
            void set_maestro(Maestro& maestro);
            Mirror* set_mirror(bool x, bool y);
            Point& set_offset(uint16_t x, uint16_t y);
            void set_pixel_color(uint16_t x, uint16_t y, const Colors::RGB& color);
            Scroll& set_scroll(uint16_t x, uint16_t y, bool reverse_x = false, bool reverse_y = false);
            void set_step_count(uint8_t step_count);
            void set_wrap(bool wrap);
            void sync(const uint32_t& new_time);
            void update(const uint32_t& current_time);
            void update_scroll(const uint32_t& current_time);

        private:
            Animation* animation_ = nullptr;

            float brightness_ = 1.0;

            Canvas* canvas_ = nullptr;

            Point dimensions_;

            Layer* layer_ = nullptr;

            Maestro* maestro_ = nullptr;

            Mirror* mirror_ = nullptr;

            Point offset_ = Point(0, 0);

            Section* parent_section_ = nullptr;

            Pixel* pixels_ = nullptr;

            Scroll* scroll_ = nullptr;

            uint8_t step_count_ = 1;

            bool wrap_ = true;
    };
}

#endif