#include "FadingRects.hpp" #include #include #include #include const int default_frames_per_rect {10}; const int fade_time {5}; // seconds enum class FadingState { fadein, sustain, fadeout }; struct Rect { double x {0}; double y {0}; double width {0}; double height {0}; double red {0.0}; double green {0.0}; double blue {0.0}; double alpha {1.0}; double cur_alpha {0.0}; double delta {0.0}; FadingState state {FadingState::fadein}; int sustain_frame {0}; double line_width {2.0}; }; class FadingRects : public ScreensaverPlugin { public: FadingRects() = default; ~FadingRects() = default; void setup(cairo_t* context, const cairo_rectangle_t& rect) override; int fps() const override; void update () override; void render() override; private: double _hue {0.0}; std::vector _rects; int _frames_per_rect {default_frames_per_rect}; Color next_color(); }; ScreensaverPlugin* create_instance() { return new FadingRects; } int FadingRects::fps() const { return 20; } void FadingRects::update() { // adjust rects for (Rect& r : _rects) { switch (r.state) { case FadingState::fadein: r.cur_alpha += r.delta; if (r.cur_alpha > r.alpha) { r.cur_alpha = r.alpha; r.state = FadingState::sustain; } break; case FadingState::sustain: r.sustain_frame++; if (r.sustain_frame > fps()) { r.state = FadingState::fadeout; } break; case FadingState::fadeout: r.cur_alpha -= r.delta; break; } } auto it = std::remove_if(_rects.begin(), _rects.end(), [](const Rect& r) { return r.cur_alpha < 0.0; }); _rects.erase(it, _rects.end()); // add new rect, if allowed _frames_per_rect--; if (_frames_per_rect == 0) { _frames_per_rect = default_frames_per_rect; cairo_rectangle_t rr {_r}; rr.x -= 50.0; rr.y -= 50.0; rr.width += 100.0; rr.height += 100.0; rr = random_rect_in_rect(rr); RGB rgb {RGB(next_color())}; Rect rect; rect.red = rgb.r; rect.green = rgb.g; rect.blue = rgb.b; rect.alpha = random01(); rect.cur_alpha = 0.0; rect.state = FadingState::fadein; rect.sustain_frame = 0; rect.delta = rect.alpha / (fade_time * fps()); rect.line_width = random_between(1.0, 20.0); rect.x = rr.x; rect.y = rr.y; rect.width = rr.width; rect.height = rr.height; _rects.push_back(rect); } } void FadingRects::render() { // clear screen make_black(); // render rects for (Rect r : _rects) { cairo_set_source_rgba(_c, r.red, r.green, r.blue, r.cur_alpha); cairo_rectangle_t rect; rect.x = r.x; rect.y = r.y; rect.width = r.width; rect.height = r.height; rounded_rect(rect, 10.0); cairo_set_line_width(_c, r.line_width); cairo_stroke(_c); } } Color FadingRects::next_color() { _hue += 0.5; if (_hue >= 360.0) { _hue -= 360.0; } HSB hsb; hsb.h = _hue; hsb.s = random01(); hsb.b = random01(); return Color {hsb}; } void FadingRects::setup(cairo_t* context, const cairo_rectangle_t& rect) { ScreensaverPlugin::setup(context, rect); _hue = sc::random::double_between(0.0, 360.0); _rects.clear(); }