1 /**
2 Copyright: Copyright (c) 2013-2014 Andrey Penechko.
3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module anchovy.gui.utils.timer;
8 
9 import core.time;
10 import std.math: isNaN, trunc;
11 
12 import anchovy.gui;
13 
14 /// Handler must number > 0 if custom repeat needed.
15 /// Timer will be added to queue again with nextTime set to number.
16 /// If number = 0 then nextTime will be set to Timer.delay.
17 /// If number < 0 or is nan, timer will be removed from queue.
18 alias double delegate(double dt) TimerHandler;
19 
20 enum TimerTickType
21 {
22 	PROCESS_ALL_UNORDERED,
23 	PROCESS_ALL_ORDERED,
24 	PROCESS_LAST,
25 }
26 
27 /// Provides basic timer functionality.
28 /// Designed to be used with TimerManager.
29 class Timer
30 {
31 	/// Used for initializing timer.
32 	/// Params:
33 	/// 	delay specifies the delay after which handler will be called.
34 	/// 	handler specifies handler to be called when delay exceeds.
35 
36 	/// 	tickType sets processing method to be used with this timer.
37 	void initialize(double _firstTime, double _currentTime, double _delay, TimerHandler _handler, TimerTickType _tickType = TimerTickType.init)
38 	in
39 	{
40 		assert(_handler);
41 		assert(_delay > 0);
42 		assert(_firstTime > 0);
43 		assert(_currentTime > 0);
44 	}
45 	body
46 	{
47 		delay = _delay;
48 		lastUpdateTime = _currentTime;
49 		handler = _handler;
50 		tickType = _tickType;
51 		nextUpdateTime = _firstTime;
52 	}
53 
54 	/// The whole delta time will be provided if currentTime > nextTime even when dt % thisDelay > 1.
55 	/// Timer will automaticaly decide to process only one period, or all. It can also process only the last update if needed.
56 	/// This method will be called only when currentTime > nextTime.
57 	void tick(double currentTime)
58 	{
59 		void updateNextTime(double newNextTime)
60 		{
61 			lastUpdateTime = nextUpdateTime;
62 
63 			if (newNextTime == 0)
64 				nextUpdateTime += delay;
65 			else if (newNextTime > 0)
66 				nextUpdateTime += newNextTime;
67 			else
68 				nextUpdateTime = double.nan;
69 		}
70 
71 		double newNextTime;
72 
73 		with(TimerTickType)
74 		final switch(tickType)
75 		{
76 			case PROCESS_ALL_ORDERED:
77 			{
78 				newNextTime = handler(nextUpdateTime - lastUpdateTime);
79 				updateNextTime(newNextTime);
80 				break;
81 			}
82 			case PROCESS_ALL_UNORDERED:
83 			{
84 				while(currentTime > nextUpdateTime)
85 				{
86 					newNextTime = handler(nextUpdateTime - lastUpdateTime);
87 					updateNextTime(newNextTime);
88 				}
89 				break;
90 			}
91 			case PROCESS_LAST:
92 			{
93 				uint timesUpdated = cast(uint)trunc((currentTime - nextUpdateTime) / delay) + 1;
94 				nextUpdateTime += timesUpdated * delay;
95 
96 				newNextTime = handler(timesUpdated);
97 				updateNextTime(newNextTime);
98 				break;
99 			}
100 		}
101 	}
102 
103 	TimerHandler handler;
104 
105 	TimerTickType tickType;
106 
107 	double lastUpdateTime;
108 	double nextUpdateTime;
109 	double delay;
110 }