178 lines
4.9 KiB
C++
178 lines
4.9 KiB
C++
#include "Grid.hpp"
|
|
#include <libsccolor.hpp>
|
|
#include <libscscreensaver.hpp>
|
|
#include <libscnumerics.hpp>
|
|
#include <vector>
|
|
#include <cmath>
|
|
#include <string>
|
|
|
|
const std::string black {"#000000"};
|
|
const std::string white {"#FFFFFF"};
|
|
const std::string sand {"#B69061"};
|
|
const std::string pastel_green {"#B1D1AF"};
|
|
|
|
struct Rect {
|
|
double cx {0};
|
|
double cy {0};
|
|
double width {0};
|
|
Color color {black};
|
|
};
|
|
|
|
struct Situation {
|
|
std::vector<Rect> rects;
|
|
Color bg {pastel_green};
|
|
};
|
|
|
|
enum class State {wait, fade};
|
|
|
|
class Grid : public ScreensaverPlugin {
|
|
public:
|
|
Grid() = default;
|
|
~Grid() = default;
|
|
|
|
void setup(cairo_t* context, const cairo_rectangle_t& rect) override;
|
|
void configure() override;
|
|
int fps() const override;
|
|
void update() override;
|
|
void render() override;
|
|
|
|
private:
|
|
size_t _h;
|
|
size_t _v;
|
|
Situation _old;
|
|
Situation _new;
|
|
Situation _cur;
|
|
std::vector<Color> _colors;
|
|
int _frames {0};
|
|
State _s {State::fade};
|
|
double _d {75}; // grid point distance
|
|
int _wait {7}; // seconds
|
|
int _fade {3}; // seconds
|
|
double _border {75};
|
|
double _min_size {2};
|
|
double _max_size {50};
|
|
bool _change_bg {true};
|
|
void animate();
|
|
};
|
|
|
|
ScreensaverPlugin* create_instance() {
|
|
return new Grid;
|
|
}
|
|
|
|
void Grid::setup(cairo_t* context, const cairo_rectangle_t& rect) {
|
|
ScreensaverPlugin::setup(context, rect);
|
|
if (_colors.size() == 0) {
|
|
_colors.emplace_back(black);
|
|
_colors.emplace_back(white);
|
|
_colors.emplace_back(sand);
|
|
}
|
|
_old.rects.clear();
|
|
_old.bg = black;
|
|
_new.rects.clear();
|
|
_cur.rects.clear();
|
|
cairo_rectangle_t inset {rect};
|
|
inset.width -= 2 * _border;
|
|
inset.height -= 2 * _border;
|
|
_h = static_cast<size_t>(round(inset.width / _d));
|
|
_v = static_cast<size_t>(round(inset.height / _d));
|
|
double h_margin = (inset.width - (_h - 1) * _d) / 2;
|
|
double v_margin = (inset.height - (_v - 1) * _d) / 2;
|
|
for (size_t x = 0; x < _h; ++x) {
|
|
for (size_t y = 0; y < _v; ++y) {
|
|
Rect r;
|
|
r.cx = _border + h_margin + x * _d;
|
|
r.cy = _border + v_margin + y * _d;
|
|
r.width = 0;
|
|
r.color = black;
|
|
_old.rects.push_back(r);
|
|
_cur.rects.push_back(r);
|
|
r.width = random_between(_min_size, _max_size);
|
|
r.color = sc::random::choice(_colors);
|
|
_new.rects.push_back(r);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Grid::configure() {
|
|
_min_size = _j["min-size"];
|
|
_max_size = _j["max-size"];
|
|
_d = _j["dist"];
|
|
_wait = _j["wait"];
|
|
_fade = _j["fade"];
|
|
_border = _j["border"];
|
|
_new.bg = Color {_j["bg"]};
|
|
_change_bg = _j["changebg"];
|
|
_colors.clear();
|
|
for (const auto& hex : _j["colors"]) {
|
|
_colors.emplace_back(hex);
|
|
}
|
|
setup(_c, _r);
|
|
}
|
|
|
|
int Grid::fps() const {
|
|
return 30;
|
|
}
|
|
|
|
void Grid::update() {
|
|
// adjust state for next render
|
|
switch (_s) {
|
|
case State::wait:
|
|
if (++_frames > _wait * fps()) {
|
|
_frames = 0;
|
|
_s = State::fade;
|
|
}
|
|
break;
|
|
case State::fade:
|
|
if (++_frames > _fade * fps()) {
|
|
_frames = 0;
|
|
_old = _new;
|
|
for (Rect& r : _new.rects) {
|
|
r.width = random_between(_min_size, _max_size);
|
|
r.color = sc::random::choice(_colors);
|
|
}
|
|
HSB newbg {random_between(0, 360), 0.16, 0.82};
|
|
_new.bg = newbg;
|
|
_s = State::wait;
|
|
} else {
|
|
animate();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Grid::render() {
|
|
// render one frame based on current state
|
|
if (_s == State::fade) {
|
|
RGB bg {RGB(_cur.bg)};
|
|
cairo_set_source_rgb(_c, bg.r, bg.g, bg.b);
|
|
cairo_rectangle(_c, _r.x, _r.y, _r.width, _r.height);
|
|
cairo_fill(_c);
|
|
for (const Rect& r : _cur.rects) {
|
|
RGB col {RGB(r.color)};
|
|
cairo_set_source_rgb(_c, col.r, col.g, col.b);
|
|
cairo_rectangle(_c, r.cx - r.width / 2, r.cy - r.width / 2, r.width, r.width);
|
|
cairo_fill(_c);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Grid::animate() {
|
|
double f {static_cast<double>(_frames) / (_fade * fps())};
|
|
if (_change_bg) {
|
|
RGB bg1 {RGB(_old.bg)};
|
|
RGB bg2 {RGB(_new.bg)};
|
|
RGB bg3 {bg1.r + f * (bg2.r - bg1.r), bg1.g + f * (bg2.g - bg1.g), bg1.b + f * (bg2.b - bg1.b)};
|
|
_cur.bg = bg3;
|
|
}
|
|
for (size_t i = 0; i < _old.rects.size(); ++i) {
|
|
Rect& r1 = _old.rects[i];
|
|
Rect& r2 = _new.rects[i];
|
|
Rect& r3 = _cur.rects[i];
|
|
RGB rgb1 {RGB(r1.color)};
|
|
RGB rgb2 {RGB(r2.color)};
|
|
RGB rgb3 {rgb1.r + f * (rgb2.r - rgb1.r), rgb1.g + f * (rgb2.g - rgb1.g), rgb1.b + f * (rgb2.b - rgb1.b)};
|
|
r3.color = rgb3;
|
|
r3.width = r1.width + f * (r2.width - r1.width);
|
|
}
|
|
}
|