150 lines
3.7 KiB
C++

#include "FadingRects.hpp"
#include <libscscreensaver.hpp>
#include <libsccolor.hpp>
#include <libscnumerics.hpp>
#include <vector>
#include <algorithm>
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<Rect> _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();
}