1 /*
2  *******************************************************************************************
3  * Dgame (a D game framework) - Copyright (c) Randy Schütt
4  *
5  * This software is provided 'as-is', without any express or implied warranty.
6  * In no event will the authors be held liable for any damages arising from
7  * the use of this software.
8  *
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  *
13  * 1. The origin of this software must not be misrepresented; you must not claim
14  *    that you wrote the original software. If you use this software in a product,
15  *    an acknowledgment in the product documentation would be appreciated but is
16  *    not required.
17  *
18  * 2. Altered source versions must be plainly marked as such, and must not be
19  *    misrepresented as being the original software.
20  *
21  * 3. This notice may not be removed or altered from any source distribution.
22  *******************************************************************************************
23  */
24 module Dgame.System.StopWatch;
25 
26 private import derelict.sdl2.functions;
27 
28 /**
29  * Convert the Clock milliseconds to seconds
30  */
31 @nogc
32 float asSeconds(uint n) pure nothrow {
33     return n >= 1000 ? (n / 1000) : 0;
34 }
35 
36 /**
37  * Convert the Clock milliseconds to minutes
38  */
39 @nogc
40 float asMinutes(uint n) pure nothrow {
41     immutable float secs = asSeconds(n);
42 
43     return secs >= 60 ? (secs / 60) : 0;
44 }
45 
46 /**
47  * Convert the Clock milliseconds to hours
48  */
49 @nogc
50 uint asHours(uint n) pure nothrow {
51     immutable float mins = asMinutes(n);
52 
53     return mins >= 60 ? cast(uint)(mins / 60) : 0;
54 }
55 
56 /**
57 * Returns the given seconds in milliseconds
58 *
59 * Example:
60 * ----
61 * int n = 5.seconds; // n is 5000 because 5 seconds are 5000 milliseconds
62 * ----
63 *
64 * ----
65 * StopWatch sw;
66 * sw.wait(5.seconds);
67 * ----
68 */
69 @property
70 @nogc
71 uint seconds(uint n) pure nothrow {
72     return n * 1000;
73 }
74 
75 /**
76 * Returns the given minutes in milliseconds
77 *
78 * Example:
79 * ----
80 * int n = 5.minutes; // n is 300_000 because 5 minutes are 300 seconds are 300_000 milliseconds
81 * ----
82 *
83 * ----
84 * StopWatch sw;
85 * sw.wait(5.minutes);
86 * ----
87 */
88 @property
89 @nogc
90 uint minutes(uint n) pure nothrow {
91     return (n * 60).seconds;
92 }
93 
94 /**
95  * The Time struct converts ticks to msecs, seconds, minutes and hours.
96  *
97  * Author: Randy Schuett (rswhite4@googlemail.com)
98  */
99 struct Time {
100     /**
101      * Milliseconds = Ticks
102      */
103     uint msecs;
104     /**
105      * Seconds = Milliseconds / 1000
106      */
107     float seconds;
108     /**
109      * Minutes = Seconds / 60
110      */
111     float minutes;
112     /**
113      * Hours = Minutes / 60
114      */
115     uint hours;
116 
117     /**
118      * CTor
119      */
120     @nogc
121     this(uint msecs) pure nothrow {
122         this.msecs   = msecs;
123         this.seconds = asSeconds(msecs);
124         this.minutes = asMinutes(msecs);
125         this.hours   = asHours(msecs);
126     }
127 
128     /**
129      * Calculate the <b>remaining</b> time.
130      */
131     @nogc
132     static Time remain(Time time) pure nothrow {
133         import std.math : floor;
134 
135         immutable float min = time.minutes;
136         immutable float sec = time.seconds;
137 
138         time.minutes -= floor(float(time.hours)) * 60;
139         time.minutes = floor(time.minutes);
140         time.seconds -= floor(min) * 60;
141         time.msecs -= cast(uint)(floor(sec) * 1000);
142 
143         return time;
144     }
145 }
146 
147 unittest {
148     Time time = Time(65_000);
149 
150     assert(time.msecs == 65_000);
151     assert(time.seconds == 65);
152     assert(time.minutes >= 1.08f && time.minutes <= 1.09f);
153 
154     time = Time.remain(time);
155 
156     assert(time.msecs == 0f);
157     assert(time.seconds == 5f);
158     assert(time.minutes == 1f);
159 }
160 
161 /**
162  * This class handles timer functions and
163  * the window class use these class to calculate the current fps.
164  *
165  * Author: Randy Schuett (rswhite4@googlemail.com)
166  */
167 struct StopWatch {
168 private:
169     uint _startTime;
170     uint _numFrames;
171     uint _currentFps;
172 
173 public:
174     /**
175      * Reset the clock time
176      */
177     @nogc
178     void reset() nothrow {
179         _startTime = SDL_GetTicks();
180     }
181 
182     /**
183      * Returns the elapsed Time since the last reset.
184      */
185     @nogc
186     Time getElapsedTime() const nothrow {
187         return Time(this.getElapsedTicks());
188     }
189 
190     /**
191      * Returns only the milliseconds since the last reset.
192      */
193     @nogc
194     uint getElapsedTicks() const nothrow {
195         return SDL_GetTicks() - _startTime;
196     }
197 
198     /**
199      * Returns the current framerate per seconds.
200      * If frame_ms is not null, the average ms per frame is stored there
201      */
202     @nogc
203     uint getCurrentFPS(uint* frame_ms = null) nothrow {
204         immutable uint elapsed_ticks = this.getElapsedTicks();
205 
206         if (frame_ms)
207             *frame_ms = elapsed_ticks / _numFrames;
208 
209         if (elapsed_ticks >= 1000) {
210             _currentFps = _numFrames;
211             _numFrames = 0;
212             this.reset();
213         }
214 
215         _numFrames++;
216 
217         return _currentFps;
218     }
219 
220     /**
221      * Returns the milliseconds since the application was started.
222      */
223     @nogc
224     static uint getTicks() nothrow {
225         return SDL_GetTicks();
226     }
227 
228     /**
229      * Returns the Time since the application was started.
230      */
231     @nogc
232     static Time getTime() nothrow {
233         return Time(StopWatch.getTicks());
234     }
235 
236     /**
237      * Wait for msecs milliseconds, which means that the application freeze for this time.
238      */
239     @nogc
240     static void wait(uint msecs) nothrow {
241         SDL_Delay(msecs);
242     }
243 }