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 |