// // Whirling.cpp // Whirling // // Created by Bob Polis at 2021-09-05 // Copyright (c) 2021 SwiftCoder. All rights reserved. // #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(); ~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() { 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); }