Speed.lua 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. local Element = require('elements/Element')
  2. ---@alias Dragging { start_time: number; start_x: number; distance: number; speed_distance: number; start_speed: number; }
  3. ---@class Speed : Element
  4. local Speed = class(Element)
  5. ---@param props? ElementProps
  6. function Speed:new(props) return Class.new(self, props) --[[@as Speed]] end
  7. function Speed:init(props)
  8. Element.init(self, 'speed', props)
  9. self.width = 0
  10. self.height = 0
  11. self.notches = 10
  12. self.notch_every = 0.1
  13. ---@type number
  14. self.notch_spacing = nil
  15. ---@type number
  16. self.font_size = nil
  17. ---@type Dragging|nil
  18. self.dragging = nil
  19. end
  20. function Speed:on_coordinates()
  21. self.height, self.width = self.by - self.ay, self.bx - self.ax
  22. self.notch_spacing = self.width / (self.notches + 1)
  23. self.font_size = round(self.height * 0.48 * options.font_scale)
  24. end
  25. function Speed:on_options() self:on_coordinates() end
  26. function Speed:speed_step(speed, up)
  27. if options.speed_step_is_factor then
  28. if up then
  29. return speed * options.speed_step
  30. else
  31. return speed * 1 / options.speed_step
  32. end
  33. else
  34. if up then
  35. return speed + options.speed_step
  36. else
  37. return speed - options.speed_step
  38. end
  39. end
  40. end
  41. function Speed:handle_cursor_down()
  42. self:tween_stop() -- Stop and cleanup possible ongoing animations
  43. self.dragging = {
  44. start_time = mp.get_time(),
  45. start_x = cursor.x,
  46. distance = 0,
  47. speed_distance = 0,
  48. start_speed = state.speed,
  49. }
  50. end
  51. function Speed:on_global_mouse_move()
  52. if not self.dragging then return end
  53. self.dragging.distance = cursor.x - self.dragging.start_x
  54. self.dragging.speed_distance = (-self.dragging.distance / self.notch_spacing * self.notch_every)
  55. local speed_current = state.speed
  56. local speed_drag_current = self.dragging.start_speed + self.dragging.speed_distance
  57. speed_drag_current = clamp(0.01, speed_drag_current, 100)
  58. local drag_dir_up = speed_drag_current > speed_current
  59. local speed_step_next = speed_current
  60. local speed_drag_diff = math.abs(speed_drag_current - speed_current)
  61. while math.abs(speed_step_next - speed_current) < speed_drag_diff do
  62. speed_step_next = self:speed_step(speed_step_next, drag_dir_up)
  63. end
  64. local speed_step_prev = self:speed_step(speed_step_next, not drag_dir_up)
  65. local speed_new = speed_step_prev
  66. local speed_next_diff = math.abs(speed_drag_current - speed_step_next)
  67. local speed_prev_diff = math.abs(speed_drag_current - speed_step_prev)
  68. if speed_next_diff < speed_prev_diff then
  69. speed_new = speed_step_next
  70. end
  71. if speed_new ~= speed_current then
  72. mp.set_property_native('speed', speed_new)
  73. end
  74. end
  75. function Speed:handle_cursor_up()
  76. self.dragging = nil
  77. request_render()
  78. end
  79. function Speed:on_global_mouse_leave()
  80. self.dragging = nil
  81. request_render()
  82. end
  83. function Speed:handle_wheel_up() mp.set_property_native('speed', self:speed_step(state.speed, true)) end
  84. function Speed:handle_wheel_down() mp.set_property_native('speed', self:speed_step(state.speed, false)) end
  85. function Speed:render()
  86. local visibility = self:get_visibility()
  87. local opacity = self.dragging and 1 or visibility
  88. if opacity <= 0 then return end
  89. cursor:zone('primary_down', self, function()
  90. self:handle_cursor_down()
  91. cursor:once('primary_up', function() self:handle_cursor_up() end)
  92. end)
  93. cursor:zone('secondary_click', self, function() mp.set_property_native('speed', 1) end)
  94. cursor:zone('wheel_down', self, function() self:handle_wheel_down() end)
  95. cursor:zone('wheel_up', self, function() self:handle_wheel_up() end)
  96. local ass = assdraw.ass_new()
  97. -- Background
  98. ass:rect(self.ax, self.ay, self.bx, self.by, {
  99. color = bg, radius = state.radius, opacity = opacity * config.opacity.speed,
  100. })
  101. -- Coordinates
  102. local ax, ay = self.ax, self.ay
  103. local bx, by = self.bx, ay + self.height
  104. local half_width = (self.width / 2)
  105. local half_x = ax + half_width
  106. -- Notches
  107. local speed_at_center = state.speed
  108. if self.dragging then
  109. speed_at_center = self.dragging.start_speed + self.dragging.speed_distance
  110. speed_at_center = clamp(0.01, speed_at_center, 100)
  111. end
  112. local nearest_notch_speed = round(speed_at_center / self.notch_every) * self.notch_every
  113. local nearest_notch_x = half_x + (((nearest_notch_speed - speed_at_center) / self.notch_every) * self.notch_spacing)
  114. local guide_size = math.floor(self.height / 7.5)
  115. local notch_by = by - guide_size
  116. local notch_ay_big = ay + round(self.font_size * 1.1)
  117. local notch_ay_medium = notch_ay_big + ((notch_by - notch_ay_big) * 0.2)
  118. local notch_ay_small = notch_ay_big + ((notch_by - notch_ay_big) * 0.4)
  119. local from_to_index = math.floor(self.notches / 2)
  120. for i = -from_to_index, from_to_index do
  121. local notch_speed = nearest_notch_speed + (i * self.notch_every)
  122. if notch_speed >= 0 and notch_speed <= 100 then
  123. local notch_x = nearest_notch_x + (i * self.notch_spacing)
  124. local notch_thickness = 1
  125. local notch_ay = notch_ay_small
  126. if (notch_speed % (self.notch_every * 10)) < 0.00000001 then
  127. notch_ay = notch_ay_big
  128. notch_thickness = 1.5
  129. elseif (notch_speed % (self.notch_every * 5)) < 0.00000001 then
  130. notch_ay = notch_ay_medium
  131. end
  132. ass:rect(notch_x - notch_thickness, notch_ay, notch_x + notch_thickness, notch_by, {
  133. color = fg,
  134. border = 1,
  135. border_color = bg,
  136. opacity = math.min(1.2 - (math.abs((notch_x - ax - half_width) / half_width)), 1) * opacity,
  137. })
  138. end
  139. end
  140. -- Center guide
  141. ass:new_event()
  142. ass:append('{\\rDefault\\an7\\blur0\\bord1\\shad0\\1c&H' .. fg .. '\\3c&H' .. bg .. '}')
  143. ass:opacity(opacity)
  144. ass:pos(0, 0)
  145. ass:draw_start()
  146. ass:move_to(half_x, by - 2 - guide_size)
  147. ass:line_to(half_x + guide_size, by - 2)
  148. ass:line_to(half_x - guide_size, by - 2)
  149. ass:draw_stop()
  150. -- Speed value
  151. local speed_text = (round(state.speed * 100) / 100) .. 'x'
  152. ass:txt(half_x, ay + (notch_ay_big - ay) / 2, 5, speed_text, {
  153. size = self.font_size,
  154. color = bgt,
  155. border = options.text_border * state.scale,
  156. border_color = bg,
  157. opacity = opacity,
  158. })
  159. return ass
  160. end
  161. return Speed