bagel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.prettierignore +2 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +13 -0
  6. data/CHANGELOG.md +5 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +187 -0
  10. data/Rakefile +12 -0
  11. data/bagel.gemspec +32 -0
  12. data/bin/console +15 -0
  13. data/bin/setup +8 -0
  14. data/lib/bagel/colors.rb +8 -0
  15. data/lib/bagel/fonts.rb +3 -0
  16. data/lib/bagel/graphic/comment.rb +49 -0
  17. data/lib/bagel/graphic/intro.rb +49 -0
  18. data/lib/bagel/graphic/scoreboard/context.rb +28 -0
  19. data/lib/bagel/graphic/scoreboard/games.rb +13 -0
  20. data/lib/bagel/graphic/scoreboard/name.rb +32 -0
  21. data/lib/bagel/graphic/scoreboard/points.rb +13 -0
  22. data/lib/bagel/graphic/scoreboard/score.rb +38 -0
  23. data/lib/bagel/graphic/scoreboard/server.rb +16 -0
  24. data/lib/bagel/graphic/scoreboard/sets.rb +13 -0
  25. data/lib/bagel/graphic/scoreboard.rb +102 -0
  26. data/lib/bagel/graphic/stats/duration.rb +33 -0
  27. data/lib/bagel/graphic/stats/header.rb +32 -0
  28. data/lib/bagel/graphic/stats/result.rb +41 -0
  29. data/lib/bagel/graphic/stats/stat.rb +48 -0
  30. data/lib/bagel/graphic/stats.rb +80 -0
  31. data/lib/bagel/graphic.rb +23 -0
  32. data/lib/bagel/tennis/checker.rb +100 -0
  33. data/lib/bagel/tennis/outcome.rb +48 -0
  34. data/lib/bagel/tennis/player.rb +22 -0
  35. data/lib/bagel/tennis/point.rb +17 -0
  36. data/lib/bagel/tennis/score.rb +68 -0
  37. data/lib/bagel/tennis/scoring.rb +26 -0
  38. data/lib/bagel/tennis/serve.rb +36 -0
  39. data/lib/bagel/tennis/stats/aces.rb +28 -0
  40. data/lib/bagel/tennis/stats/break_points_won.rb +33 -0
  41. data/lib/bagel/tennis/stats/double_faults.rb +28 -0
  42. data/lib/bagel/tennis/stats/first_serve_in.rb +33 -0
  43. data/lib/bagel/tennis/stats/first_serve_points_won.rb +33 -0
  44. data/lib/bagel/tennis/stats/net_points_won.rb +33 -0
  45. data/lib/bagel/tennis/stats/points_won.rb +24 -0
  46. data/lib/bagel/tennis/stats/second_serve_points_won.rb +33 -0
  47. data/lib/bagel/tennis/stats/stat.rb +40 -0
  48. data/lib/bagel/tennis/stats/unforced_errors.rb +24 -0
  49. data/lib/bagel/tennis/stats/winners.rb +24 -0
  50. data/lib/bagel/tennis/stats.rb +16 -0
  51. data/lib/bagel/tennis.rb +13 -0
  52. data/lib/bagel/timeframe.rb +31 -0
  53. data/lib/bagel/version.rb +5 -0
  54. data/lib/bagel/video/clip.rb +41 -0
  55. data/lib/bagel/video/clip_builder_factory.rb +13 -0
  56. data/lib/bagel/video/clip_builders/clip_builder.rb +32 -0
  57. data/lib/bagel/video/clip_builders/intro_clip_builder.rb +37 -0
  58. data/lib/bagel/video/clip_builders/point_clip_builder.rb +67 -0
  59. data/lib/bagel/video/clip_builders/stats_clip_builder.rb +56 -0
  60. data/lib/bagel/video/clip_data_parser.rb +39 -0
  61. data/lib/bagel/video/clip_director.rb +16 -0
  62. data/lib/bagel/video/clip_saver.rb +59 -0
  63. data/lib/bagel/video/fade.rb +24 -0
  64. data/lib/bagel/video/ffmpeg/concat_file.rb +45 -0
  65. data/lib/bagel/video/ffmpeg/fade_filter.rb +34 -0
  66. data/lib/bagel/video/ffmpeg/overlay_filter.rb +52 -0
  67. data/lib/bagel/video/ffmpeg.rb +41 -0
  68. data/lib/bagel/video/orientation.rb +8 -0
  69. data/lib/bagel/video/overlays/comment.rb +15 -0
  70. data/lib/bagel/video/overlays/intro.rb +15 -0
  71. data/lib/bagel/video/overlays/overlay.rb +28 -0
  72. data/lib/bagel/video/overlays/scoreboard.rb +15 -0
  73. data/lib/bagel/video/overlays/second_serve.rb +15 -0
  74. data/lib/bagel/video/overlays/stats.rb +15 -0
  75. data/lib/bagel/video/padding.rb +9 -0
  76. data/lib/bagel/video/video.rb +18 -0
  77. data/lib/bagel/video.rb +32 -0
  78. data/lib/bagel.rb +76 -0
  79. metadata +196 -0
@@ -0,0 +1,23 @@
1
+ require 'bagel/graphic/intro'
2
+ require 'bagel/graphic/comment'
3
+
4
+ require 'bagel/graphic/scoreboard'
5
+ require 'bagel/graphic/scoreboard/context'
6
+ require 'bagel/graphic/scoreboard/server'
7
+ require 'bagel/graphic/scoreboard/name'
8
+ require 'bagel/graphic/scoreboard/score'
9
+ require 'bagel/graphic/scoreboard/sets'
10
+ require 'bagel/graphic/scoreboard/games'
11
+ require 'bagel/graphic/scoreboard/points'
12
+
13
+ require 'bagel/graphic/stats'
14
+ require 'bagel/graphic/stats/header'
15
+ require 'bagel/graphic/stats/result'
16
+ require 'bagel/graphic/stats/duration'
17
+ require 'bagel/graphic/stats/stat'
18
+
19
+ module Bagel
20
+ module Graphic
21
+ FILENAME_EXTENSION = '.png'
22
+ end
23
+ end
@@ -0,0 +1,100 @@
1
+ module Bagel::Tennis
2
+ class Checker
3
+ BREAK_POINT = 'BREAK POINT'
4
+ SET_POINT = 'SET POINT'
5
+ MATCH_POINT = 'MATCH POINT'
6
+
7
+ MINIMUM_POINTS_TO_WIN_GAME = 4
8
+ MINIMUM_POINTS_TO_WIN_TIEBREAK = 7
9
+
10
+ MINIMUM_GAMES_TO_WIN_SET = 6
11
+ SETS_TO_WIN_MATCH = 2
12
+
13
+ TIEBREAK_AT = [ MINIMUM_GAMES_TO_WIN_SET, MINIMUM_GAMES_TO_WIN_SET ]
14
+
15
+ private attr_reader :sets, :points, :server, :receiver
16
+
17
+ def initialize(sets:, points:, server:)
18
+ @sets, @points, @server = sets, points, server
19
+ @receiver = Player.opponent(server)
20
+ end
21
+
22
+ def break_point?
23
+ next_point_wins_game?(receiver)
24
+ end
25
+
26
+ def set_point?
27
+ next_point_wins_set?(server) || next_point_wins_set?(receiver)
28
+ end
29
+
30
+ def match_point?
31
+ next_point_wins_match?(server) || next_point_wins_match?(receiver)
32
+ end
33
+
34
+ def tiebreak?
35
+ sets.last == Tennis::Checker::TIEBREAK_AT
36
+ end
37
+
38
+ private
39
+
40
+ def points_for(player)
41
+ points[Player.index(player)]
42
+ end
43
+
44
+ def games_for(player, set)
45
+ set[Player.index(player)]
46
+ end
47
+
48
+ def next_point_wins_game?(player)
49
+ will_have_required_points?(player) && will_have_point_margin?(player)
50
+ end
51
+
52
+ def next_point_wins_set?(player)
53
+ next_point_wins_game?(player) && next_game_wins_set?(player)
54
+ end
55
+
56
+ def next_game_wins_set?(player)
57
+ will_have_required_games?(sets.last, player) && will_have_game_margin?(sets.last, player)
58
+ end
59
+
60
+ def next_point_wins_match?(player)
61
+ next_point_wins_set?(player) && next_set_wins_match?(player)
62
+ end
63
+
64
+ def next_set_wins_match?(player)
65
+ sets_won(player) + 1 == SETS_TO_WIN_MATCH
66
+ end
67
+
68
+ def will_have_required_points?(player)
69
+ points_for(player) + 1 >= (tiebreak? ? MINIMUM_POINTS_TO_WIN_TIEBREAK : MINIMUM_POINTS_TO_WIN_GAME)
70
+ end
71
+
72
+ def will_have_required_games?(set, player)
73
+ games_for(player, set) + 1 >= MINIMUM_GAMES_TO_WIN_SET
74
+ end
75
+
76
+ def will_have_point_margin?(player)
77
+ points_for(player) - points_for(Player.opponent(player)) >= 1
78
+ end
79
+
80
+ def will_have_game_margin?(set, player)
81
+ games_for(player, set) - games_for(Player.opponent(player), set) >= 1
82
+ end
83
+
84
+ def sets_won(player)
85
+ sets.count { |set| won_set?(set, player) }
86
+ end
87
+
88
+ def won_set?(set, player)
89
+ enough_games_won?(player, set) && more_games_than_opponent?(player, set)
90
+ end
91
+
92
+ def enough_games_won?(player, set)
93
+ games_for(player, set) >= MINIMUM_GAMES_TO_WIN_SET
94
+ end
95
+
96
+ def more_games_than_opponent?(player, set)
97
+ games_for(player, set) > games_for(Player.opponent(player), set)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,48 @@
1
+ module Bagel::Tennis
2
+ class Outcome
3
+ SERVE = 'S'
4
+ FOREHAND = 'FH'
5
+ BACKHAND = 'BH'
6
+
7
+ WINNER = 'W'
8
+ FORCED_ERROR = 'FE'
9
+ UNFORCED_ERROR = 'UE'
10
+
11
+ private attr_reader :winner, :reason, :shot, :net
12
+
13
+ def initialize(winner:, reason:, shot:, net: nil)
14
+ @winner = winner
15
+ @reason = reason
16
+ @shot = shot
17
+ @net = net
18
+ end
19
+
20
+ def ace?
21
+ winner? && shot == SERVE
22
+ end
23
+
24
+ def winner?
25
+ reason == WINNER
26
+ end
27
+
28
+ def unforced_error?
29
+ reason == UNFORCED_ERROR
30
+ end
31
+
32
+ def p1_won?
33
+ winner == Player.ONE
34
+ end
35
+
36
+ def p2_won?
37
+ winner == Player.TWO
38
+ end
39
+
40
+ def p1_net_point?
41
+ net && !net[Player.index(Player.ONE)].nil?
42
+ end
43
+
44
+ def p2_net_point?
45
+ net && !net[Player.index(Player.TWO)].nil?
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,22 @@
1
+ module Bagel::Tennis
2
+ class Player
3
+ PLAYER_ONE = 1
4
+ PLAYER_TWO = 2
5
+
6
+ def self.ONE
7
+ PLAYER_ONE
8
+ end
9
+
10
+ def self.TWO
11
+ PLAYER_TWO
12
+ end
13
+
14
+ def self.index(id)
15
+ id - 1
16
+ end
17
+
18
+ def self.opponent(id)
19
+ id == PLAYER_ONE ? PLAYER_TWO : PLAYER_ONE
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ module Bagel::Tennis
2
+ class Point
3
+ extend Forwardable
4
+
5
+ def_delegators :@timeframe, :start, :finish
6
+ def_delegators :@score, :break_point?, :set_point?, :match_point?
7
+ def_delegators :@serve, :server?, :no_faults?, :second_serve?, :double_fault?, :p1_serve?, :p2_serve?
8
+ def_delegators :@outcome, :ace?, :p1_won?, :p2_won?, :winner?, :unforced_error?, :p1_net_point?, :p2_net_point?
9
+
10
+ def initialize(timeframe:, score:, serve:, outcome:)
11
+ @timeframe = timeframe
12
+ @score = score
13
+ @serve = serve
14
+ @outcome = outcome
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,68 @@
1
+ module Bagel::Tennis
2
+ class Score
3
+ attr_reader :sets, :points
4
+ private attr_reader :server, :checker
5
+
6
+ def initialize(sets:, points: nil, server: nil)
7
+ @sets, @points = sets, points
8
+ @server = server
9
+ @checker = Tennis::Checker.new(sets: sets, points: points, server: server)
10
+ end
11
+
12
+ def id
13
+ id_parts.map { |i| i.to_s.rjust(2, '0') }.join('-')
14
+ end
15
+
16
+ def context
17
+ return nil if server.nil?
18
+ return Tennis::Checker::MATCH_POINT if match_point?
19
+ return Tennis::Checker::SET_POINT if set_point?
20
+ return Tennis::Checker::BREAK_POINT if break_point?
21
+ end
22
+
23
+ def server?(player_id)
24
+ server == player_id
25
+ end
26
+
27
+ def set_number
28
+ sets.count
29
+ end
30
+
31
+ def set_score
32
+ sets.last
33
+ end
34
+
35
+ def break_point?
36
+ checker.break_point?
37
+ end
38
+
39
+ def set_point?
40
+ checker.set_point?
41
+ end
42
+
43
+ def match_point?
44
+ checker.match_point?
45
+ end
46
+
47
+ def tiebreak?
48
+ checker.tiebreak?
49
+ end
50
+
51
+ private
52
+
53
+ def id_parts
54
+ parts = [ set_number ]
55
+ parts << game_number if points
56
+ parts << point_number if points
57
+ parts
58
+ end
59
+
60
+ def game_number
61
+ sets.last.sum + 1
62
+ end
63
+
64
+ def point_number
65
+ points.sum + 1
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,26 @@
1
+ module Bagel::Tennis
2
+ module Scoring
3
+ LOVE = '0'
4
+ FIFTEEN = '15'
5
+ THIRTY = '30'
6
+ FORTY = '40'
7
+ FORTY_BLANK = ''
8
+ ADVANTAGE = 'A'
9
+
10
+ def self.point_call(player, opponent, tiebreak=false)
11
+ return player.to_s if tiebreak
12
+
13
+ return LOVE if player == 0
14
+ return FIFTEEN if player == 1
15
+ return THIRTY if player == 2
16
+
17
+ if opponent < 3 || player == opponent
18
+ FORTY
19
+ elsif player < opponent
20
+ FORTY_BLANK
21
+ else
22
+ ADVANTAGE
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,36 @@
1
+ module Bagel::Tennis
2
+ class Serve
3
+ NO_FAULT = 0
4
+ DOUBLE_FAULT = 2
5
+
6
+ private attr_reader :server, :faults
7
+
8
+ def initialize(server:, faults:)
9
+ @server, @faults = server, faults
10
+ end
11
+
12
+ def server?(player)
13
+ server == player
14
+ end
15
+
16
+ def no_faults?
17
+ faults == NO_FAULT
18
+ end
19
+
20
+ def second_serve?
21
+ faults > NO_FAULT
22
+ end
23
+
24
+ def double_fault?
25
+ faults == DOUBLE_FAULT
26
+ end
27
+
28
+ def p1_serve?
29
+ server?(Player.ONE)
30
+ end
31
+
32
+ def p2_serve?
33
+ server?(Player.TWO)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ module Bagel::Tennis::Stats
2
+ class Aces < Stat
3
+ private attr_reader :p1, :p2
4
+
5
+ def name
6
+ 'ACES'
7
+ end
8
+
9
+ def values
10
+ [ p1, p2 ]
11
+ end
12
+
13
+ def superior
14
+ [ p1 > p2, p2 > p1 ]
15
+ end
16
+
17
+ private
18
+
19
+ def calculate
20
+ @p1 = aces(Player.ONE)
21
+ @p2 = aces(Player.TWO)
22
+ end
23
+
24
+ def aces(player)
25
+ points.count { |point| point.server?(player) && point.ace? }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ module Bagel::Tennis::Stats
2
+ class BreakPointsWon < Stat
3
+ private attr_reader :p1_label, :p2_label, :p1_percentage, :p2_percentage
4
+
5
+ def name
6
+ 'BREAK POINTS WON'
7
+ end
8
+
9
+ def values
10
+ [ p1_label, p2_label ]
11
+ end
12
+
13
+ def superior
14
+ [ p1_percentage > p2_percentage, p2_percentage > p1_percentage ]
15
+ end
16
+
17
+ private
18
+
19
+ def calculate
20
+ p1_break_points = points.select(&:break_point?).select(&:p2_serve?)
21
+ p2_break_points = points.select(&:break_point?).select(&:p1_serve?)
22
+
23
+ p1_break_points_won = p1_break_points.select(&:p1_won?)
24
+ p2_break_points_won = p2_break_points.select(&:p2_won?)
25
+
26
+ @p1_percentage = percentage(p1_break_points.count, p1_break_points_won.count)
27
+ @p2_percentage = percentage(p2_break_points.count, p2_break_points_won.count)
28
+
29
+ @p1_label = label(p1_break_points.count, p1_break_points_won.count, p1_percentage)
30
+ @p2_label = label(p2_break_points.count, p2_break_points_won.count, p2_percentage)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ module Bagel::Tennis::Stats
2
+ class DoubleFaults < Stat
3
+ private attr_reader :p1, :p2
4
+
5
+ def name
6
+ 'DOUBLE FAULTS'
7
+ end
8
+
9
+ def values
10
+ [ p1, p2 ]
11
+ end
12
+
13
+ def superior
14
+ [ p1 < p2, p2 < p1 ]
15
+ end
16
+
17
+ private
18
+
19
+ def calculate
20
+ @p1 = double_faults(Player.ONE)
21
+ @p2 = double_faults(Player.TWO)
22
+ end
23
+
24
+ def double_faults(player)
25
+ points.count { |point| point.server?(player) && point.double_fault? }
26
+ end
27
+ end
28
+ end