Example 4.5 uses a circle to trace the random walk of a circle across a window. The circle leaves a path as it moves, and it periodically changes its size and its color.
Example 4.5. Circle Random Walk
#include <vector> #include <GL/sgl.hpp> #include <fstream> #include <iostream> struct Circle { double x; // The location of the circle's center: double y; // point (x,y) double r; // The circle's radius sgl::Color color; // The circle's color // Constructor requires location, radius, and color information Circle(double x, double y, double r, const sgl::Color& color) : x(x), y(y), r(r), color(color) {} void draw() { sgl::set_color(color); sgl::draw_circle(x, y, r + 1); sgl::set_color(sgl::WHITE); sgl::fill_circle(x, y, r); } }; // A window that traces the movements of a circle // on a random walk. The trace leaves behind a multicolor // tube-like structure of varying diameter class RandomCircleWindow : public sgl::Window { double x; // Current position of the double y; // circle's center: (x, y) double z; // Circle's radius int dx; // Change in x int dy; // Change in y double dz; // Change in radius int run_x; // Length of run for dx int run_y; // Length of run for dy int run_z; // Length of run for radius change bool animate; // Are we animating? std::vector<Circle> track; // Record of all the circle locations sgl::Color current_color; // The current color for a circle int color_run; // Save the current locations to a text file void save() { std::string filename; std::cout << "Enter file name: "; // Get file name from user std::cin >> filename; std::ofstream fout(filename); // Open file for writing, if possible if (fout.good()) { fout << track.size() << '\n'; // Store number of circles // Store each circle's component data about its location, size, and color for (auto& circ : track) fout << circ.x << ' ' << circ.y << ' ' << circ.r << ' ' << circ.color.red << ' ' << circ.color.green << ' ' << circ.color.blue << '\n'; } else std::cout << "Could not save the file" << '\n'; } // Load circle locations from a text file void load() { std::string filename; std::cout << "Enter file name: "; // Get file name from user std::cin >> filename; std::ifstream fin(filename); // Oprn the file for reading, if possible if (fin.good()) { track.clear(); // Empty the circle vector int size; fin >> size; // Read number of circles // Obtain the location, size, and color information for each circle for (int i = 0; i < size; i++) { double in_x, in_y, in_z, red, green, blue; fin >> in_x >> in_y >> in_z; fin >> red >> green >> blue; // Add the circle to the collection of circles track.push_back(Circle(in_x, in_y, in_z, sgl::Color(red, green, blue))); } } else std::cout << "Could not load the file" << '\n'; } public: // Create a window of sufficient size RandomCircleWindow(double min_x, double max_x, double min_y, double max_y) : sgl::Window("Tubes", min_x, max_x, min_y, max_y), x((min_x + max_x) / 2), y((min_y + max_y) / 2), z(10), dx(0), dy(0), dz(0.01), run_x(0), run_y(0), run_z(0), animate(true), current_color(0.9, 0.9, 0.9), color_run(0) { start_timer(1); // Enable animation } // Draw the track of all the multicolor circles void paint() override { for (auto& circ : track) circ.draw(); } // Respond to user's key strokes void key_pressed(int k, double mouse_x, double mouse_y) override { if (animate) animate = false; else { animate = true; start_timer(1); } animate = !animate; // Pause or resume switch (k) { case 'S': // Save the circle track to a text file case 's': save(); break; case 'L': // Load a circle track from a text file case 'l': load(); break; } std::cout << "# circles: " << track.size() << '\n'; repaint(); sgl::Window::key_pressed(k, mouse_x, mouse_y); // Defer to base class for further processing } void timer_expired() override { // Determine if a run of each component has run its course // and update if necessary if (run_x == 0) { run_x = sgl::random(1, 100); // Determine length of next run for x dx = sgl::random(-3, 3); // New x direction and speed } if (run_y == 0) { run_y = sgl::random(1, 100); // Determine length of next run for y dy = sgl::random(-3, 3); // New y direction and speed } if (run_z == 0) { run_z = sgl::random(1, 100); // Determine length of next run for the radius dz = 0.1* sgl::random(-3, 3); // New radius //dz = 0.1; // Fixed radius } x += dx; // Update x y += dy; // Update y z += dz; // Update radius // Limit the range of the circle's random walk x = (x < min_x - z) ? min_x - z : x; x = (x > max_x + z) ? max_x + z : x; y = (y < min_y - z) ? min_y - z : y; y = (y > max_y + z) ? max_y + z : y; // Bound the circle's size z = (z < 10) ? 10 : z; z = (z > 30) ? 30 : z; run_x--; // Completed one more step of the run for x run_y--; // Completed one more step of the run for y run_z--; // Completed one more step of the run for radius // Determine if the color has run its course, and a new // color should be generated if (color_run == 0) { color_run = sgl::random(100, 1000); // New run length current_color.red = sgl::random(1, 1000) / 1000.0; // New red component current_color.green = sgl::random(1, 1000) / 1000.0; // New green component current_color.blue = sgl::random(1, 1000) / 1000.0; // New blue component } color_run--; // Completed one more step of the run for this color if (animate) // Should we continue to track circles? start_timer(1); //repaint(); // Record the information for this circle track.push_back(Circle(x, y, z, current_color)); } }; int main() { sgl::set_random_seed(42); // Set the random number generator seed // Make and run a window for displaying the circles RandomCircleWindow{0, 1900, 0, 1000}.run(); }
The net effect of
Example 4.5
is that it leaves
an image that resembles multicolored pipes flowing through a
space.
Example 4.5 is responsible for
the art on the
title page of this document.
Figure 4.6 provides a screenshot of one of the random walks.
Copyright ©2019 Richard L. Halterman | Version 0.9.5 | February 17, 2019 |