animation.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. this.animationTime_set = function (value) {
  2. if (this.animationStartTime == null) {
  3. this.animationStartTime = 0;
  4. }
  5. if (this.animationStopTime == null) {
  6. this.animationStopTime = this.animationDuration;
  7. }
  8. // Save copies to avoid repeated reads.
  9. var duration = this.animationStopTime - this.animationStartTime,
  10. rate = this.animationRate;
  11. // Range limit the incoming value.
  12. value = Math.min(Math.max(this.animationStartTime, value), this.animationStopTime);
  13. // Keep paused if updating start/pause from null/null. Use t=0 instead of `this.time` so that
  14. // setting `node.animationTime` during initialization is consistent across multiple clients.
  15. if (this.animationStartSIM == null) {
  16. this.animationPauseSIM = 0;
  17. }
  18. // Calculate the start and stop times that makes the new time work.
  19. this.animationStartSIM =
  20. (this.animationPauseSIM != null ? this.animationPauseSIM : this.time) -
  21. (rate >= 0 ? value - this.animationStartTime : value - duration) / rate;
  22. this.animationStopSIM =
  23. this.animationStartSIM +
  24. (rate >= 0 ? duration : -duration) / rate;
  25. // Update the node and fire the changed event.
  26. if (value !== this.animationTimeUpdated) {
  27. this.animationTimeUpdated = value;
  28. this.animationUpdate(value, this.animationDuration);
  29. this.animationTimeChanged(value);
  30. } //@ sourceURL=animation.animationTime.set.vwf
  31. }
  32. this.animationTime_get = function () {
  33. // Save copies to avoid repeated reads.
  34. var startTime = this.animationStartTime;
  35. var stopTime = this.animationStopTime;
  36. var rate = this.animationRate;
  37. var animationPauseSIM = this.animationPauseSIM;
  38. var animationStartSIM = this.animationStartSIM;
  39. var time = this.time;
  40. // Calculate the time from the start and current/pause times.
  41. var value = (
  42. (animationPauseSIM != null ? animationPauseSIM : time) -
  43. (animationStartSIM != null ? animationStartSIM : time)
  44. ) * rate + (rate >= 0 ? startTime : stopTime);
  45. // Range limit the value.
  46. value = Math.min(Math.max(startTime, value), stopTime);
  47. // If changed since last seen, update and fire the changed event.
  48. if (value !== this.animationTimeUpdated) {
  49. this.animationTimeUpdated = value;
  50. this.animationUpdate(value, this.animationDuration);
  51. this.animationTimeChanged(value);
  52. }
  53. return value; //@ sourceURL=animation.animationTime.get.vwf
  54. }
  55. this.animationDuration_set = function (value) {
  56. var duration = value, rate = this.animationRate;
  57. this.animationDuration = duration;
  58. this.animationDurationSIM = (rate >= 0 ? duration : -duration) / rate;
  59. }
  60. this.animationRate_set = function (value) {
  61. var duration = this.animationDuration, rate = value;
  62. this.animationRate = rate;
  63. this.animationDurationSIM = (rate >= 0 ? duration : -duration) / rate;
  64. }
  65. this.animationPlaying_set = function (value) {
  66. if (this.animationStartTime == null) {
  67. this.animationStartTime = 0;
  68. }
  69. if (this.animationStopTime == null) {
  70. this.animationStopTime = this.animationDuration;
  71. }
  72. if (this.animationStartSIM != null && this.animationPauseSIM == null) {
  73. if (!value) {
  74. // Mark as paused at the current time.
  75. this.animationPauseSIM = this.time;
  76. // Send the `animationStopped` event if stopping at the end.
  77. if (this.time == this.animationStopSIM) {
  78. this.animationStopped();
  79. }
  80. }
  81. } else {
  82. if (value) {
  83. // Save copies to avoid repeated reads.
  84. var duration = this.animationStopTime - this.animationStartTime,
  85. rate = this.animationRate;
  86. // Start from the beginning if resuming from the end.
  87. if (this.animationPauseSIM == this.animationStopSIM) {
  88. this.animationPauseSIM = this.animationStartSIM;
  89. }
  90. // Recalculate the start and stop times to keep paused time unchanged, then resume.
  91. this.animationStartSIM =
  92. (this.animationStartSIM != null ? this.animationStartSIM : this.time) -
  93. (this.animationPauseSIM != null ? this.animationPauseSIM : this.time) +
  94. this.time;
  95. this.animationStopSIM =
  96. this.animationStartSIM +
  97. (rate >= 0 ? duration : -duration) / rate;
  98. this.animationPauseSIM = null;
  99. // Send the `animationStarted` event if starting from the beginning.
  100. if (this.time == this.animationStartSIM) {
  101. this.animationStarted();
  102. }
  103. // Start the animation worker.
  104. this.logger.debug("scheduling animationTick");
  105. this.animationTick();
  106. }
  107. } //@ sourceURL=animation.animationPlaying.set.vwf
  108. }
  109. this.animationPlaying_get = function () {
  110. return this.animationStartSIM != null && this.animationPauseSIM == null;
  111. }
  112. this.animationStartTime_set = function (value) {
  113. this.animationStartTime = value ? Math.min(Math.max(0, value), this.animationDuration) : value;
  114. }
  115. this.animationStopTime_set = function (value) {
  116. this.animationStopTime = value ? Math.min(Math.max(0, value), this.animationDuration) : value;
  117. }
  118. this.animationStartFrame_set = function (value) {
  119. this.animationStartTime = value / this.animationFPS
  120. }
  121. this.animationStartFrame_get = function () {
  122. return Math.floor(this.animationStartTime * this.animationFPS)
  123. }
  124. this.animationStopFrame_set = function (value) {
  125. this.animationStopTime = value / this.animationFPS
  126. }
  127. this.animationStopFrame_get = function () {
  128. return Math.floor(this.animationStopTime * this.animationFPS)
  129. }
  130. this.animationFrames_set = function (value) {
  131. this.animationDuration = value / this.animationFPS
  132. }
  133. this.animationFrames_get = function () {
  134. return Math.ceil(this.animationFPS * this.animationDuration)
  135. }
  136. this.animationFrame_set = function (value) {
  137. if (this.animationFPS) {
  138. this.animationTime = value / this.animationFPS;
  139. }
  140. }
  141. this.animationFrame_get = function () {
  142. if (this.animationFPS) {
  143. return Math.floor(this.animationTime * this.animationFPS);
  144. }
  145. }
  146. //methods
  147. this.animationPlay = function (startTime, stopTime) {
  148. if (!isNaN(stopTime)) {
  149. this.animationStopTime = stopTime;
  150. }
  151. if (!isNaN(startTime)) {
  152. this.animationStartTime = startTime;
  153. }
  154. this.animationPlaying = true;
  155. }
  156. this.animationPause = function () {
  157. this.animationPlaying = false;
  158. }
  159. this.animationResume = function () {
  160. this.animationPlaying = true;
  161. }
  162. this.animationStop = function () {
  163. this.animationPlaying = false;
  164. this.animationTime = 0;
  165. }
  166. this.animationTick = function () {
  167. if (this.animationPlaying) {
  168. // Read the time to recognize the current time and update.
  169. // TODO: move side effects out of the getter!!! (says Kevin)
  170. this.animationTime;
  171. // Loop or stop after reaching the end.
  172. if (this.time === this.animationStopSIM) {
  173. if (this.animationLoop) {
  174. this.animationLooped();
  175. this.animationTime = this.animationRate >= 0 ?
  176. this.animationStartTime : this.animationStopTime;
  177. } else {
  178. this.animationPlaying = false;
  179. }
  180. }
  181. // Schedule the next tick if still playing.
  182. if (this.animationPlaying) {
  183. if (this.animationStopSIM - this.time > 1 / this.animationTPS) {
  184. this.in(1 / this.animationTPS).animationTick(); // next interval
  185. } else {
  186. // TODO: When animationStopSIM is 0 (usually when a model does not actually have an
  187. // animation on it), we schedule a method call for a time in the past (at time 0).
  188. // That immediately calls animationTick again, but this.time does not equal
  189. // animationStopSIM as we would expect. So, it doesn't stop the animation and we get
  190. // caught in an infinite loop.
  191. // Ideally we should catch the case where animationStopSIM is 0 before this point.
  192. // But for now, we catch it here.
  193. if (this.animationStopSIM > 0) {
  194. this.at(this.animationStopSIM).animationTick(); // exactly at end
  195. } else {
  196. this.animationPlaying = false;
  197. }
  198. }
  199. } else {
  200. this.logger.debug("canceling animationTick");
  201. }
  202. } //@ sourceURL=animation.animationTick.vwf
  203. }
  204. this.animationUpdate = function (time, duration) {
  205. //console.log("Do on animation update")
  206. }
  207. this.initialize = function () {
  208. // Locate child nodes that extend or implement "http://vwf.example.com/animation/position.vwf"
  209. // to identify themselves as animation key positions.
  210. var positions = this.find("./element(*,'proxy/animation/position.vwf')");
  211. // Fill in missing `animationTime` properties, distributing evenly between the left and right
  212. // positions that define `animationTime`.
  213. // 1: [ - ] => [ 0 ]
  214. // 1: [ 0, - ] => [ 0, 1 ]
  215. // 1: [ -, 1 ] => [ 0, 1 ]
  216. // 1: [ 0, -, - ] => [ 0, 1/2, 1 ]
  217. // 1: [ -, -, 1 ] => [ 0, 1/2, 1 ]
  218. // 1: [ 0, - , -, 1 ] => [ 0, 1/3 , 2/3, 1 ]
  219. var leftTime, leftIndex;
  220. var rightTime, rightIndex = -Infinity;
  221. if (positions.length > 0) {
  222. positions.sort(function (a, b) {
  223. return a.sequence - b.sequence;
  224. });
  225. if (positions[0].animationTime === null) {
  226. positions[0].animationTime = 0;
  227. }
  228. if (positions[positions.length - 1].animationTime === null) {
  229. positions[positions.length - 1].animationTime = this.animationDuration;
  230. }
  231. positions.forEach(function (position, index) {
  232. if (position.animationTime !== null) {
  233. leftTime = position.animationTime;
  234. leftIndex = index;
  235. } else {
  236. if (index > rightIndex) {
  237. for (rightIndex = index + 1; rightIndex < positions.length; rightIndex++) {
  238. if ((rightTime = /* assignment! */ positions[rightIndex].animationTime) !== null) {
  239. break;
  240. }
  241. }
  242. }
  243. position.animationTime = leftTime + (rightTime - leftTime) *
  244. (index - leftIndex) / (rightIndex - leftIndex);
  245. }
  246. }, this);
  247. }
  248. } //@ sourceURL=http://vwf.example.com/animation.vwf/scripts~initialize