1 /**
2  * Defines methods for scheduling tasks with different conditions for executing.
3  *
4  */
5 module dash.utility.tasks;
6 import dash.utility.time, dash.utility.output, dash.utility.math;
7 
8 import core.time;
9 import std.algorithm: min;
10 import std.parallelism: parallel;
11 import std.uuid: UUID, randomUUID;
12 import std.typecons;
13 
14 public:
15 /**
16  * Schedule a task to be executed until it returns true.
17  *
18  * Params:
19  *  dg =                The task to execute.
20  *
21  * Returns: The ID of the task.
22  */
23 UUID scheduleTask( bool delegate() dg )
24 {
25     auto id = randomUUID();
26     scheduledTasks ~= tuple( dg, id );
27     return id;
28 }
29 
30 /**
31  * Schedule a task to interpolate a value over a period of time.
32  *
33  * Params:
34  *  T =                 The type to interpolate, either vector or quaternion.
35  *  val =               [ref] The value to interpolate.
36  *  start =             The starting value for interpolation.
37  *  end =               The target value for interpolation.
38  *  interpFunc =        [default=lerp] The function to use for interpolation.
39  *
40  * Returns: The ID of the task.
41  *
42  * Example:
43  * ---
44  * scheduleInterpolateTask( position, startNode, endNode, 100.msecs );
45  * ---
46  */
47 UUID scheduleInterpolateTask(T)( ref T val, T start, T end, Duration duration, T function( T, T, float ) interpFunc = &lerp!( T ) )// if( is_vector!T || is_quaternion!T )
48 {
49     return scheduleTimedTask( duration, ( elapsed )
50     {
51         val = interpFunc( start, end, elapsed / duration.toSeconds );
52     } );
53 }
54 ///
55 unittest
56 {
57     import std.stdio;
58 
59     writeln( "Dash Tasks scheduleInterpolateTask unittest 1" );
60 
61     vec3f interpVec = vec3f( 0, 0, 0 );
62     vec3f start = vec3f( 0, 1, 0 );
63     vec3f end = vec3f( 0, 1, 1 );
64     scheduleInterpolateTask( interpVec, start, end, 100.msecs );
65 
66     while( scheduledTasks.length )
67     {
68         Time.update();
69         executeTasks();
70     }
71 
72     assert( interpVec == end );
73 }
74 
75 /**
76  * Schedule a task to interpolate a property over a period of time.
77  *
78  * Params:
79  *  prop =              The name of the property being interpolated.
80  *  T =                 The type to interpolate, either vector or quaternion.
81  *  Owner =             The type that owns the property interpolating.
82  *  own =               [ref] The owner of the property.
83  *  start =             The starting value for interpolation.
84  *  end =               The target value for interpolation.
85  *  interpFunc =        [default=lerp] The function to use for interpolation.
86  *
87  * Returns: The ID of the task.
88  *
89  * Example:
90  * ---
91  * scheduleInterpolateTask!q{position}( transform, startNode, endNode, 100.msecs );
92  * ---
93  */
94 UUID scheduleInterpolateTask( string prop, T, Owner )( ref Owner own, T start, T end, Duration duration, T function( T, T, float ) interpFunc = &lerp!( T ) )
95     if( __traits( compiles, mixin( "own." ~ prop ) ) )
96 {
97     auto startTime = Time.totalTime;
98     return scheduleTimedTask( duration, ( elapsed )
99     {
100         mixin( "own." ~ prop ~ " = interpFunc( start, end, elapsed / duration.toSeconds );" );
101     } );
102 }
103 ///
104 unittest
105 {
106     import std.stdio;
107 
108     writeln( "Dash Tasks scheduleInterpolateTask unittest 2" );
109 
110     auto testClass = new TestPropertyInterpolate;
111     testClass.vector = vec3f( 0, 0, 0 );
112     vec3f start = vec3f( 0, 1, 0 );
113     vec3f end = vec3f( 0, 1, 1 );
114     scheduleInterpolateTask!q{vector}( testClass, start, end, 100.msecs );
115 
116     while( scheduledTasks.length )
117     {
118         executeTasks();
119         Time.update();
120     }
121 
122     assert( testClass.vector == end );
123 }
124 version( unittest )
125 class TestPropertyInterpolate
126 {
127     import dash.utility.math;
128 
129     private vec3f _vector;
130     public @property vec3f vector() { return _vector; }
131     public @property void vector( vec3f newVal ) { _vector = newVal; }
132 }
133 
134 /**
135  * Schedule a task to be executed until the duration expires.
136  *
137  * Params:
138  *  duration =          The duration to execute the task for.
139  *  dg =                The task to execute.
140  *
141  * Returns: The ID of the task.
142  */
143 UUID scheduleTimedTask( Duration duration, void delegate() dg )
144 {
145     auto startTime = Time.totalTime;
146     return scheduleTask( {
147         dg();
148         return Time.totalTime >= startTime + duration.toSeconds;
149     } );
150 }
151 
152 /// ditto
153 UUID scheduleTimedTask( Duration duration, void delegate( float ) dg )
154 {
155     auto startTime = Time.totalTime;
156     return scheduleTask( {
157         dg( min( Time.totalTime - startTime, duration.toSeconds ) );
158         return Time.totalTime >= startTime + duration.toSeconds;
159     } );
160 }
161 
162 /// ditto
163 UUID scheduleTimedTask( Duration duration, void delegate( float, float ) dg )
164 {
165     auto startTime = Time.totalTime;
166     return scheduleTask( {
167         dg( min( Time.totalTime - startTime, duration.toSeconds ), duration.toSeconds );
168         return Time.totalTime >= startTime + duration.toSeconds;
169     } );
170 }
171 
172 /// ditto
173 UUID scheduleTimedTask( Duration duration, bool delegate() dg )
174 {
175     auto startTime = Time.totalTime;
176     return scheduleTask( {
177         if( dg() )
178             return true;
179         else
180             return Time.totalTime >= startTime + duration.toSeconds;
181     } );
182 }
183 
184 /// ditto
185 UUID scheduleTimedTask( Duration duration, bool delegate( float ) dg )
186 {
187     auto startTime = Time.totalTime;
188     return scheduleTask( {
189         if( dg( min( Time.totalTime - startTime, duration.toSeconds ) ) )
190             return true;
191         else
192             return Time.totalTime >= startTime + duration.toSeconds;
193     } );
194 }
195 
196 /// ditto
197 UUID scheduleTimedTask( Duration duration, bool delegate( float, float ) dg )
198 {
199     auto startTime = Time.totalTime;
200     return scheduleTask( {
201         if( dg( min( Time.totalTime - startTime, duration.toSeconds ), duration.toSeconds ) )
202             return true;
203         else
204             return Time.totalTime >= startTime + duration.toSeconds;
205     } );
206 }
207 
208 /**
209  * Schedule a task to be execuated after the specified amount of time.
210  *
211  * Params:
212  *  delay =             The ammount of time to wait before executing.
213  *  dg =                The task to execute.
214  *
215  * Returns: The ID of the task.
216  */
217 UUID scheduleDelayedTask( Duration delay, void delegate() dg )
218 {
219     auto startTime = Time.totalTime;
220     return scheduleTask( {
221         if( Time.totalTime - startTime >= delay.toSeconds )
222         {
223             dg();
224             return true;
225         }
226         else
227         {
228             return false;
229         }
230     } );
231 }
232 
233 /**
234  * Schedule a task to be executed on an interval, until the task returns true.
235  *
236  * Params:
237  *  interval =          The interval on which to call this task.
238  *  dg =                The task to execute.
239  *
240  * Returns: The ID of the task.
241  */
242 UUID scheduleIntervaledTask( Duration interval, bool delegate() dg )
243 {
244     auto timeTilExe = interval.toSeconds;
245     return scheduleTask( {
246         timeTilExe -= Time.deltaTime;
247         if( timeTilExe <= 0 )
248         {
249             if( dg() )
250                 return true;
251 
252             timeTilExe = interval.toSeconds;
253         }
254 
255         return false;
256     } );
257 }
258 
259 /**
260  * Schedule a task to be executed on an interval a given number of times.
261  *
262  * Params:
263  *  interval =          The interval on which to call this task.
264  *  numExecutions =     The number of time to execute the task.
265  *  dg =                The task to execute.
266  *
267  * Returns: The ID of the task.
268  */
269 UUID scheduleIntervaledTask( Duration interval, uint numExecutions, void delegate() dg )
270 {
271     auto timeTilExe = interval.toSeconds;
272     uint executedTimes = 0;
273     return scheduleTask( {
274         timeTilExe -= Time.deltaTime;
275         if( timeTilExe <= 0 )
276         {
277             dg();
278 
279             ++executedTimes;
280             timeTilExe = interval.toSeconds;
281         }
282 
283         return executedTimes == numExecutions;
284     } );
285 }
286 
287 /**
288  * Schedule a task to be executed on an interval a given number of times, or until the event returns true.
289  *
290  * Params:
291  *  interval =          The interval on which to call this task.
292  *  numExecutions =     The number of time to execute the task.
293  *  dg =                The task to execute.
294  *
295  * Returns: The ID of the task.
296  */
297 UUID scheduleIntervaledTask( Duration interval, uint numExecutions, bool delegate() dg )
298 {
299     auto timeTilExe = interval.toSeconds;
300     uint executedTimes = 0;
301     return scheduleTask( {
302         timeTilExe -= Time.deltaTime;
303         if( timeTilExe <= 0 )
304         {
305             if( dg() )
306                 return true;
307 
308             ++executedTimes;
309             timeTilExe = interval.toSeconds;
310         }
311 
312         return executedTimes == numExecutions;
313     } );
314 }
315 
316 /**
317  * Executes all scheduled tasks.
318  */
319 void executeTasks()
320 {
321     size_t[] toRemove;
322     foreach( i, task; scheduledTasks/*.parallel*/ )
323     {
324         if( task[ 0 ]() )
325             synchronized toRemove ~= i;
326     }
327     foreach_reverse( i; toRemove )
328     {
329         // Get tasks after one being removed
330         auto end = scheduledTasks[ i+1..$ ];
331         // Get tasks before one being removed
332         scheduledTasks = scheduledTasks[ 0..i ];
333         // Add end back
334         scheduledTasks ~= end;
335     }
336 }
337 
338 /**
339  * Cancels the given task from executing.
340  *
341  * Params:
342  *  id =				The id of the task to cancel.
343  */
344 void cancelTask( UUID id )
345 {
346     import std.algorithm;
347 
348     auto i = scheduledTasks.countUntil!( tup => tup[ 1 ] == id );
349 
350     // Get tasks after one being removed.s
351     auto end = scheduledTasks[ i+1..$ ];
352     // Get tasks before one being removed.
353     scheduledTasks = scheduledTasks[ 0..i ];
354     // Add end back.
355     scheduledTasks ~= end;
356 }
357 
358 /**
359  * Cancels all running tasks.
360  */
361 void resetTasks()
362 {
363     scheduledTasks = [];
364 }
365 
366 private:
367 /// The tasks that have been scheduled
368 Tuple!( bool delegate(), UUID )[] scheduledTasks;