animation_player.nim 3.7 KB

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