whirled_peas 0.7.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (220) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +25 -0
  4. data/README.md +467 -120
  5. data/Rakefile +1 -20
  6. data/bin/easing +33 -0
  7. data/bin/reset_cursor +11 -0
  8. data/bin/screen_test +68 -0
  9. data/examples/graph.rb +54 -0
  10. data/examples/intro.rb +3 -3
  11. data/examples/scrolling.rb +5 -4
  12. data/lib/data/themes.yaml +13 -0
  13. data/lib/whirled_peas.rb +12 -6
  14. data/lib/whirled_peas/animator.rb +5 -0
  15. data/lib/whirled_peas/animator/debug_consumer.rb +17 -0
  16. data/lib/whirled_peas/animator/easing.rb +72 -0
  17. data/lib/whirled_peas/animator/frame.rb +5 -0
  18. data/lib/whirled_peas/animator/frameset.rb +33 -0
  19. data/lib/whirled_peas/animator/producer.rb +35 -0
  20. data/lib/whirled_peas/animator/renderer_consumer.rb +31 -0
  21. data/lib/whirled_peas/command.rb +5 -0
  22. data/lib/whirled_peas/command/base.rb +86 -0
  23. data/lib/whirled_peas/command/config_command.rb +43 -0
  24. data/lib/whirled_peas/command/debug.rb +21 -0
  25. data/lib/whirled_peas/command/fonts.rb +22 -0
  26. data/lib/whirled_peas/command/frame_command.rb +34 -0
  27. data/lib/whirled_peas/command/frames.rb +24 -0
  28. data/lib/whirled_peas/command/help.rb +38 -0
  29. data/lib/whirled_peas/command/play.rb +111 -0
  30. data/lib/whirled_peas/command/record.rb +57 -0
  31. data/lib/whirled_peas/command/still.rb +29 -0
  32. data/lib/whirled_peas/command/themes.rb +76 -0
  33. data/lib/whirled_peas/command_line.rb +24 -212
  34. data/lib/whirled_peas/config.rb +56 -6
  35. data/lib/whirled_peas/device.rb +5 -0
  36. data/lib/whirled_peas/device/null_device.rb +8 -0
  37. data/lib/whirled_peas/device/output_file.rb +19 -0
  38. data/lib/whirled_peas/device/screen.rb +26 -0
  39. data/lib/whirled_peas/graphics/composer.rb +23 -2
  40. data/lib/whirled_peas/graphics/container_painter.rb +91 -0
  41. data/lib/whirled_peas/graphics/content_dimensions.rb +19 -0
  42. data/lib/whirled_peas/graphics/content_painter.rb +20 -0
  43. data/lib/whirled_peas/graphics/graph_dimensions.rb +12 -0
  44. data/lib/whirled_peas/graphics/graph_painter.rb +97 -0
  45. data/lib/whirled_peas/graphics/grid_painter.rb +1 -4
  46. data/lib/whirled_peas/graphics/painter.rb +10 -0
  47. data/lib/whirled_peas/graphics/renderer.rb +8 -2
  48. data/lib/whirled_peas/graphics/text_painter.rb +7 -11
  49. data/lib/whirled_peas/settings/border.rb +25 -4
  50. data/lib/whirled_peas/settings/container_settings.rb +2 -4
  51. data/lib/whirled_peas/settings/element_settings.rb +12 -3
  52. data/lib/whirled_peas/settings/graph_settings.rb +21 -0
  53. data/lib/whirled_peas/settings/grid_settings.rb +7 -0
  54. data/lib/whirled_peas/settings/text_settings.rb +1 -0
  55. data/lib/whirled_peas/settings/theme.rb +40 -0
  56. data/lib/whirled_peas/settings/theme_library.rb +63 -0
  57. data/lib/whirled_peas/utils/ansi.rb +13 -0
  58. data/lib/whirled_peas/utils/file_handler.rb +57 -0
  59. data/lib/whirled_peas/version.rb +1 -1
  60. data/screen_test/elements/box.frame +1 -1
  61. data/screen_test/elements/box.rb +1 -1
  62. data/screen_test/elements/graph.frame +1 -0
  63. data/screen_test/elements/graph.rb +12 -0
  64. data/screen_test/elements/grid.frame +1 -1
  65. data/screen_test/elements/grid.rb +1 -1
  66. data/screen_test/elements/screen_overflow_x.frame +1 -1
  67. data/screen_test/elements/screen_overflow_x.rb +1 -1
  68. data/screen_test/elements/screen_overflow_y.frame +1 -1
  69. data/screen_test/elements/screen_overflow_y.rb +1 -1
  70. data/screen_test/elements/text.frame +1 -1
  71. data/screen_test/elements/text.rb +1 -1
  72. data/screen_test/elements/text_multiline.frame +1 -1
  73. data/screen_test/elements/text_multiline.rb +1 -1
  74. data/screen_test/elements/theme.frame +1 -0
  75. data/screen_test/elements/theme.rb +26 -0
  76. data/screen_test/settings/align/box_around.frame +1 -1
  77. data/screen_test/settings/align/box_around.rb +1 -1
  78. data/screen_test/settings/align/box_between.frame +1 -1
  79. data/screen_test/settings/align/box_between.rb +1 -1
  80. data/screen_test/settings/align/box_center.frame +1 -1
  81. data/screen_test/settings/align/box_center.rb +1 -1
  82. data/screen_test/settings/align/box_default.frame +1 -1
  83. data/screen_test/settings/align/box_default.rb +1 -1
  84. data/screen_test/settings/align/box_evenly.frame +1 -1
  85. data/screen_test/settings/align/box_evenly.rb +1 -1
  86. data/screen_test/settings/align/box_left.frame +1 -1
  87. data/screen_test/settings/align/box_left.rb +1 -1
  88. data/screen_test/settings/align/box_right.frame +1 -1
  89. data/screen_test/settings/align/box_right.rb +1 -1
  90. data/screen_test/settings/align/children_center.frame +1 -1
  91. data/screen_test/settings/align/children_center.rb +1 -1
  92. data/screen_test/settings/align/children_left.frame +1 -1
  93. data/screen_test/settings/align/children_left.rb +1 -1
  94. data/screen_test/settings/align/children_right.frame +1 -1
  95. data/screen_test/settings/align/children_right.rb +1 -1
  96. data/screen_test/settings/align/grid_center.frame +1 -1
  97. data/screen_test/settings/align/grid_center.rb +1 -1
  98. data/screen_test/settings/align/grid_default.frame +1 -1
  99. data/screen_test/settings/align/grid_default.rb +1 -1
  100. data/screen_test/settings/align/grid_left.frame +1 -1
  101. data/screen_test/settings/align/grid_left.rb +1 -1
  102. data/screen_test/settings/align/grid_right.frame +1 -1
  103. data/screen_test/settings/align/grid_right.rb +1 -1
  104. data/screen_test/settings/ansi/bold.frame +1 -1
  105. data/screen_test/settings/ansi/bold.rb +1 -1
  106. data/screen_test/settings/ansi/color.frame +1 -1
  107. data/screen_test/settings/ansi/color.rb +1 -1
  108. data/screen_test/settings/ansi/underline.frame +1 -1
  109. data/screen_test/settings/ansi/underline.rb +1 -1
  110. data/screen_test/settings/border.frame +1 -1
  111. data/screen_test/settings/border.rb +1 -1
  112. data/screen_test/settings/flow/box_b2t.frame +1 -1
  113. data/screen_test/settings/flow/box_b2t.rb +1 -1
  114. data/screen_test/settings/flow/box_l2r.frame +1 -1
  115. data/screen_test/settings/flow/box_l2r.rb +1 -1
  116. data/screen_test/settings/flow/box_r2l.frame +1 -1
  117. data/screen_test/settings/flow/box_r2l.rb +1 -1
  118. data/screen_test/settings/flow/box_t2b.frame +1 -1
  119. data/screen_test/settings/flow/box_t2b.rb +1 -1
  120. data/screen_test/settings/flow/grid_b2t.frame +1 -1
  121. data/screen_test/settings/flow/grid_b2t.rb +2 -2
  122. data/screen_test/settings/flow/grid_l2r.frame +1 -1
  123. data/screen_test/settings/flow/grid_l2r.rb +2 -2
  124. data/screen_test/settings/flow/grid_r2l.frame +1 -1
  125. data/screen_test/settings/flow/grid_r2l.rb +2 -2
  126. data/screen_test/settings/flow/grid_t2b.frame +1 -1
  127. data/screen_test/settings/flow/grid_t2b.rb +2 -2
  128. data/screen_test/settings/height/box.frame +1 -1
  129. data/screen_test/settings/height/box.rb +1 -1
  130. data/screen_test/settings/height/box_border_sizing.frame +1 -1
  131. data/screen_test/settings/height/box_border_sizing.rb +1 -1
  132. data/screen_test/settings/height/grid.frame +1 -1
  133. data/screen_test/settings/height/grid.rb +1 -1
  134. data/screen_test/settings/height/overflow_box.frame +1 -1
  135. data/screen_test/settings/height/overflow_box.rb +1 -1
  136. data/screen_test/settings/height/overflow_box_l2r.frame +1 -1
  137. data/screen_test/settings/height/overflow_box_l2r.rb +1 -1
  138. data/screen_test/settings/height/overflow_box_t2b.frame +1 -1
  139. data/screen_test/settings/height/overflow_box_t2b.rb +1 -1
  140. data/screen_test/settings/height/overflow_grid.frame +1 -1
  141. data/screen_test/settings/height/overflow_grid.rb +1 -1
  142. data/screen_test/settings/margin.frame +1 -1
  143. data/screen_test/settings/margin.rb +1 -1
  144. data/screen_test/settings/padding.frame +1 -1
  145. data/screen_test/settings/padding.rb +1 -1
  146. data/screen_test/settings/position/box_left.frame +1 -1
  147. data/screen_test/settings/position/box_left.rb +1 -1
  148. data/screen_test/settings/position/box_left_negative.frame +1 -1
  149. data/screen_test/settings/position/box_left_negative.rb +1 -1
  150. data/screen_test/settings/position/box_top.frame +1 -1
  151. data/screen_test/settings/position/box_top.rb +1 -1
  152. data/screen_test/settings/position/box_top_negative.frame +1 -1
  153. data/screen_test/settings/position/box_top_negative.rb +1 -1
  154. data/screen_test/settings/position/grid_left.frame +1 -1
  155. data/screen_test/settings/position/grid_left.rb +1 -1
  156. data/screen_test/settings/position/grid_left_negative.frame +1 -1
  157. data/screen_test/settings/position/grid_left_negative.rb +1 -1
  158. data/screen_test/settings/position/grid_top.frame +1 -1
  159. data/screen_test/settings/position/grid_top.rb +1 -1
  160. data/screen_test/settings/position/grid_top_negative.frame +1 -1
  161. data/screen_test/settings/position/grid_top_negative.rb +1 -1
  162. data/screen_test/settings/scroll/horiz_box.frame +1 -1
  163. data/screen_test/settings/scroll/horiz_box.rb +1 -1
  164. data/screen_test/settings/scroll/horiz_box_align_center.rb +1 -1
  165. data/screen_test/settings/scroll/horiz_box_align_right.rb +1 -1
  166. data/screen_test/settings/scroll/vert_box.frame +1 -1
  167. data/screen_test/settings/scroll/vert_box.rb +1 -1
  168. data/screen_test/settings/title_font.frame +1 -1
  169. data/screen_test/settings/title_font.rb +1 -1
  170. data/screen_test/settings/valign/box_around.frame +1 -1
  171. data/screen_test/settings/valign/box_around.rb +1 -1
  172. data/screen_test/settings/valign/box_between.frame +1 -1
  173. data/screen_test/settings/valign/box_between.rb +1 -1
  174. data/screen_test/settings/valign/box_bottom.frame +1 -1
  175. data/screen_test/settings/valign/box_bottom.rb +1 -1
  176. data/screen_test/settings/valign/box_default.frame +1 -1
  177. data/screen_test/settings/valign/box_default.rb +1 -1
  178. data/screen_test/settings/valign/box_evenly.frame +1 -1
  179. data/screen_test/settings/valign/box_evenly.rb +1 -1
  180. data/screen_test/settings/valign/box_middle.frame +1 -1
  181. data/screen_test/settings/valign/box_middle.rb +1 -1
  182. data/screen_test/settings/valign/box_top.frame +1 -1
  183. data/screen_test/settings/valign/box_top.rb +1 -1
  184. data/screen_test/settings/valign/grid_bottom.frame +1 -1
  185. data/screen_test/settings/valign/grid_bottom.rb +1 -1
  186. data/screen_test/settings/valign/grid_default.frame +1 -1
  187. data/screen_test/settings/valign/grid_default.rb +1 -1
  188. data/screen_test/settings/valign/grid_middle.frame +1 -1
  189. data/screen_test/settings/valign/grid_middle.rb +1 -1
  190. data/screen_test/settings/valign/grid_top.frame +1 -1
  191. data/screen_test/settings/valign/grid_top.rb +1 -1
  192. data/screen_test/settings/width/box_border_sizing.frame +1 -1
  193. data/screen_test/settings/width/box_border_sizing.rb +1 -1
  194. data/screen_test/settings/width/box_content.frame +1 -1
  195. data/screen_test/settings/width/box_content.rb +1 -1
  196. data/screen_test/settings/width/box_default.frame +1 -1
  197. data/screen_test/settings/width/box_default.rb +1 -1
  198. data/screen_test/settings/width/grid.frame +1 -1
  199. data/screen_test/settings/width/grid.rb +1 -1
  200. data/screen_test/settings/width/overflow_align_center.frame +1 -1
  201. data/screen_test/settings/width/overflow_align_center.rb +1 -1
  202. data/screen_test/settings/width/overflow_align_right.frame +1 -1
  203. data/screen_test/settings/width/overflow_align_right.rb +1 -1
  204. data/screen_test/settings/width/overflow_box.frame +1 -1
  205. data/screen_test/settings/width/overflow_box.rb +1 -1
  206. data/screen_test/settings/width/overflow_box_l2r.frame +1 -1
  207. data/screen_test/settings/width/overflow_box_l2r.rb +1 -1
  208. data/screen_test/settings/width/overflow_box_t2b.frame +1 -1
  209. data/screen_test/settings/width/overflow_box_t2b.rb +1 -1
  210. data/screen_test/settings/width/overflow_grid.frame +1 -1
  211. data/screen_test/settings/width/overflow_grid.rb +1 -1
  212. data/tools/whirled_peas/tools/screen_tester.rb +131 -65
  213. metadata +42 -9
  214. data/lib/whirled_peas/frame.rb +0 -6
  215. data/lib/whirled_peas/frame/consumer.rb +0 -30
  216. data/lib/whirled_peas/frame/debug_consumer.rb +0 -30
  217. data/lib/whirled_peas/frame/event_loop.rb +0 -90
  218. data/lib/whirled_peas/frame/producer.rb +0 -67
  219. data/lib/whirled_peas/graphics/screen.rb +0 -70
  220. data/lib/whirled_peas/graphics/text_dimensions.rb +0 -15
data/Rakefile CHANGED
@@ -3,29 +3,10 @@ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- def screen_test(file, method)
7
- raise ArgumentError, 'Missing argument: file' unless file
8
- require_relative 'tools/whirled_peas/tools/screen_tester'
9
- WhirledPeas::Tools::ScreenTester.new(file).send(method)
10
- end
11
-
12
- namespace :screen_test do
13
- %i[template view run save update debug].each do |t|
14
- task t, [:file] do |_, args|
15
- screen_test(args[:file], t)
16
- end
17
- end
18
-
19
- task :update_all do
20
- require_relative 'tools/whirled_peas/tools/screen_tester'
21
- WhirledPeas::Tools::ScreenTester.update_all
22
- end
23
- end
24
-
25
6
  task :screen_test do
26
7
  require_relative 'tools/whirled_peas/tools/screen_tester'
27
8
  WhirledPeas::Tools::ScreenTester.run_all
28
9
  end
29
10
 
30
- task default: :ci
31
11
  task ci: %i[spec screen_test]
12
+ task default: :ci
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'whirled_peas'
5
+ require 'whirled_peas/animator/easing'
6
+
7
+ module WhirledPeas
8
+ NUM_POINTS = 60
9
+
10
+ Animator::Easing::EASING.keys.each do |func|
11
+ Animator::Easing::EFFECTS.each do |effect|
12
+ easing = Animator::Easing.new(func, effect)
13
+ graph = Array.new(NUM_POINTS / ASPECT_RATIO) { '┃' }
14
+ NUM_POINTS.times.each do |i|
15
+ input = i.to_f / (NUM_POINTS - 1)
16
+ eased_value = ((easing.ease(input) * NUM_POINTS * 2) / ASPECT_RATIO).floor
17
+ graph.each.with_index do |row, row_num|
18
+ y_value = NUM_POINTS.to_f / ASPECT_RATIO - row_num - 1
19
+ row << if eased_value == 2 * y_value
20
+ '▄'
21
+ elsif eased_value == 2 * y_value + 1
22
+ '▀'
23
+ else
24
+ ' '
25
+ end
26
+ end
27
+ end
28
+ graph << '┗' + '━' * NUM_POINTS
29
+ puts "#{func} (#{effect})"
30
+ puts graph.join("\n")
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'whirled_peas'
5
+ require 'whirled_peas/utils/ansi'
6
+
7
+ module WhirledPeas
8
+ print Utils::Ansi.clear
9
+ print Utils::Ansi.cursor_visible(true)
10
+ STDOUT.flush
11
+ end
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'whirled_peas'
5
+ require 'whirled_peas/utils/formatted_string'
6
+
7
+ require_relative '../tools/whirled_peas/tools/screen_tester'
8
+
9
+ def print_usage(exit_code=0)
10
+ puts "Usage: #{$0} [file] [options]"
11
+ puts
12
+ puts 'If not file or options are provide, all tests are run'
13
+ puts
14
+ puts 'If no file is provided, the supported options are'
15
+ puts ' --help print this usage statement and exit'
16
+ puts ' --view-pending interactively display and optionally save rendered output for each pending test'
17
+ puts ' --view-failed interactively display and optionally save rendered output for each faiing test'
18
+ puts
19
+ puts 'If a screen test file is provided as the first argument, the supported options are'
20
+ puts ' --run run screen test for given file'
21
+ puts ' --view interactively display and optionally save the file\'s test output'
22
+ puts ' --template print out template tree for the test template'
23
+ puts ' --debug render the test template without displying it, printing out debug information'
24
+ exit(exit_code)
25
+ end
26
+
27
+ module WhirledPeas
28
+ if ARGV.length == 0
29
+ Tools::ScreenTester.run_all
30
+ elsif ARGV.length == 1
31
+ option = ARGV[0]
32
+ case option
33
+ when '--help'
34
+ print_usage
35
+ when '--view-pending'
36
+ Tools::ScreenTester.view_pending
37
+ when '--view-failed'
38
+ Tools::ScreenTester.view_failed
39
+ else
40
+ print_usage(1)
41
+ end
42
+ elsif ARGV.length == 2
43
+ file, option = ARGV
44
+ case option
45
+ when '--run'
46
+ tester = Tools::ScreenTester.new(file)
47
+ if tester.pending?
48
+ puts Utils::FormattedString.new('Pending', Settings::TextColor::YELLOW)
49
+ puts "Run `#{$0} #{file} --view` to view and optionally save ouput."
50
+ elsif tester.failed?
51
+ puts Utils::FormattedString.new('Failed', Settings::TextColor::RED)
52
+ puts "Run `#{$0} #{file} --view` to compare and optionally save ouput."
53
+ else
54
+ puts Utils::FormattedString.new('Pass', Settings::TextColor::GREEN)
55
+ end
56
+ when '--view'
57
+ Tools::ScreenTester.new(file).view
58
+ when '--template'
59
+ Tools::ScreenTester.new(file).template
60
+ when '--debug'
61
+ Tools::ScreenTester.new(file).debug
62
+ else
63
+ print_usage(1)
64
+ end
65
+ else
66
+ print_usage(1)
67
+ end
68
+ end
@@ -0,0 +1,54 @@
1
+ require 'bundler/setup'
2
+ require 'whirled_peas'
3
+ require 'whirled_peas/animator/easing'
4
+
5
+ module WhirledPeas
6
+ PublicEasing = Animator::Easing
7
+ end
8
+
9
+ class TemplateFactory
10
+ INNER_WIDTH = 60
11
+
12
+ def build(name, args)
13
+ WhirledPeas.template do |composer, settings|
14
+ settings.bg_color = :bright_white
15
+ settings.align = :center
16
+ settings.valign = :middle
17
+ composer.add_grid do |composer, settings|
18
+ settings.num_cols = 2
19
+ WhirledPeas::PublicEasing::EASING.keys.each do |func|
20
+ easing = WhirledPeas::PublicEasing.new(func, :in_out)
21
+ composer.add_box do |composer, settings|
22
+ settings.flow = :t2b
23
+ settings.set_padding(horiz: 4, vert: 2)
24
+ composer.add_box do |_, settings|
25
+ settings.margin.bottom = 1
26
+ settings.color = :blue
27
+ settings.underline = true
28
+ "#{func} (in_out)"
29
+ end
30
+ composer.add_graph do |composer, settings|
31
+ settings.axis_color = :black
32
+ settings.color = :bright_blue
33
+ settings.height = 15
34
+ INNER_WIDTH.times.map do |i|
35
+ easing.ease(i.to_f / (INNER_WIDTH - 1))
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ class Application
46
+ def start(producer)
47
+ producer.add_frame('intro', duration: 5)
48
+ end
49
+ end
50
+
51
+ WhirledPeas.configure do |config|
52
+ config.template_factory = TemplateFactory.new
53
+ config.application = Application.new
54
+ end
@@ -40,13 +40,13 @@ class TemplateFactory
40
40
  end
41
41
  end
42
42
 
43
- class Driver
43
+ class Application
44
44
  def start(producer)
45
- producer.send_frame('intro', duration: 5)
45
+ producer.add_frame('intro', duration: 5)
46
46
  end
47
47
  end
48
48
 
49
49
  WhirledPeas.configure do |config|
50
50
  config.template_factory = TemplateFactory.new
51
- config.driver = Driver.new
51
+ config.application = Application.new
52
52
  end
@@ -39,15 +39,16 @@ class TemplateFactory
39
39
  end
40
40
  end
41
41
 
42
- class Driver
42
+ class Application
43
43
  def start(producer)
44
- 53.times do |i|
45
- producer.send_frame('intro', duration: 0.3, args: { top: -i })
44
+ producer.frameset(5, easing: :bezier) do |fs|
45
+ 53.times { |i| fs.add_frame('intro', args: { top: -i }) }
46
46
  end
47
+ producer.add_frame('hold', duration: 1, args: { top: -52})
47
48
  end
48
49
  end
49
50
 
50
51
  WhirledPeas.configure do |config|
51
52
  config.template_factory = TemplateFactory.new
52
- config.driver = Driver.new
53
+ config.application = Application.new
53
54
  end
@@ -0,0 +1,13 @@
1
+ bright:
2
+ bg_color: :bright_white
3
+ color: :blue
4
+ border_color: :bright_blue
5
+ axis_color: :red
6
+ title_font: :default
7
+
8
+ dark:
9
+ bg_color: :black
10
+ color: :white
11
+ border_color: :gray
12
+ axis_color: :bright_white
13
+ title_font: :swan
@@ -1,8 +1,6 @@
1
- require 'logger'
2
-
3
- require 'whirled_peas/errors'
1
+ require 'whirled_peas/animator'
4
2
  require 'whirled_peas/config'
5
- require 'whirled_peas/frame'
3
+ require 'whirled_peas/errors'
6
4
  require 'whirled_peas/graphics'
7
5
  require 'whirled_peas/settings'
8
6
  require 'whirled_peas/utils'
@@ -17,8 +15,16 @@ module WhirledPeas
17
15
  yield config
18
16
  end
19
17
 
20
- def self.template(&block)
18
+ def self.template(theme_name=nil, &block)
21
19
  require 'whirled_peas/graphics/composer'
22
- Graphics::Composer.build(&block)
20
+ Graphics::Composer.build(theme_name, &block)
21
+ end
22
+
23
+ def self.define_theme(name, &block)
24
+ require 'whirled_peas/settings/theme'
25
+ require 'whirled_peas/settings/theme_library'
26
+ theme = Settings::Theme.new
27
+ yield theme
28
+ Settings::ThemeLibrary.add(name, theme)
23
29
  end
24
30
  end
@@ -0,0 +1,5 @@
1
+ module WhirledPeas
2
+ module Animator
3
+ end
4
+ private_constant :Animator
5
+ end
@@ -0,0 +1,17 @@
1
+ module WhirledPeas
2
+ module Animator
3
+ class DebugConsumer
4
+ def add_frameset(frameset)
5
+ require 'json'
6
+
7
+ frameset.each_frame do |frame, args|
8
+ puts [frame, *(JSON.generate(args) unless args.empty?)].join(' ')
9
+ end
10
+ end
11
+
12
+ def process
13
+ # no op
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,72 @@
1
+ module WhirledPeas
2
+ module Animator
3
+ class Easing
4
+ # Implementations of available ease-in functions. Ease-out and ease-in-out can all be
5
+ # derived from ease-in.
6
+ EASING = {
7
+ bezier: proc do |value|
8
+ value /= 2
9
+ 2 * value * value * (3 - 2 * value)
10
+ end,
11
+ linear: proc { |value| value },
12
+ parametric: proc do |value|
13
+ value /= 2
14
+ squared = value * value
15
+ 2 * squared / (2 * (squared - value) + 1)
16
+ end,
17
+ quadratic: proc do |value|
18
+ value * value
19
+ end
20
+ }
21
+
22
+ EFFECTS = %i[in out in_out]
23
+
24
+ def initialize(easing=:linear, effect=:in_out)
25
+ unless EASING.key?(easing)
26
+ raise ArgumentError,
27
+ "Invalid easing function: #{easing}, expecting one of #{EASING.keys.join(', ')}"
28
+ end
29
+ unless EFFECTS.include?(effect)
30
+ raise ArgumentError,
31
+ "Invalid effect: #{effect}, expecting one of #{EFFECTS.join(', ')}"
32
+ end
33
+ @easing = easing
34
+ @effect = effect
35
+ end
36
+
37
+ def ease(value)
38
+ case effect
39
+ when :in
40
+ ease_in(value)
41
+ when :out
42
+ ease_out(value)
43
+ else
44
+ ease_in_out(value)
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ attr_reader :easing, :effect
51
+
52
+ # The procs in EASING define the ease-in functions, so we simply need
53
+ # to invoke the function with the given normalized value
54
+ def ease_in(value)
55
+ EASING[easing].call(value)
56
+ end
57
+
58
+ # The ease-out function will be the ease-in function rotated 180 degrees.
59
+ def ease_out(value)
60
+ 1 - EASING[easing].call(1 - value)
61
+ end
62
+
63
+ def ease_in_out(value)
64
+ if value < 0.5
65
+ ease_in(value * 2) / 2
66
+ else
67
+ 0.5 + ease_out(2 * value - 1) / 2
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,5 @@
1
+ module WhirledPeas
2
+ module Animator
3
+ Frame = Struct.new(:name, :args)
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'easing'
2
+ require_relative 'frame'
3
+
4
+ module WhirledPeas
5
+ module Animator
6
+ class Frameset
7
+ def initialize(frame_slots, easing, effect)
8
+ @frame_slots = frame_slots
9
+ @easing = Easing.new(easing, effect)
10
+ @frames = []
11
+ end
12
+
13
+ def add_frame(name, args: {})
14
+ frames << [name, args]
15
+ end
16
+
17
+ # Yield each frame in an "eased" order
18
+ def each_frame(&block)
19
+ frame_slots.times do |i|
20
+ input = i.to_f / (frame_slots - 1)
21
+ eased_value = @easing.ease(input)
22
+ index = (eased_value * (frames.length - 1)).floor
23
+ yield *frames[index]
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :frame_slots, :easing, :frames
30
+ end
31
+ private_constant :Frameset
32
+ end
33
+ end
@@ -0,0 +1,35 @@
1
+ require_relative 'easing'
2
+ require_relative 'frameset'
3
+
4
+ module WhirledPeas
5
+ module Animator
6
+ class Producer
7
+ def self.produce(consumer, refresh_rate)
8
+ producer = new(consumer, refresh_rate)
9
+ yield producer
10
+ consumer.process
11
+ end
12
+
13
+ def initialize(consumer, refresh_rate)
14
+ @consumer = consumer
15
+ @refresh_rate = refresh_rate
16
+ end
17
+
18
+ def add_frame(name, duration: nil, args: {})
19
+ frameset(duration || 1 / refresh_rate) do |fs|
20
+ fs.add_frame(name, args: args)
21
+ end
22
+ end
23
+
24
+ def frameset(duration, easing: :linear, effect: :in_out, &block)
25
+ fs = Frameset.new((duration * refresh_rate).round, easing, effect)
26
+ yield fs
27
+ consumer.add_frameset(fs)
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :consumer, :refresh_rate
33
+ end
34
+ end
35
+ end