animation_player.nim 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. # author: Ethosa
  2. ## Provides animation in most nodes
  3. import
  4. node,
  5. ../core/enums,
  6. ../core/tools,
  7. ../thirdparty/opengl
  8. type
  9. AnimationMode* {.pure, size: sizeof(int8).} = enum
  10. ANIMATION_NORMAL,
  11. ANIMATION_EASE,
  12. ANIMATION_BEZIER
  13. AnimationObject* = object
  14. states: seq[tuple[tick: int, value: float]]
  15. obj: ptr float
  16. AnimationPlayerObj* {.final.} = object of NodeObj
  17. objects*: seq[AnimationObject]
  18. duration*: int64
  19. tick*: int64
  20. is_played*: bool
  21. loop*: bool
  22. bezier*: tuple[p0, p1: float]
  23. mode*: AnimationMode
  24. AnimationPlayerRef* = ref AnimationPlayerObj
  25. proc AnimationPlayer*(name: string = "AnimationPlayer"): AnimationPlayerRef =
  26. runnableExamples:
  27. var animplayer = AnimationPlayer("AnimationPlayer")
  28. nodepattern(AnimationPlayerRef)
  29. result.objects = @[]
  30. result.bezier = (0.25, 0.75)
  31. result.duration = 180
  32. result.tick = 0
  33. result.is_played = false
  34. result.loop = true
  35. result.kind = ANIMATION_PLAYER_NODE
  36. result.type_of_node = NODE_TYPE_DEFAULT
  37. result.mode = ANIMATION_NORMAL
  38. proc ease(self: AnimationPlayerRef, states: seq[tuple[tick: int, value: float]]): float =
  39. var time = (self.tick - states[0].tick).float / ((states[1].tick - states[0].tick).float / 2.0)
  40. let diff = states[1].value - states[0].value
  41. if time < 1:
  42. return diff / 2 * time * time + states[0].value
  43. time -= 1
  44. return -diff / 2 * (time * (time - 2) - 1) + states[0].value
  45. proc bezier(self: AnimationPlayerRef, states: seq[tuple[tick: int, value: float]], current: float): float =
  46. let
  47. step = 1f / (states[1].tick - states[0].tick).float
  48. t = step * (self.tick - states[0].tick).float
  49. diff = states[1].value - states[0].value
  50. result = cubic_bezier(t, 0.0, self.bezier[0], self.bezier[1], 1.0)
  51. result = states[0].value + diff*result
  52. method addState*(self: AnimationPlayerRef, obj: ptr float, states: seq[tuple[tick: int, value: float]]) {.base.} =
  53. ## Adds a new state to AnimationPlayer.
  54. self.objects.add(AnimationObject(
  55. states: states,
  56. obj: obj
  57. ))
  58. method draw*(self: AnimationPlayerRef, w: GLfloat, h: GLfloat) =
  59. ## This uses in the `window.nim`.
  60. if self.is_played:
  61. if self.tick > self.duration:
  62. self.tick = 0
  63. if not self.loop:
  64. self.is_played = false
  65. return
  66. var
  67. current_states: seq[tuple[tick: int, value: float]] = @[]
  68. current: ptr float
  69. for obj in self.objects:
  70. current = obj.obj
  71. for i in countdown(obj.states.high, 0):
  72. if self.tick >= obj.states[i].tick:
  73. current_states.add(obj.states[i])
  74. if self.tick == obj.states[i].tick:
  75. current[] = obj.states[i].value
  76. break
  77. if current_states.len() == 1:
  78. for i in 0..obj.states.high:
  79. if current_states[0].tick < obj.states[i].tick:
  80. current_states.add(obj.states[i])
  81. break
  82. if current_states.len() == 2:
  83. if self.mode == ANIMATION_NORMAL:
  84. let
  85. diff_time: float = (current_states[1].tick - current_states[0].tick).float
  86. diff: float = current_states[1].value - current_states[0].value
  87. current[] = current_states[0].value + (self.tick - current_states[0].tick).float/diff_time * diff
  88. elif self.mode == ANIMATION_EASE:
  89. current[] = self.ease(current_states)
  90. elif self.mode == ANIMATION_BEZIER:
  91. current[] = bezier(self, current_states, current[])
  92. current_states = @[]
  93. self.tick += 1
  94. method play*(self: AnimationPlayerRef) {.base.} =
  95. ## Plays animation.
  96. self.is_played = true
  97. method setDuration*(self: AnimationPlayerRef, duration: int) {.base.} =
  98. ## Changes animation duration.
  99. self.duration = duration
  100. method stop*(self: AnimationPlayerRef) {.base.} =
  101. ## Stops animation.
  102. self.is_played = false