109 lines
2.8 KiB
C++

#include "Whirling.hpp"
#include <libsccolor.hpp>
#include <libscscreensaver.hpp>
#include <vector>
#include <cmath>
struct Lissajous {
double f[4]; // frequencies
double p[4]; // phases
int steps; // number of line segments
double delta; // phase shift per frame
RGB color;
double alpha;
double line_width;
cairo_rectangle_t frame;
double calc_x(double phi) const;
double calc_y(double phi) const;
};
double Lissajous::calc_x(double phi) const {
return sin(f[0] * phi + p[0]) * cos(f[1] * phi + p[1]);
}
double Lissajous::calc_y(double phi) const {
return sin(f[2] * phi + p[2]) * cos(f[3] * phi + p[3]);
}
class Whirling : public ScreensaverPlugin {
public:
Whirling() = default;
~Whirling() = default;
int fps() const override;
void update() override;
void render() override;
void configure() override;
private:
std::vector<Lissajous> knots;
void render_knot(const Lissajous& knot);
};
ScreensaverPlugin* create_instance() {
return new Whirling;
}
int Whirling::fps() const {
return 30;
}
void Whirling::update() {
for (Lissajous& knot : knots) {
for (int i = 0; i < 4; ++i) {
knot.p[i] += knot.delta;
}
knot.frame.x = _r.width * 0.5;
knot.frame.y = _r.height * 0.5;
double sz = _r.width < _r.height ? _r.width : _r.height;
knot.frame.width = sz * 0.45;
knot.frame.height = sz * 0.45;
}
}
void Whirling::render() {
make_black();
for (const Lissajous& knot : knots) {
render_knot(knot);
}
}
void Whirling::render_knot(const Lissajous& knot) {
cairo_new_path(_c);
cairo_move_to(_c,
knot.frame.width * knot.calc_x(0) + knot.frame.x,
knot.frame.height * knot.calc_y(0) + knot.frame.y);
for (int step = 1; step < knot.steps; ++step) {
double phi = step * 2 * M_PI / knot.steps;
cairo_line_to(_c,
knot.frame.width * knot.calc_x(phi) + knot.frame.x,
knot.frame.height * knot.calc_y(phi) + knot.frame.y);
}
cairo_close_path(_c);
cairo_set_source_rgba(_c, knot.color.r, knot.color.g, knot.color.b, knot.alpha);
cairo_set_line_width(_c, knot.line_width);
cairo_stroke(_c);
}
void Whirling::configure() {
for (const auto& obj : _j["knots"]) {
Lissajous knot;
for (int i = 0; i < 4; ++i) {
knot.f[i] = obj["f"][i];
}
for (int i = 0; i < 4; ++i) {
knot.p[i] = obj["p"][i];
}
knot.steps = obj["steps"];
knot.delta = obj["delta"];
knot.color.r = obj["r"];
knot.color.g = obj["g"];
knot.color.b = obj["b"];
knot.alpha = obj["a"];
knot.line_width = obj["line_width"];
knots.push_back(knot);
}
}