whirled_peas 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9652a887fc6a68ba5367864a25b4a211674433eae86a2f68db3923e873ab49e8
4
- data.tar.gz: 72926cd288adc3c30393990d88dcc0bca53e2fbfe79d56768e438ffaee2cf3ba
3
+ metadata.gz: 7553fd2ea78bf560098d8be8cd3987f1e8a686cb000cf6ea082a922512764069
4
+ data.tar.gz: 8fa1e64c35b311ae9346badc0198f556528c53dad9bfa0b99b2805c901369b8f
5
5
  SHA512:
6
- metadata.gz: 9e0124bd724ece36b76e1d527ffb3ccd5a06e9cd7f00c1ae5d0c39445d0c4ed4804d8c2cdc0c3df250f0d927b9927efd97d118fd0fd76bbc42277fda68a674cb
7
- data.tar.gz: '09edc4873598d1363d7d3ef2c548699bcbfa229a8821e458c201608ec9f7e3aba8cf2f081f559edc51c04244267f569e8444b7952b21ab41bfea8a7807c0432a'
6
+ metadata.gz: 531b93061429918f99dc5aa7de09a53a7fda218c97bac57b47d5a450ad22510207f71ba5a095f5a1939e4cce9d1f13d70a981e15b4e3209d0786ba12be048bd6
7
+ data.tar.gz: ea7daec0ccd65894bec99bfff75219d55dfc2cd9fc7b80d1e593b60e7a15a3e8eb7cbfa49e8ef20e8da5acbce2738d054cf56a9b0231661e46a5c399c83b8906
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.9.0 - 2021-01-29
4
+
5
+ BREAKING: `Frameset#add_frame` signature changed
6
+
7
+ - [1c28947](https://github.com/tcollier/whirled_peas/tree/1c28947980eedce034d2596732ee4860717904c5): Add graph support
8
+
3
9
  ## v0.8.0 - 2021-01-29
4
10
 
5
11
  BREAKING: `Producer` now responds to `#add_frame` instead of `#send_frame`
data/README.md CHANGED
@@ -93,16 +93,33 @@ def start(producer)
93
93
  end
94
94
  ```
95
95
 
96
- The producer provides a single method
96
+ The producer provides the following methods
97
97
 
98
98
  ```ruby
99
- # Send frame events to the UI
99
+ # Add a frame to be displayed
100
100
  #
101
101
  # @param name [String] application defined name for the frame. The template factory will be provided this name
102
102
  # @param duration [Number] time in seconds this frame should be displayed for (defaults to 1 frame)
103
103
  # @param args [Hash<Symbol, Object>] key value pairs to send as arguments to the template factory
104
104
  def add_frame(name, duration:, args:)
105
- # implementation
105
+ end
106
+
107
+ # Create and yield a frameset instance that allows applications to add multiple frames to play over the
108
+ # given duration. Adding frames to the yielded frameset will result in playback that is eased by the
109
+ # gvien `easing` and `effect` arguments (default is `:linear` easing)
110
+ def frameset(duration, easing:, effect:, &block)
111
+ end
112
+ ```
113
+
114
+ A frameset instance provides one method
115
+
116
+ ```ruby
117
+ # Add a frame to be displayed, the duration will be determine by the number of frames in the frameset along
118
+ # with the duration and easing of the frameset
119
+ #
120
+ # @param name [String] application defined name for the frame. The template factory will be provided this name
121
+ # @param args [Hash<Symbol, Object>] key value pairs to send as arguments to the template factory
122
+ def add_frame(name, args:)
106
123
  end
107
124
  ```
108
125
 
@@ -155,6 +172,7 @@ A template is created with `WhirledPeas.template`, which yields a `Composer` obj
155
172
  A `Composer` provides the following methods to add child elements, each of these takes an optional string argument that is set as the name of the element (which can be useful when debugging).
156
173
 
157
174
  - `add_box` - yields a `Composer` for a `Box` and a `BoxSettings`, which will be added to the parent's children
175
+ - `add_graph` - yields a `nil` and a `GraphSettings`, which will be added to the parent's children, the block should return an array of numbers
158
176
  - `add_grid` - yields a `Composer` for a `Grid` and a `GridSettings`, which will be added to the parent's children
159
177
  - `add_text` - yields `nil` and a `TextSettings`, which will be added to the parent's children
160
178
 
@@ -217,25 +235,26 @@ end
217
235
  Each element type has an associated settings type, which provide a custom set of options to format the output. Child settings will inherit from the parent, where applicable
218
236
  The available settigs are
219
237
 
220
- | Setting | Description | Default | Availability | Inherited |
221
- | ------------ | -------------------------------------------------------------------------------- | ---------- | --------------------- | -------------------- |
222
- | `align` | Justifies the content in the horizontal direction | `:left` | `Box`, `Grid` | No |
223
- | `bg_color` | Background color (see [Colors](#colors)) | | `Box`, `Grid`, `Text` | Yes |
224
- | `bold` | `true` makes the font bold | `false` | `Box`, `Grid`, `Text` | Yes |
225
- | `border` | Set the border for the lements | none | `Box`, `Grid`, | Only style and color |
226
- | `color` | Foreground text color (see [Colors](#colors)) | | `Box`, `Grid`, `Text` | Yes |
227
- | `flow` | Flow to display child elements (see [Display Flow](#display-flow)) | `:l2r` | `Box`, `Grid` | No |
228
- | `height` | Override the calculated height of an element's content area | | `Box`, `Grid` | No |
229
- | `margin` | Set the (left, top, right, bottom) margin of the element | `0` | `Box`, `Grid` | No |
230
- | `num_cols` | Number of columns in the grid (must be set!) | | `Grid` | No |
231
- | `padding` | Set the (left, top, right, bottom) padding of the element | `0` | `Box`, `Grid` | No |
232
- | `position` | Set the (left, top) position of the element relative to parent content area | `0` | `Box`, `Grid` | No |
233
- | `scrollbar` | Display a scroll bar for vertical or horizontal scrolling | | `Box` | No |
234
- | `sizing` | Sizing model (`:content` or `:border`) used in conjunction with `width`/`height` | `:content` | `Box` | No |
235
- | `title_font` | Font used for "large" text (see [Large Text](#large-text), ignores `underline`) | | `Text` | No |
236
- | `underline` | `true` underlines the font | `false` | `Box`, `Grid`, `Text` | Yes |
237
- | `width` | Override the calculated width of an element's content area | | `Box`, `Grid` | No |
238
- | `valign` | Justifies the content in the vertical direction | `:top` | `Box`, `Grid` | No |
238
+ | Setting | Description | Default | Availability | Inherited |
239
+ | ------------ | -------------------------------------------------------------------------------- | ---------- | ------------ | -------------------- |
240
+ | `align` | Justifies the content in the horizontal direction | `:left` | `Container` | No |
241
+ | `axis_color` | Color used to paint the axes of the graph (see [Colors](#colors)) | | `Graph` | No |
242
+ | `bg_color` | Background color (see [Colors](#colors)) | | all | Yes |
243
+ | `bold` | `true` makes the font bold | `false` | all | Yes |
244
+ | `border` | Set the border for the lements | none | `Container`, | Only style and color |
245
+ | `color` | Foreground text color (see [Colors](#colors)) | | all | Yes |
246
+ | `flow` | Flow to display child elements (see [Display Flow](#display-flow)) | `:l2r` | `Container` | No |
247
+ | `height` | Override the calculated height of an element's content area | | all | No |
248
+ | `margin` | Set the (left, top, right, bottom) margin of the element | `0` | `Container` | No |
249
+ | `num_cols` | Number of columns in the grid (must be set!) | | `Grid` | No |
250
+ | `padding` | Set the (left, top, right, bottom) padding of the element | `0` | `Container` | No |
251
+ | `position` | Set the (left, top) position of the element relative to parent content area | `0` | `Container` | No |
252
+ | `scrollbar` | Display a scroll bar for vertical or horizontal scrolling | | `Box` | No |
253
+ | `sizing` | Sizing model (`:content` or `:border`) used in conjunction with `width`/`height` | `:content` | `Box` | No |
254
+ | `title_font` | Font used for "large" text (see [Large Text](#large-text), ignores `underline`) | | `Text` | No |
255
+ | `underline` | `true` underlines the font | `false` | all | Yes |
256
+ | `width` | Override the calculated width of an element's content area | | all | No |
257
+ | `valign` | Justifies the content in the vertical direction | `:top` | `Container` | No |
239
258
 
240
259
  ##### Alignment
241
260
 
@@ -671,7 +690,265 @@ $ whirled_peas title_fonts
671
690
 
672
691
  Note: when using a title font with WhirledPeas for the first time on a system, the gem loads all fonts to check which ones are available. This can be a slow process and may cause a noticeable delay when running a visualization. Running the command above will cache the results and thus when a WhirledPeas visualization is run, there will be no lag from loading fonts.
673
692
 
674
- ### Example
693
+ #### Easing
694
+
695
+ The play duration of a frame within a frameset is determined by the easing function and effect along with the frames relative position within the frameset. The three effects are
696
+
697
+ - `:in` - apply easing only to the start of the frameset
698
+ - `:out` - apply easing only to the end of the frameset
699
+ - `:in_out` - apply easing to the start and end of the frameset
700
+
701
+ The available easing functions are
702
+
703
+ - `:bezier`
704
+
705
+ ```
706
+ # bezier (in)
707
+ ┃ ▄▀
708
+ ┃ ▄▄▀
709
+ ┃ ▄▀
710
+ ┃ ▄▀▀
711
+ ┃ ▄▄▀
712
+ ┃ ▄▄▀
713
+ ┃ ▄▄▀
714
+ ┃ ▄▀
715
+ ┃ ▄▄▀▀
716
+ ┃ ▄▄▀
717
+ ┃ ▄▀▀
718
+ ┃ ▄▄▀▀
719
+ ┃ ▄▄▀▀▀
720
+ ┃ ▄▄▄▀▀▀
721
+ ┃▄▄▄▄▄▄▄▄▄▄▀▀▀
722
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
723
+ ```
724
+
725
+ ```
726
+ # bezier (out)
727
+ ┃ ▄▄▄▀▀▀▀▀▀▀▀▀
728
+ ┃ ▄▄▄▀▀▀
729
+ ┃ ▄▄▄▀▀
730
+ ┃ ▄▄▀▀
731
+ ┃ ▄▄▀
732
+ ┃ ▄▀▀
733
+ ┃ ▄▄▀▀
734
+ ┃ ▄▀
735
+ ┃ ▄▀▀
736
+ ┃ ▄▀▀
737
+ ┃ ▄▀▀
738
+ ┃ ▄▄▀
739
+ ┃ ▄▀
740
+ ┃ ▄▀▀
741
+ ┃▄▄▀
742
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
743
+ ```
744
+
745
+ ```
746
+ # bezier (in_out)
747
+ ┃ ▄▄▄▀▀▀▀▀▀
748
+ ┃ ▄▄▀▀
749
+ ┃ ▄▀▀
750
+ ┃ ▄▀▀
751
+ ┃ ▄▀▀
752
+ ┃ ▄▀▀
753
+ ┃ ▄▄▀
754
+ ┃ ▄▀
755
+ ┃ ▄▀▀
756
+ ┃ ▄▄▀
757
+ ┃ ▄▄▀
758
+ ┃ ▄▄▀
759
+ ┃ ▄▄▀
760
+ ┃ ▄▄▀▀
761
+ ┃▄▄▄▄▄▄▄▀▀▀
762
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
763
+ ```
764
+
765
+ `:linear`
766
+
767
+ ```
768
+ # linear (in)
769
+ ┃ ▄▄▀
770
+ ┃ ▄▄▀▀
771
+ ┃ ▄▄▀▀
772
+ ┃ ▄▄▀▀
773
+ ┃ ▄▄▀▀
774
+ ┃ ▄▄▀▀
775
+ ┃ ▄▄▀▀
776
+ ┃ ▄▄▀▀
777
+ ┃ ▄▄▀▀
778
+ ┃ ▄▄▀▀
779
+ ┃ ▄▄▀▀
780
+ ┃ ▄▄▀▀
781
+ ┃ ▄▄▀▀
782
+ ┃ ▄▄▀▀
783
+ ┃▄▄▀▀
784
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
785
+ ```
786
+
787
+ ```
788
+ # linear (out)
789
+ ┃ ▄▄▀
790
+ ┃ ▄▄▀▀
791
+ ┃ ▄▄▀▀
792
+ ┃ ▄▄▀▀
793
+ ┃ ▄▄▀▀
794
+ ┃ ▄▄▀▀
795
+ ┃ ▄▄▀▀
796
+ ┃ ▄▄▀▀
797
+ ┃ ▄▄▀▀
798
+ ┃ ▄▄▀▀
799
+ ┃ ▄▄▀▀
800
+ ┃ ▄▄▀▀
801
+ ┃ ▄▄▀▀
802
+ ┃ ▄▄▀▀
803
+ ┃▄▄▀▀
804
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
805
+ ```
806
+
807
+ ```
808
+ # linear (in_out)
809
+ ┃ ▄▄▀
810
+ ┃ ▄▄▀▀
811
+ ┃ ▄▄▀▀
812
+ ┃ ▄▄▀▀
813
+ ┃ ▄▄▀▀
814
+ ┃ ▄▄▀▀
815
+ ┃ ▄▄▀▀
816
+ ┃ ▄▄▀▀
817
+ ┃ ▄▄▀▀
818
+ ┃ ▄▄▀▀
819
+ ┃ ▄▄▀▀
820
+ ┃ ▄▄▀▀
821
+ ┃ ▄▄▀▀
822
+ ┃ ▄▄▀▀
823
+ ┃▄▄▀▀
824
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
825
+ ```
826
+
827
+ `:parametric`
828
+
829
+ ```
830
+ # parametric (in)
831
+ ┃ ▄
832
+ ┃ ▄▀
833
+ ┃ ▄▀
834
+ ┃ ▄▄▀
835
+ ┃ ▄▀
836
+ ┃ ▄▀
837
+ ┃ ▄▀
838
+ ┃ ▄▄▀
839
+ ┃ ▄▀
840
+ ┃ ▄▀▀
841
+ ┃ ▄▀▀
842
+ ┃ ▄▄▀▀
843
+ ┃ ▄▄▄▀▀
844
+ ┃ ▄▄▄▄▀▀
845
+ ┃▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀
846
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
847
+ ```
848
+
849
+ ```
850
+ # parametric (out)
851
+ ┃ ▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀
852
+ ┃ ▄▄▀▀▀▀
853
+ ┃ ▄▄▀▀▀
854
+ ┃ ▄▄▀▀
855
+ ┃ ▄▄▀
856
+ ┃ ▄▄▀
857
+ ┃ ▄▀
858
+ ┃ ▄▀▀
859
+ ┃ ▄▀
860
+ ┃ ▄▀
861
+ ┃ ▄▀
862
+ ┃ ▄▀▀
863
+ ┃ ▄▀
864
+ ┃ ▄▀
865
+ ┃▄▀
866
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
867
+ ```
868
+
869
+ ```
870
+ # parametric (in_out)
871
+ ┃ ▄▄▄▀▀▀▀▀▀▀▀▀
872
+ ┃ ▄▄▀▀
873
+ ┃ ▄▀▀
874
+ ┃ ▄▄▀
875
+ ┃ ▄▀
876
+ ┃ ▄▀
877
+ ┃ ▄▀
878
+ ┃ ▄▀
879
+ ┃ ▄▀
880
+ ┃ ▄▀
881
+ ┃ ▄▀
882
+ ┃ ▄▀▀
883
+ ┃ ▄▄▀
884
+ ┃ ▄▄▀▀
885
+ ┃▄▄▄▄▄▄▄▄▄▄▀▀▀
886
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
887
+ ```
888
+
889
+ `:quadratic`
890
+
891
+ ```
892
+ # quadratic (in)
893
+ ┃ ▄▄
894
+ ┃ ▄▀
895
+ ┃ ▄▀
896
+ ┃ ▄▀
897
+ ┃ ▄▀
898
+ ┃ ▄▀▀
899
+ ┃ ▄▀
900
+ ┃ ▄▀▀
901
+ ┃ ▄▀▀
902
+ ┃ ▄▀▀
903
+ ┃ ▄▄▀▀
904
+ ┃ ▄▄▀▀
905
+ ┃ ▄▄▄▀▀
906
+ ┃ ▄▄▄▀▀▀
907
+ ┃▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀
908
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
909
+ ```
910
+
911
+ ```
912
+ # quadratic (out)
913
+ ┃ ▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀
914
+ ┃ ▄▄▄▀▀▀
915
+ ┃ ▄▄▀▀▀
916
+ ┃ ▄▄▀▀
917
+ ┃ ▄▄▀▀
918
+ ┃ ▄▄▀
919
+ ┃ ▄▄▀
920
+ ┃ ▄▄▀
921
+ ┃ ▄▀
922
+ ┃ ▄▄▀
923
+ ┃ ▄▀
924
+ ┃ ▄▀
925
+ ┃ ▄▀
926
+ ┃ ▄▀
927
+ ┃▄▀▀
928
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
929
+ ```
930
+
931
+ ```
932
+ # quadratic (in_out)
933
+ ┃ ▄▄▄▀▀▀▀▀▀▀
934
+ ┃ ▄▄▀▀▀
935
+ ┃ ▄▀▀
936
+ ┃ ▄▀▀
937
+ ┃ ▄▄▀
938
+ ┃ ▄▀
939
+ ┃ ▄▀
940
+ ┃ ▄▀
941
+ ┃ ▄▀
942
+ ┃ ▄▀
943
+ ┃ ▄▀▀
944
+ ┃ ▄▄▀
945
+ ┃ ▄▄▀
946
+ ┃ ▄▄▄▀▀
947
+ ┃▄▄▄▄▄▄▄▄▀▀▀
948
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
949
+ ```
950
+
951
+ ### Full template example
675
952
 
676
953
  ```ruby
677
954
  class TemplateFactory
@@ -840,16 +1117,15 @@ Usage: screen_test [file] [options]
840
1117
  If not file or options are provide, all tests are run
841
1118
 
842
1119
  If no file is provided, the supported options are
843
- --help print this usage statement and exit
844
- --view-pending interactively display and optionally save rendered output for each pending test
845
- --view-failed interactively display and optionally save rendered output for each faiing test
846
-
847
- If the file provide is a screen test, the supported options are
848
- --run run screen test for given file
849
- --view interactively display and optionally save the file's test output
850
- --template print out template tree for the test template
851
- --debug render the test template without displying it, printing out debug information
852
-
1120
+ --help print this usage statement and exit
1121
+ --view-pending interactively display and optionally save rendered output for each pending test
1122
+ --view-failed interactively display and optionally save rendered output for each faiing test
1123
+
1124
+ If a screen test file is provided as the first argument, the supported options are
1125
+ --run run screen test for given file
1126
+ --view interactively display and optionally save the file's test output
1127
+ --template print out template tree for the test template
1128
+ --debug render the test template without displying it, printing out debug information
853
1129
  ```
854
1130
 
855
1131
  ## Contributing
@@ -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,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
@@ -42,7 +42,7 @@ end
42
42
  class Application
43
43
  def start(producer)
44
44
  producer.frameset(5, easing: :bezier) do |fs|
45
- 53.times { |i| fs.add_frame('intro', { top: -i }) }
45
+ 53.times { |i| fs.add_frame('intro', args: { top: -i }) }
46
46
  end
47
47
  producer.add_frame('hold', duration: 1, args: { top: -52})
48
48
  end
@@ -10,7 +10,7 @@ module WhirledPeas
10
10
  @frames = []
11
11
  end
12
12
 
13
- def add_frame(name, args={})
13
+ def add_frame(name, args: {})
14
14
  frames << [name, args]
15
15
  end
16
16
 
@@ -17,7 +17,7 @@ module WhirledPeas
17
17
 
18
18
  def add_frame(name, duration: nil, args: {})
19
19
  frameset(duration || 1 / refresh_rate) do |fs|
20
- fs.add_frame(name, args)
20
+ fs.add_frame(name, args: args)
21
21
  end
22
22
  end
23
23
 
@@ -1,8 +1,10 @@
1
1
  require 'whirled_peas/settings/box_settings'
2
+ require 'whirled_peas/settings/graph_settings'
2
3
  require 'whirled_peas/settings/grid_settings'
3
4
  require 'whirled_peas/settings/text_settings'
4
5
 
5
6
  require_relative 'box_painter'
7
+ require_relative 'graph_painter'
6
8
  require_relative 'grid_painter'
7
9
  require_relative 'text_painter'
8
10
 
@@ -45,6 +47,7 @@ module WhirledPeas
45
47
  child = TextPainter.new(name, child_settings)
46
48
  # TextPainters are not composable, so yield nil
47
49
  content = yield nil, child_settings
50
+ child_settings.validate!
48
51
  unless self.class.stringable?(content)
49
52
  raise ArgumentError, "Unsupported type for text: #{content.class}"
50
53
  end
@@ -52,11 +55,25 @@ module WhirledPeas
52
55
  painter.add_child(child)
53
56
  end
54
57
 
58
+ def add_graph(name=self.class.next_name, &block)
59
+ child_settings = Settings::GraphSettings.inherit(painter.settings)
60
+ child = GraphPainter.new(name, child_settings)
61
+ # GraphPainters are not composable, so yield nil
62
+ content = yield nil, child_settings
63
+ child_settings.validate!
64
+ unless content.is_a?(Array) && content.length > 0
65
+ raise ArgumentError, 'Graphs require a non-empty array as the content'
66
+ end
67
+ child.content = content
68
+ painter.add_child(child)
69
+ end
70
+
55
71
  def add_box(name=self.class.next_name, &block)
56
72
  child_settings = Settings::BoxSettings.inherit(painter.settings)
57
73
  child = BoxPainter.new(name, child_settings)
58
74
  composer = self.class.new(child)
59
75
  value = yield composer, child.settings
76
+ child_settings.validate!
60
77
  painter.add_child(child)
61
78
  if !child.children? && self.class.stringable?(value)
62
79
  composer.add_text("#{name}-Text") { value.to_s }
@@ -68,6 +85,7 @@ module WhirledPeas
68
85
  child = GridPainter.new(name, child_settings)
69
86
  composer = self.class.new(child)
70
87
  values = yield composer, child.settings
88
+ child_settings.validate!
71
89
  painter.add_child(child)
72
90
  if !child.children? && values.is_a?(Array)
73
91
  values.each.with_index do |value, index|
@@ -0,0 +1,19 @@
1
+ module WhirledPeas
2
+ module Graphics
3
+ class ContentDimensions
4
+ attr_reader :outer_width, :outer_height
5
+
6
+ def initialize(settings, content)
7
+ if settings.width
8
+ @outer_width = settings.width
9
+ else
10
+ @outer_width = 0
11
+ content.each do |line|
12
+ @outer_width = line.length if line.length > @outer_width
13
+ end
14
+ end
15
+ @outer_height = settings.height || content.length
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ require_relative 'painter'
2
+
3
+ module WhirledPeas
4
+ module Graphics
5
+ class ContentPainter < Painter
6
+ attr_accessor :content
7
+
8
+ def dimensions
9
+ ContentDimensions.new(settings, content_lines)
10
+ end
11
+
12
+ private
13
+
14
+ def content_lines
15
+ raise NotImplementedError, "#{self.class} must implement #content_lines"
16
+ end
17
+ end
18
+ private_constant :ContentPainter
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ module WhirledPeas
2
+ module Graphics
3
+ class TextDimensions
4
+ attr_reader :outer_width, :outer_height
5
+
6
+ def initialize(settings, content)
7
+ @outer_width = settings.width
8
+ @outer_height = settings.height
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,95 @@
1
+ require 'whirled_peas/utils/ansi'
2
+
3
+ require_relative 'content_painter'
4
+
5
+ module WhirledPeas
6
+ module Graphics
7
+ class GraphPainter < ContentPainter
8
+ def paint(canvas, left, top, &block)
9
+ axis_formatting = [*settings.axis_color, *settings.bg_color]
10
+ plot_formatting = [*settings.color, *settings.bg_color]
11
+ axes_lines.each.with_index do |axis_line, row_index|
12
+ canvas.stroke(left, top + row_index, axis_line, axis_formatting, &block)
13
+ next if row_index >= plot_lines.length
14
+ canvas.stroke(left + 1, top + row_index, plot_lines[row_index], plot_formatting, &block)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def content_lines
21
+ axes_lines
22
+ end
23
+
24
+ def plot_lines
25
+ return @plot_lines if @plot_lines
26
+ min_y = 1.0 / 0
27
+ max_y = -1.0 / 0
28
+ if settings.width
29
+ interpolated = inner_width.times.map do |i|
30
+ x = (i * (content.length - 1).to_f / inner_width).floor
31
+ max_y = content[x] if content[x] > max_y
32
+ min_y = content[x] if content[x] < min_y
33
+ content[x]
34
+ end
35
+ else
36
+ interpolated = content
37
+ content.each do |y|
38
+ max_y = y if y > max_y
39
+ min_y = y if y < min_y
40
+ end
41
+ end
42
+ scaled = interpolated.map do |y|
43
+ (2 * inner_height * (y - min_y).to_f / (max_y - min_y)).floor
44
+ end
45
+ @plot_lines = Array.new(inner_height) { '' }
46
+ scaled.each.with_index do |y, x_index|
47
+ @plot_lines.each.with_index do |row, row_index|
48
+ y_index = inner_height - row_index - 1
49
+ asc, next_y = if scaled.length == 1
50
+ [true, y]
51
+ elsif x_index == scaled.length - 1
52
+ y >= scaled[x_index - 1]
53
+ [true, y]
54
+ else
55
+ scaled[x_index + 1] >= y
56
+ [true, scaled[x_index + 1]]
57
+ end
58
+ if asc
59
+ top_half = (y...next_y).include?(2 * y_index + 1)
60
+ bottom_half = (y...next_y).include?(2 * y_index)
61
+ else
62
+ top_half = (next_y...y).include?(2 * y_index + 1)
63
+ bottom_half = (next_y...y).include?(2 * y_index)
64
+ end
65
+ row << if top_half && bottom_half
66
+ '█'
67
+ elsif top_half
68
+ '▀'
69
+ elsif bottom_half
70
+ '▄'
71
+ else
72
+ ' '
73
+ end
74
+ end
75
+ end
76
+ @plot_lines
77
+ end
78
+
79
+ def inner_height
80
+ settings.height - 1
81
+ end
82
+
83
+ def inner_width
84
+ settings.width.nil? ? content.length : settings.width - 1
85
+ end
86
+
87
+ def axes_lines
88
+ return @axes_lines if @axes_lines
89
+ @axes_lines = inner_height.times.map { '┃' }
90
+ @axes_lines << '┗' + '━' * inner_width
91
+ @axes_lines
92
+ end
93
+ end
94
+ end
95
+ end
@@ -28,9 +28,6 @@ module WhirledPeas
28
28
 
29
29
  def dimensions
30
30
  @dimensions ||= begin
31
- if settings.num_cols.nil? || settings.num_cols == 0
32
- raise SettingsError, "`num_cols` must be set for GridSettings(#{name})"
33
- end
34
31
  num_cols = settings.num_cols
35
32
  num_rows = (num_children.to_f / num_cols).ceil
36
33
  content_width = 0
@@ -1,14 +1,12 @@
1
1
  require 'whirled_peas/utils/formatted_string'
2
2
  require 'whirled_peas/utils/title_font'
3
3
 
4
- require_relative 'painter'
5
- require_relative 'text_dimensions'
4
+ require_relative 'content_dimensions'
5
+ require_relative 'content_painter'
6
6
 
7
7
  module WhirledPeas
8
8
  module Graphics
9
- class TextPainter < Painter
10
- attr_reader :content
11
-
9
+ class TextPainter < ContentPainter
12
10
  def paint(canvas, left, top, &block)
13
11
  return unless canvas.writable?
14
12
  formatting = [*settings.color, *settings.bg_color]
@@ -16,17 +14,15 @@ module WhirledPeas
16
14
  if settings.underline? && settings.title_font.nil?
17
15
  formatting << Utils::Ansi::UNDERLINE
18
16
  end
19
- content.each.with_index do |line, index|
17
+ content_lines.each.with_index do |line, index|
20
18
  canvas.stroke(left, top + index, line, formatting, &block)
21
19
  end
22
20
  end
23
21
 
24
- def dimensions
25
- TextDimensions.new(content)
26
- end
22
+ private
27
23
 
28
- def content=(content)
29
- @content = if settings.title_font
24
+ def content_lines
25
+ @content_lines = if settings.title_font
30
26
  Utils::TitleFont.to_s(
31
27
  content, settings.title_font
32
28
  ).split("\n")
@@ -12,8 +12,6 @@ require_relative 'vert_alignment'
12
12
  module WhirledPeas
13
13
  module Settings
14
14
  class ContainerSettings < ElementSettings
15
- attr_accessor :width, :height
16
-
17
15
  def align
18
16
  @_align || Alignment::DEFAULT
19
17
  end
@@ -13,6 +13,11 @@ module WhirledPeas
13
13
  child
14
14
  end
15
15
 
16
+ attr_accessor :width, :height
17
+
18
+ def validate!
19
+ end
20
+
16
21
  def bg_color
17
22
  @_bg_color || BgColor::DEFAULT
18
23
  end
@@ -0,0 +1,21 @@
1
+ require_relative 'text_color'
2
+ require_relative 'element_settings'
3
+
4
+ module WhirledPeas
5
+ module Settings
6
+ class GraphSettings < ElementSettings
7
+ def axis_color
8
+ @_axis_color || color
9
+ end
10
+
11
+ def axis_color=(color)
12
+ @_axis_color = TextColor.validate!(color)
13
+ end
14
+
15
+ def validate!
16
+ super
17
+ raise SettingsError, "`height` must be set for GraphSettings" if height.nil?
18
+ end
19
+ end
20
+ end
21
+ end
@@ -7,6 +7,13 @@ module WhirledPeas
7
7
  class GridSettings < ContainerSettings
8
8
  attr_accessor :num_cols
9
9
 
10
+ def validate!
11
+ super
12
+ if num_cols.nil? || num_cols <= 0
13
+ raise SettingsError, "`num_cols` must be set to a positive number for GridSettings"
14
+ end
15
+ end
16
+
10
17
  def set_scrollbar(*)
11
18
  raise NotImplementedError, 'Grids do not support scrollbars'
12
19
  end
@@ -28,7 +28,7 @@ module WhirledPeas
28
28
  BRIGHT_OFFSET = 60
29
29
 
30
30
  class << self
31
- def with_screen(output, width: nil, height: nil, &block)
31
+ def with_screen(output=STDOUT, width: nil, height: nil, &block)
32
32
  require 'highline'
33
33
  unless width && height
34
34
  width, height = HighLine.new.terminal.terminal_size
@@ -1,3 +1,3 @@
1
1
  module WhirledPeas
2
- VERSION = '0.8.0'
2
+ VERSION = '0.9.0'
3
3
  end
@@ -0,0 +1 @@
1
+ [?25l┃ █ ┃ █ ┃ ▄▀ ┃ █ ┃ ▄▀ ┃ █ ┃ ▄▀ ┃ █ ┃ █ ┃ █ ┃ ▄▀ ┗━━━━━━━━━━[?25h
@@ -0,0 +1,12 @@
1
+ require 'whirled_peas'
2
+
3
+ class TemplateFactory
4
+ def build(*)
5
+ WhirledPeas.template do |composer|
6
+ composer.add_graph('Graph') do |_, settings|
7
+ settings.height = 12
8
+ 10.times.map { |i| i ** 2 }
9
+ end
10
+ end
11
+ end
12
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whirled_peas
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Collier
@@ -70,9 +70,11 @@ files:
70
70
  - README.md
71
71
  - Rakefile
72
72
  - bin/console
73
+ - bin/easing
73
74
  - bin/reset_cursor
74
75
  - bin/screen_test
75
76
  - bin/setup
77
+ - examples/graph.rb
76
78
  - examples/intro.rb
77
79
  - examples/scrolling.rb
78
80
  - exe/whirled_peas
@@ -109,12 +111,15 @@ files:
109
111
  - lib/whirled_peas/graphics/container_coords.rb
110
112
  - lib/whirled_peas/graphics/container_dimensions.rb
111
113
  - lib/whirled_peas/graphics/container_painter.rb
114
+ - lib/whirled_peas/graphics/content_dimensions.rb
115
+ - lib/whirled_peas/graphics/content_painter.rb
112
116
  - lib/whirled_peas/graphics/debugger.rb
117
+ - lib/whirled_peas/graphics/graph_dimensions.rb
118
+ - lib/whirled_peas/graphics/graph_painter.rb
113
119
  - lib/whirled_peas/graphics/grid_painter.rb
114
120
  - lib/whirled_peas/graphics/mock_screen.rb
115
121
  - lib/whirled_peas/graphics/painter.rb
116
122
  - lib/whirled_peas/graphics/renderer.rb
117
- - lib/whirled_peas/graphics/text_dimensions.rb
118
123
  - lib/whirled_peas/graphics/text_painter.rb
119
124
  - lib/whirled_peas/null_logger.rb
120
125
  - lib/whirled_peas/settings.rb
@@ -127,6 +132,7 @@ files:
127
132
  - lib/whirled_peas/settings/debugger.rb
128
133
  - lib/whirled_peas/settings/display_flow.rb
129
134
  - lib/whirled_peas/settings/element_settings.rb
135
+ - lib/whirled_peas/settings/graph_settings.rb
130
136
  - lib/whirled_peas/settings/grid_settings.rb
131
137
  - lib/whirled_peas/settings/margin.rb
132
138
  - lib/whirled_peas/settings/padding.rb
@@ -145,6 +151,8 @@ files:
145
151
  - lib/whirled_peas/version.rb
146
152
  - screen_test/elements/box.frame
147
153
  - screen_test/elements/box.rb
154
+ - screen_test/elements/graph.frame
155
+ - screen_test/elements/graph.rb
148
156
  - screen_test/elements/grid.frame
149
157
  - screen_test/elements/grid.rb
150
158
  - screen_test/elements/screen_overflow_x.frame
@@ -1,15 +0,0 @@
1
- module WhirledPeas
2
- module Graphics
3
- class TextDimensions
4
- attr_reader :outer_width, :outer_height
5
-
6
- def initialize(content)
7
- @outer_width = 0
8
- content.each do |line|
9
- @outer_width = line.length if line.length > @outer_width
10
- end
11
- @outer_height = content.length
12
- end
13
- end
14
- end
15
- end