150 lines
3.7 KiB
C++
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();
|
|
}
|