diff --git a/modules/Whirling/Whirling.cpp b/modules/Whirling/Whirling.cpp index 1469c92..248ee0e 100644 --- a/modules/Whirling/Whirling.cpp +++ b/modules/Whirling/Whirling.cpp @@ -8,31 +8,126 @@ #include "Whirling.hpp" #include +#include +#include + +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; + + Lissajous(double f0, + double f1, + double f2, + double f3, + double p0, + double p1, + double p2, + double p3, + int segments, + double shift); + + double calc_x(double phi) const; + double calc_y(double phi) const; +}; + +Lissajous::Lissajous(double f0, + double f1, + double f2, + double f3, + double p0, + double p1, + double p2, + double p3, + int segments, + double shift) +{ + f[0] = f0; f[1] = f1; f[2] = f2; f[3] = f3; + p[0] = p0; p[1] = p1; p[2] = p2; p[3] = p3; + steps = segments; + delta = shift; +} + +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(); ~Whirling() = default; int fps() const override; void update() override; void render() override; + + private: + std::vector knots; + + void render_knot(const Lissajous& knot); }; ScreensaverPlugin* create_instance() { return new Whirling; } +Whirling::Whirling() { + knots.emplace_back(9, 7, 5, 7, + 0, 0, 0, 0, + 1000, + 2 * M_PI / 360.0); + Lissajous& knot = knots.back(); + knot.color = {1.0, 1.0, 1.0}; // white + knot.alpha = 1.0; // opaque + knot.line_width = 1.0; +} + int Whirling::fps() const { return 30; } void Whirling::update() { - // adjust state for next render - + 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() { - // render one frame based on current state - + 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); }