1 module animate.animate.animation; 2 3 import std.stdio; 4 import core.time; 5 6 import animate.animate.interpolator; 7 8 immutable int INFINITE = -1; 9 10 enum RepeatMode 11 { 12 REPEAT, 13 REVERSE 14 } 15 16 enum AnimationSetMode 17 { 18 PARALLEL, 19 SEQUENTIAL 20 } 21 22 interface Animatable 23 { 24 protected void updateProgress(double progress); 25 public void update(Duration deltaTime); 26 public bool isRunning(); 27 } 28 29 interface UpdateListener 30 { 31 void onAnimationEnd(); 32 void onAnimationRepeat(); 33 } 34 35 class Animation : Animatable 36 { 37 private 38 { 39 Duration m_duration; 40 Duration m_progress; 41 bool m_isRunning; 42 43 Interpolator m_interpolator; 44 45 RepeatMode m_repeatMode; 46 int m_repeatCount; 47 int m_currentRunCount; 48 49 bool m_isReverse; 50 51 UpdateListener[] m_listeners; 52 } 53 54 this(Duration duration) 55 { 56 m_duration = duration; 57 m_interpolator = new LinearInterpolator(); 58 m_isRunning = true; 59 60 m_repeatMode = RepeatMode.REPEAT; 61 m_isReverse = false; 62 } 63 64 /// This is called with the value (0-1) of the 65 /// amount that this animation has completed by. TODO: word better 66 protected abstract void updateProgress(double progress); 67 68 /// This takes the delta time since the last update call as the input. 69 final void update(Duration deltaTime) 70 { 71 if(m_isRunning) 72 { 73 m_progress += deltaTime; 74 double progress = cast(double)(m_progress.total!"usecs") / m_duration.total!"usecs"; 75 76 if(progress >= 1.0) 77 { 78 if(m_repeatCount != 0) 79 { 80 while(progress >= 1.0) 81 { 82 progress -= 1.0; 83 ++m_currentRunCount; 84 } 85 86 //Check that we are still in a valid animation frame... 87 if(m_repeatCount > 0 && m_repeatCount < m_currentRunCount) 88 { 89 //We have run out of animation frames, so just leave this at the end animation... 90 final switch(m_repeatMode) 91 { 92 case RepeatMode.REPEAT: 93 progress = 1.0; 94 break; 95 case RepeatMode.REVERSE: 96 m_isReverse = m_repeatCount % 2 == 0; 97 m_progress = usecs(m_duration.total!"usecs"); 98 break; 99 } 100 m_isRunning = false; 101 sendOnAnimationEnd(); 102 } 103 else 104 { 105 //We ARE in a valid animation frame, so update the status accordingly 106 final switch(m_repeatMode) 107 { 108 case RepeatMode.REPEAT: 109 m_progress = usecs(m_progress.total!"usecs" % m_duration.total!"usecs"); 110 break; 111 case RepeatMode.REVERSE: 112 m_isReverse = m_currentRunCount % 2 == 1; 113 m_progress = msecs(m_progress.total!"usecs" % m_duration.total!"usecs"); 114 progress = cast(double)(m_progress.total!"usecs") / m_duration.total!"usecs"; 115 break; 116 } 117 118 sendOnAnimationRepeat(); 119 } 120 } 121 else 122 { 123 progress = 1.0; 124 m_isRunning = false; 125 sendOnAnimationEnd(); 126 } 127 } 128 129 progress = m_isReverse ? 1.0 - progress : progress; 130 131 // interpolate the current progress value 132 progress = m_interpolator.interpolate(progress); 133 134 // send the progress update call to this animation 135 updateProgress(progress); 136 } 137 } 138 139 void addUpdateListener(UpdateListener listener) 140 { 141 m_listeners ~= listener; 142 } 143 144 void sendOnAnimationEnd() 145 { 146 foreach(listener; m_listeners) 147 { 148 listener.onAnimationEnd(); 149 } 150 } 151 152 void sendOnAnimationRepeat() 153 { 154 foreach(listener; m_listeners) 155 { 156 listener.onAnimationRepeat(); 157 } 158 } 159 160 void setInterpolator(Interpolator interpolator) 161 { 162 if(interpolator) 163 { 164 m_interpolator = interpolator; 165 } 166 else if(!m_interpolator) 167 { 168 m_interpolator = new LinearInterpolator(); 169 } 170 } 171 172 final bool isRunning() 173 { 174 return m_isRunning; 175 } 176 177 /// This determines the style of our animation repeat 178 @property 179 { 180 RepeatMode repeatMode(RepeatMode mode) 181 { 182 m_repeatMode = mode; 183 return m_repeatMode; 184 } 185 186 RepeatMode repeatMode() 187 { 188 return m_repeatMode; 189 } 190 } 191 192 /// If the repeat count is negative, then we repeat infinitely. 193 /// Otherwise, we run the animation repeatCount number of times. 194 @property 195 { 196 int repeatCount(int count) 197 { 198 m_repeatCount = count; 199 return m_repeatCount; 200 } 201 202 int repeatCount() 203 { 204 return m_repeatCount; 205 } 206 } 207 } 208 209 class ValueAnimation(T) : Animation 210 if(isNumeric(T)) 211 { 212 private 213 { 214 T m_startVal; 215 T m_difference; 216 T* m_target; 217 } 218 219 this(Duration duration, T* target, T start, T end) 220 { 221 super(duration); 222 m_startVal = start; 223 m_difference = end - start; 224 } 225 226 override protected void updateProgress(double progress) 227 { 228 *m_target = m_startVal + (temp * progress); 229 } 230 231 } 232 233 class DelegateAnimation : Animation 234 { 235 private 236 { 237 void delegate(double) m_update; 238 } 239 240 this(Duration duration, void delegate(double) update) 241 { 242 super(duration); 243 m_update = update; 244 } 245 246 override protected void updateProgress(double progress) 247 { 248 m_update(progress); 249 } 250 251 } 252 253 ///For now, all this class does is run a bunch of animations simultaneously. 254 class AnimationSet : Animatable 255 { 256 private 257 { 258 Animation[] m_anims; 259 int m_currentAnim; 260 261 AnimationSetMode m_mode; 262 bool m_isRunning; 263 } 264 265 this(Animation[] anims...) 266 { 267 m_anims = anims.dup; 268 m_mode = AnimationSetMode.PARALLEL; 269 m_isRunning = true; 270 } 271 272 void setMode(AnimationSetMode mode) 273 { 274 m_mode = mode; 275 } 276 277 //Does nothing because this doesn't need it... 278 final void updateProgress(double progress) {}; 279 280 final void update(Duration deltaT) 281 { 282 if(m_isRunning) 283 { 284 final switch(m_mode) 285 { 286 case AnimationSetMode.PARALLEL: 287 m_isRunning = false; 288 foreach(anim; m_anims) 289 { 290 anim.update(deltaT); 291 m_isRunning = anim.isRunning() || m_isRunning; 292 } 293 break; 294 case AnimationSetMode.SEQUENTIAL: 295 m_anims[m_currentAnim].update(deltaT); 296 if(!m_anims[m_currentAnim].isRunning()) 297 { 298 m_currentAnim++; 299 m_isRunning = m_currentAnim < m_anims.length; 300 } 301 break; 302 } 303 } 304 } 305 306 final bool isRunning() 307 { 308 return m_isRunning; 309 } 310 }