PageRenderTime 54ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/src/helpers/AnimationEngine.java

https://bitbucket.org/erab6887/devgui2
Java | 429 lines | 306 code | 98 blank | 25 comment | 50 complexity | eff726397f6f13dd2afc2b451d978515 MD5 | raw file
  1. package helpers;
  2. import interfaces.Animatable;
  3. import java.awt.event.ActionEvent;
  4. import java.awt.event.ActionListener;
  5. import java.util.ArrayList;
  6. import java.util.Iterator;
  7. import javax.swing.Timer;
  8. public class AnimationEngine {
  9. private AnimationEngineTimer timer;
  10. private boolean linear = false;
  11. private boolean quad = false;
  12. private boolean powerOfN = false;
  13. private int N = 2;
  14. private boolean easeIn = false;
  15. private boolean easeOut = false;
  16. private boolean easeInOut = false;
  17. public static enum Direction {
  18. FORWARD, BACKWARD
  19. }
  20. private long startTime; //the start time of animation engine
  21. private long currentStartTime; // time of current animation cycle
  22. private long pauseStartTime;
  23. private int repeatNumTimes = 1;
  24. private boolean running = false;
  25. private boolean shouldStop = false;
  26. private boolean hasStarted = false;
  27. private int initialDelay;
  28. //the animationDuration of animation
  29. private int animationDuration;
  30. private int resolution = 20;
  31. private Direction defaultDirection = Direction.FORWARD;
  32. private Direction direction;
  33. //will hold all object that should be called by the animation engine when there's animations to be done
  34. private ArrayList<Object> animatableTargets = new ArrayList<Object>();
  35. public AnimationEngine(int duration) {
  36. this(duration,null);
  37. }
  38. /**
  39. * @param animationDuration, the animationDuration of the base animation
  40. * @param obj, the target object that will be called by the AnimationEngine when there occur events to be animated
  41. */
  42. public AnimationEngine(int duration,Object animatableTarget) {
  43. this.animationDuration = duration;
  44. this.linear = true;
  45. this.easeIn = true;
  46. addAnimatableTarget(animatableTarget);
  47. timer = new AnimationEngineTimer();
  48. }
  49. public Direction getDefaultDirection() {
  50. return defaultDirection;
  51. }
  52. public void setDefaultDirection(Direction defaultDirection) {
  53. if(isRunning()){
  54. throw new IllegalStateException("Could not set the defaultDirection if the Animation Engine is running!");
  55. }
  56. this.defaultDirection = defaultDirection;
  57. }
  58. public int getRepeatNumTimes() {
  59. return repeatNumTimes;
  60. }
  61. public void setRepeatNumTimes(int repeatNumTimes) {
  62. this.repeatNumTimes = repeatNumTimes;
  63. }
  64. public void setLinear() {
  65. this.linear = true;
  66. this.powerOfN = false;
  67. this.quad = false;
  68. }
  69. public void setQuad() {
  70. this.linear = false;
  71. this.powerOfN = false;
  72. this.quad = true;
  73. }
  74. public void setPowerOfN(int n) {
  75. this.linear = false;
  76. this.powerOfN = true;
  77. this.N = n;
  78. this.quad = false;
  79. }
  80. public float quad(float fraction) {
  81. return (float)Math.pow(fraction,2);
  82. }
  83. private float powerOfN(float fraction) {
  84. return (float)Math.pow(fraction, N);
  85. }
  86. public boolean isEaseIn() {
  87. return easeIn;
  88. }
  89. public void setEaseIn() {
  90. this.easeOut = false;
  91. this.easeInOut = false;
  92. this.easeIn = true;
  93. }
  94. public boolean isEaseOut() {
  95. return easeOut;
  96. }
  97. public void setEaseOut() {
  98. this.easeIn = false;
  99. this.easeInOut = false;
  100. this.easeOut = true;
  101. }
  102. public boolean isEaseInOut() {
  103. return easeInOut;
  104. }
  105. public void setEaseInOut() {
  106. this.easeIn = false;
  107. this.easeOut = false;
  108. this.easeInOut = true;
  109. }
  110. /**
  111. *
  112. * @param animatableTarget, the object to be added to the list that is used by the Animation engine to notify when there's a animation/event to be done.
  113. */
  114. public synchronized void addAnimatableTarget(Object animatableTarget) {
  115. if(animatableTarget != null) {
  116. if(!animatableTargets.contains(animatableTarget)) {// if the target isn't in the list, add it.
  117. if(animatableTarget instanceof Animatable)
  118. animatableTargets.add(animatableTarget);
  119. }
  120. }
  121. }
  122. /**
  123. *
  124. * @param animatableTarget, the object to be removed from the list that is used by the Animation engine to notify when there's a animation/event to be done
  125. */
  126. public synchronized void removeAnimatableTarget(Object animatableTarget) {
  127. if(animatableTarget != null) {
  128. animatableTargets.remove(animatableTarget);
  129. }
  130. }
  131. /**
  132. *
  133. * @return, true if the Animation engine is running, false otherwise
  134. */
  135. public boolean isRunning() {
  136. return running;
  137. }
  138. /**
  139. *
  140. * @return the animationDuration of the animation in milliseconds, it the animationDuration is < 0 then animations will run forever.
  141. */
  142. public int getDuration() {
  143. return animationDuration;
  144. }
  145. public void setDuration(int duration) {
  146. if(isRunning()) {
  147. throw new IllegalStateException("Could not set the animationDuration if the Animation Engine is running!");
  148. }
  149. this.animationDuration = duration;
  150. }
  151. public int getResolution() {
  152. return resolution;
  153. }
  154. public void setResolution(int resolution) {
  155. if(resolution < 0 ) {
  156. throw new IllegalArgumentException("the resoltion =" +resolution +" which is < 0 and that is not allowed");
  157. } else if(isRunning()) {
  158. throw new IllegalStateException("Could not set the resoltion if the Animation Engine is running!");
  159. }
  160. this.resolution = resolution;
  161. timer.setResolution(resolution);
  162. }
  163. public int getInitialDelay() {
  164. return initialDelay;
  165. }
  166. public void setInitialDelay(int initialDelay) {
  167. if(initialDelay < 0 ) {
  168. throw new IllegalArgumentException("the initialDelay = "+initialDelay+ " which is smaller < 0 and is not allowed!");
  169. } else if(isRunning()) {
  170. throw new IllegalStateException("Could not set the resoltion if the Animation Engine is running!");
  171. }
  172. this.initialDelay = initialDelay;
  173. timer.setInitialDelay(initialDelay);
  174. }
  175. public void start() {
  176. if(isRunning()) {
  177. throw new IllegalStateException("Could not start the Animation Engine if it is already running!");
  178. }
  179. running = true;
  180. direction = defaultDirection;
  181. startTime = (System.nanoTime() / 1000000) + getInitialDelay();
  182. currentStartTime = startTime;
  183. timer.start();
  184. }
  185. public void stop() {
  186. timer.stop();
  187. end();
  188. shouldStop = false;
  189. running = false;
  190. hasStarted = false;
  191. }
  192. public void pause() {
  193. if (isRunning()) {
  194. pauseStartTime = System.nanoTime();
  195. running = false;
  196. timer.stop();
  197. }
  198. }
  199. public void resume() {
  200. if (pauseStartTime > 0) {
  201. long pause = (System.nanoTime() - pauseStartTime) / 1000000;
  202. startTime += pause;
  203. currentStartTime += pause;
  204. timer.start();
  205. pauseStartTime = 0;
  206. running = true;
  207. }
  208. }
  209. public synchronized void begin() {
  210. Iterator<Object> iter = animatableTargets.iterator();
  211. while(iter.hasNext()){
  212. Object animatableTarget = iter.next();
  213. if(animatableTarget instanceof Animatable) {
  214. ((Animatable)animatableTarget).begin();
  215. }
  216. }
  217. }
  218. public synchronized void end() {
  219. Iterator<Object> iter = animatableTargets.iterator();
  220. while(iter.hasNext()){
  221. Object animatableTarget = iter.next();
  222. if(animatableTarget instanceof Animatable) {
  223. ((Animatable)animatableTarget).end();
  224. }
  225. }
  226. }
  227. private synchronized void repeat() {
  228. Iterator<Object> iter = animatableTargets.iterator();
  229. while(iter.hasNext()){
  230. Object animatableTarget = iter.next();
  231. if(animatableTarget instanceof Animatable) {
  232. ((Animatable)animatableTarget).repeat();
  233. }
  234. }
  235. }
  236. private synchronized void animationEvent(float fraction) {
  237. Iterator<Object> iter = animatableTargets.iterator();
  238. while(iter.hasNext()){
  239. Object animatableTarget = iter.next();
  240. if(animatableTarget instanceof Animatable) {
  241. ((Animatable)animatableTarget).animationEvent(fraction);
  242. }
  243. }
  244. if (shouldStop) {
  245. stop();
  246. }
  247. }
  248. public float getFraction() {
  249. float fraction;
  250. long currentTime = System.nanoTime() / 1000000;
  251. long animationCycleElapsedTime = currentTime - this.currentStartTime;
  252. int currentCycle =(int) ((currentTime - startTime) / animationDuration);
  253. //long animationTotElapsedTime = currentTime - startTime;
  254. //double currentCycle = (double)animationTotElapsedTime / animationDuration;
  255. if(!hasStarted) {
  256. begin();
  257. hasStarted = true;
  258. }
  259. boolean isCycle = (animationCycleElapsedTime > animationDuration);
  260. if(repeatNumTimes != 1 && (currentCycle < repeatNumTimes) && isCycle ) { // repeat
  261. long actualCycleTime = animationCycleElapsedTime % animationDuration;
  262. fraction = (float)actualCycleTime / animationDuration;
  263. p.print("repeatNumTimes"+repeatNumTimes);
  264. currentStartTime = currentTime - actualCycleTime;
  265. repeat();
  266. } else if(animationDuration >= 0 && isCycle) {
  267. if (direction == Direction.BACKWARD) {
  268. fraction = 0.0f;
  269. } else {
  270. fraction = 1.0f;
  271. }
  272. shouldStop = true;
  273. } else { // in the middle of start and end time
  274. fraction = 0.0f;
  275. if(animationDuration >= 0) {
  276. fraction = (float)animationCycleElapsedTime / animationDuration;
  277. if(direction == Direction.BACKWARD) {
  278. fraction = 1.0f-fraction ;
  279. }
  280. //make sure that the fraction is between 0 - 1
  281. fraction = Math.min(fraction, 1.0f);
  282. fraction = Math.max(fraction, 0.0f);
  283. }
  284. }
  285. if(isEaseOut()){
  286. if(quad){
  287. fraction = 1.0f - quad(1- fraction);
  288. } else if (powerOfN) {
  289. fraction = 1.0f - powerOfN((1- fraction));
  290. }
  291. } else if(isEaseInOut()) {
  292. if(quad){
  293. if (fraction <= 0.5) { // the first half of the animation)
  294. fraction = quad(2 * fraction) / 2;
  295. } else { // the second half
  296. fraction = (2 - quad(2 * (1 - fraction))) / 2;
  297. }
  298. } else if(powerOfN) {
  299. if (fraction <= 0.5) {
  300. fraction = powerOfN(2 * fraction) / 2;
  301. } else {
  302. fraction = (2 - powerOfN(2 * (1 - fraction))) / 2;
  303. }
  304. }
  305. }
  306. return fraction;
  307. }
  308. private class AnimationEngineTimer implements ActionListener {
  309. Timer timer;
  310. public AnimationEngineTimer () {
  311. timer = new Timer(resolution,this);
  312. timer.setInitialDelay(0);
  313. }
  314. public void start() {
  315. timer.start();
  316. }
  317. public void stop() {
  318. timer.stop();
  319. }
  320. public void setResolution(int resolution) {
  321. timer.setDelay(resolution);
  322. }
  323. public void setInitialDelay(int delay){
  324. timer.setInitialDelay(delay);
  325. }
  326. @Override
  327. public void actionPerformed(ActionEvent arg0) {
  328. animationEvent(getFraction());
  329. }
  330. }
  331. }