109 lines
2.8 KiB
C++
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);
|
|
}
|
|
}
|