Example 4.4 illustrates the use of an SGL timer that controls the aimation of an analog clock.
Example 4.4. Clock Animation
#include <GL/sgl.hpp> #include <cmath> #include <sstream> #include <iomanip> const double MIN_X = -100.0, MAX_X = 100.0, MIN_Y = -100.0, MAX_Y = 100.0; const int FONT_SIZE = 18; const int TIME_INCREMENT = 200; const double PI = 3.141592653589793, // Close enough! HALF_PI = PI/2.0; std::ostringstream oss; sgl::Point minutes_to_position(int minutes, double offset) { double x = MAX_X/2.0 - offset, y = 0.0, fraction = -PI*minutes/30 + HALF_PI, sine = std::sin(fraction), cosine = std::cos(fraction); return {x*cosine - y*sine, x*sine + y*cosine}; } sgl::Point hours_to_position(int hours, int minutes, double offset) { return minutes_to_position(static_cast<int>((hours + minutes/60.0)*5), offset); } std::string time_to_string(int hours, int minutes) { oss.str(""); // Clear the output string oss.width(2); oss.fill(' '); oss << ((hours == 0)? 12 : hours) << ":"; oss.width(2); oss.fill('0'); oss << minutes; return oss.str(); } class ClockWindow: public sgl::Window { int hours; int minutes; public: ClockWindow(): sgl::Window("Clock", 100, 100, 500, 500, MIN_X, MAX_X, MIN_Y, MAX_Y), hours(0), minutes(0) { start_timer(TIME_INCREMENT); } void paint() override { // Draw frame sgl::set_line_width(1); sgl::draw_circle(0.0, 0.0, MIN_X/2.0 - 5.0); // Draw center sgl::fill_circle(0.0, 0.0, 2.0); // Draw positions sgl::Point loc; for (int i = 0; i < 12; i++) { loc = hours_to_position(i, 0, 1.0); sgl::fill_rectangle(loc.x - 2.0, loc.y - 2.0, 4.0, 4.0); } // Draw minute hand sgl::set_line_width(2); loc = minutes_to_position(minutes, 5.0); sgl::draw_line(0.0, 0.0, loc.x, loc.y); // Draw hour hand sgl::set_line_width(5); loc = hours_to_position(hours, minutes, 20.0); sgl::draw_line(0.0, 0.0, loc.x, loc.y); // Print digital text sgl::draw_text(time_to_string(hours, minutes).c_str(), -5.0, MIN_Y + 5.0, 18); } void update_time() { minutes++; if (minutes == 60) { minutes = 0; hours++; if (hours == 12) hours = 0; } repaint(); } void timer_expired() { update_time(); start_timer(TIME_INCREMENT); } void mouse_pressed(double, double, sgl::MouseButton) override { update_time(); } void key_pressed(int key, double x, double y) override { update_time(); sgl::Window::key_pressed(key, x, y); } void resized(int w, int h) override { if (w != h) { if (w < h) h = w; else w = h; set_window_size(w, h); } sgl::Window::resized(w, h); } }; int main() { sgl::run<ClockWindow>(); }Figure 4.5 shows the program in action.
Observe that the program provides a digital display of the time in
addition to the analog clock face.
The window's constructor sets a timer running.
Every
200 milliseconds (1/5 of a second) the
event manager calls the window's timer_expired
method.
The timer_expired
method calls the
update_time
method to adjust the
hours
and
minutes
values and notifies the event manager
that the window needs to be
repainted. The paint
method draws the clock
with the hands in their proper places based on the values of
hours
and minutes
. Some
helper functions take care of the mathematics.
Observe that an SGL timer (like a GLUT timer) sends the notification
event once; the programmer must restart the timer after each
timeout to continuously send notifications to the window.
In Example 4.4, in addition to the automatic animation controlled by the timer, the user can speed up the minute time lapses by pressing a mouse button or pressing a key.
Copyright ©2019 Richard L. Halterman | Version 0.9.5 | February 17, 2019 |