vamp 0.1.3 → 0.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ed0c28b4a5fa22a09ccafc6674d51076fb75816
4
- data.tar.gz: 931968ca87c4c1cf6c4c5a4b8865625bece04ec0
3
+ metadata.gz: 4c4315d0432b085681e7afa71522bfc0fe3e04f3
4
+ data.tar.gz: 591b310d5939ddfa3070a867b54f8c603092e72f
5
5
  SHA512:
6
- metadata.gz: b196797bc06e8c60be048e59a4a9a11bcbf8fdece181c07d3e0832afd61616f79aee6caa725380f58b4be0bc61a6f04c72dde40dd5a8cfd33debf0c5fb1db91f
7
- data.tar.gz: e122c443482653ceaa831f2dad9a6d84a010dd2040e710c89b159b6a6bbc5803d4ffb0738ed2fec214c9fb8da6b1cba24a307f03b3cd2bb998f0415310d6605b
6
+ metadata.gz: 16383a97ea438b01e28dc035290fc1d5ed58952f516a70df23a702088a2d323c138b475e5dd91a5e7653ba57b6d57d241e49ceac634e794f9f651a2df48868b2
7
+ data.tar.gz: f831bae4c7f817ac9fc073fdefd45ff0f8a5dd48bc868709c0cb4a634d4ee91e37ce0b141ba1ab3dc10ba8f1ada633aca1f6e173db5ced106dfcbabaa51f3614
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  /.idea/
3
3
  /.yardoc
4
4
  /Gemfile.lock
5
+ /*.gem
5
6
  /_yardoc/
6
7
  /coverage/
7
8
  /doc/
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # Vamp - necessities for the elegant vampire
2
2
 
3
3
  Want to pimp up your command line interface?
4
- Just require this gem insert some code and your CLI makes witty vampire quotes.
4
+ Just require this gem, insert some code and your CLI makes witty vampire quotes.
5
+ You can even play animated ascii art in a console window.
5
6
 
6
7
  ## Installation
7
8
 
@@ -19,10 +20,9 @@ Or install it yourself as:
19
20
 
20
21
  $ gem install vamp
21
22
 
22
- ## Usage
23
+ ## Usage with thor
23
24
 
24
25
  ```ruby
25
-
26
26
  require "thor"
27
27
  require "vamp"
28
28
 
@@ -53,6 +53,23 @@ end
53
53
  CLI.start(ARGV)
54
54
  ```
55
55
 
56
+ ## Animation Usage
57
+
58
+ You can play an ascii art animation within a terminal window by using vamp.
59
+
60
+ ```ruby
61
+ require "vamp"
62
+
63
+ animator = Vamp::Animator.new(File.join(Gem.loaded_specs["vamp"].gem_dir, "files", "vampire.txt"), 31, 0, 24,
64
+ "No man knows till he has suffered from the night how sweet and how dear to his heart and eye the morning can be.")
65
+ animator.play
66
+ ```
67
+
68
+ To see the animation in higher resolution just click on the following image.
69
+
70
+ [![Animation Demo](https://raw.githubusercontent.com/m-31/vamp/data/pic/demo_001.gif)](https://raw.githubusercontent.com/m-31/vamp/data/pic/demo_001.mp4)
71
+
72
+
56
73
  ## Development
57
74
 
58
75
  After checking out the repo, run `bundle install` to install dependencies.
data/lib/vamp/art.rb ADDED
@@ -0,0 +1,189 @@
1
+ module Vamp
2
+ module Art
3
+
4
+ def self.remove_ansi(text)
5
+ text.gsub(/[\[\d;]+m/, '')
6
+ end
7
+
8
+ # ascii art by the queen of ascii art: Joan Stark (jgs)
9
+ # https://en.wikipedia.org/wiki/Joan_Stark
10
+ VAMPIRE = <<-'END'
11
+ =/\ /\=
12
+ / \'._ (\_/) _.'/ \
13
+ / .''._'--(o.o)--'_.''. \
14
+ /.' _/ |`'=/ " \='`| \_ `.\
15
+ /` .' `\;-,'\___/',-;/` '. '\
16
+ /.-' jgs `\(-V-)/` `-.\
17
+ ` " " `
18
+ END
19
+ HOBGOBLIN_COLOR = <<-'END'
20
+  , _.._ ,
21
+ (`._."` `"._.')
22
+ '._ _.' /\
23
+ | /`-. .-'\ | __ .'.'
24
+ |(_()_\/_()_)|' `\ ( (
25
+ ; ,____, ; \ ) )
26
+ \ /VvvV\ / \ \.__ / /
27
+ /`'._`""`_.' \ \ `\/ /
28
+ / . `--' \ \ /
29
+ / / `-, _.----' \ ;
30
+ / / ) / .--------` \
31
+ / /.----' / / ___. \
32
+ / /| _ _,| (---' \ |
33
+ / / | \`""` \\\\ \ |
34
+ / /` | | \\\` \ \
35
+ / / ; | / /
36
+ / / _ \ / /` /`
37
+ / _\/( | | / .'_
38
+ | ( \ '--' \ .' (__)`\
39
+ \\\\ `-------' jgs /________.'
40
+ `\\\
41
+ 
42
+ END
43
+ HOBGOBLIN = remove_ansi(HOBGOBLIN_COLOR)
44
+ SHARK_COLOR = <<-'END'
45
+  o
46
+  ___.. ,
47
+  __..--''__ ( .';
48
+  o __.-------.-' `--..__ .' ;
49
+  _.--' 0) .--._ ``--...____.' .'
50
+  ( _. )). .__.-'' <
51
+  `````---....._____.....- -..___ _____...--'-.'.
52
+  jgs `-.___.' ``````` `.;
53
+ 
54
+ END
55
+ SHARK = remove_ansi(SHARK_COLOR)
56
+ DRAGON_COLOR = <<-'END'
57
+   ___ ___
58
+  ,_;-'-._\__/_.-'
59
+  ,_\ a\/a
60
+  ,_\ ,-( _'-._
61
+ ,_\ (==/\ |'-._\
62
+  ,_\ -. /\ \/ | |\ `
63
+  ,_\ \/==\ '._\7 '._
64
+ jgs '. '._ \=/\'-}}} '-}}}
65
+  / /> /` )
66
+ |\_.' .'( <_ <_
67
+ \____.-' '--}}}-}}}
68
+ 
69
+ END
70
+ DRAGON = remove_ansi(DRAGON_COLOR)
71
+
72
+ # ascii art by Daniel Au (dcau)
73
+ # http://www.oocities.org/SoHo/7373/dcau.htm
74
+ SKULL_COLOR = <<-'END'
75
+  _,.-------.,_
76
+ ,;~' '~;,
77
+ ,; ;,
78
+ ; ;
79
+ ,' ',
80
+ ,; ;,
81
+ ; ; . . ; ;
82
+ | ; ______ ______ ; |
83
+ | `/~" ~" . "~ "~\' |
84
+ | ~ ,-~~~^~, | ,~^~~~-, ~ |
85
+ | | }:{ | |
86
+ | l / | \ ! |
87
+ .~ (__,.--" .^. "--.,__) ~.
88
+ | ---;' / | \ `;--- |
89
+ \__. \/^\/ .__/
90
+ V| \ / |V
91
+ | |T~\___!___!___/~T| |
92
+ | |`IIII_I_I_I_IIII'| |
93
+ | \,III I I I III,/ |
94
+ \ `~~~~~~~~~~' /
95
+ \ . . / -dcau
96
+ \. ^ ./
97
+ ^~~~^~~~^
98
+ 
99
+ END
100
+ SKULL = remove_ansi(SKULL_COLOR)
101
+
102
+ # acii art by Andreas Freise (a:f)
103
+ # http://www.ascii-art.de/
104
+ LIZARD_COLOR = <<-'END'
105
+  )/_
106
+ _.--..---"-,--c_
107
+ \L..' ._O__)_
108
+ ,-. _.+ _ \..--( / a:f
109
+ `\.-''__.-' \ ( \_
110
+ `''' `\__ /\
111
+ ')
112
+ 
113
+ END
114
+ LIZARD = remove_ansi(LIZARD_COLOR)
115
+
116
+ # ascii art by Nico Okha (mOm)
117
+ RUNNING = <<-'END'
118
+ _
119
+ _( }
120
+ /_ )
121
+ / "/
122
+ / -;\
123
+ ;--' ´ /'
124
+ <_
125
+ END
126
+ DECLARING = <<-'END'
127
+ ,
128
+ (}_/
129
+ /,;
130
+ "^/ \
131
+ \|/
132
+ //
133
+ ""`
134
+ END
135
+
136
+ DECLARING2 = <<-'END'
137
+ !
138
+ ||
139
+ !|
140
+ | \ _
141
+ | \ /~ ~\
142
+ \ \_ /<- ->\
143
+ \ \_\.O.|\\
144
+ \ ' /__"
145
+ | \
146
+ \ ' . \ \
147
+ \ /) >
148
+ \ , | /
149
+ | \__/ <
150
+ > /o |)
151
+ / T /
152
+ / | /
153
+ / | . /
154
+ \/ \/ \
155
+ \ \ \
156
+ \ \ \
157
+ \ > \
158
+ /^ / ^\
159
+ ^^^~ ~~~mOm
160
+ END
161
+ GREEK = <<-'END'
162
+ ___
163
+ /. `\
164
+ / , ` |
165
+ /^\__/'
166
+ _,--~, /_
167
+ /' ~~~ -__ ~-,
168
+ ,/ , _ \
169
+ / |`-_ _ '_/%\ `\
170
+ / |_, ~~ __/`\ \
171
+ ,--~~"' / \ `--/ |, >
172
+ //^|~~---' ; , , | '
173
+ ,'` , ( | /
174
+ ,' `;;;,' \ ,' |
175
+ / 77); `./||`
176
+ / ,"^ \, ,
177
+ / / \ ,
178
+ ( ,' \ |
179
+ \ `. \_- \
180
+ \ ; \ `,
181
+ ` ' \ |
182
+ \ ` \ `
183
+ | ) `, `.
184
+ / ' ,' )
185
+ mooO' Ooom' mOm
186
+
187
+ END
188
+ end
189
+ end
@@ -0,0 +1,142 @@
1
+ require "forwardable"
2
+ require_relative "dotter"
3
+ require_relative "text_dotter"
4
+ require_relative "transfer"
5
+ require_relative "transfer5"
6
+
7
+ module Vamp
8
+ module Graphic
9
+ # Graphic Context
10
+ class Context
11
+ extend Forwardable
12
+
13
+ def_delegators :@dotter, :dot, :dot?, :undot, :in?, :clear, :screen, :width, :height
14
+
15
+ attr_reader :dotter # can set a dot within [0, width] [0, height[]]
16
+
17
+ def initialize(dotter)
18
+ @dotter = dotter
19
+ end
20
+
21
+ # Cohen–Sutherland clipping algorithm clips a line from
22
+ # P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with
23
+ # diagonal from (xmin, ymin) to (xmax, ymax).
24
+ def line(x0, y0, x1, y1)
25
+ xmin = 0.0
26
+ ymin = 0.0
27
+ xmax = width - 1.0
28
+ ymax = height - 1.0
29
+
30
+ # compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
31
+ outcode0 = compute_out_code(x0, y0)
32
+ outcode1 = compute_out_code(x1, y1)
33
+
34
+ accept = false
35
+
36
+ while true
37
+ if 0 == (outcode0 | outcode1) # Bitwise OR is 0. Trivially accept and get out of loop
38
+ accept = true
39
+ break
40
+ elsif 0 != (outcode0 & outcode1) # Bitwise AND is not 0. Trivially reject and get out of loop
41
+ break
42
+ else
43
+ # failed both tests, so calculate the line segment to clip
44
+ # from an outside point to an intersection with clip edge
45
+
46
+ # At least one endpoint is outside the clip rectangle; pick it.
47
+ outcodeOut = outcode0 != 0 ? outcode0 : outcode1
48
+
49
+
50
+ # Now find the intersection point;
51
+ # use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
52
+ if 0 != (outcodeOut & TOP) # point is above the clip rectangle
53
+ x = x0.to_f + (x1 - x0) * (ymax - y0) / (y1 - y0)
54
+ y = ymax
55
+ elsif (0 != outcodeOut & BOTTOM) # point is below the clip rectangle
56
+ x = x0.to_f + (x1 - x0) * (ymin - y0) / (y1 - y0)
57
+ y = ymin
58
+ elsif (0 != outcodeOut & RIGHT) # point is to the right of clip rectangle
59
+ y = y0.to_f + (y1 - y0) * (xmax - x0) / (x1 - x0)
60
+ x = xmax
61
+ elsif (0 != outcodeOut & LEFT) # point is to the left of clip rectangle
62
+ y = y0.to_f + (y1 - y0) * (xmin - x0) / (x1 - x0)
63
+ x = xmin
64
+ end
65
+
66
+ # Now we move outside point to intersection point to clip
67
+ # and get ready for next pass.
68
+ if outcodeOut == outcode0
69
+ x0 = x
70
+ y0 = y
71
+ outcode0 = compute_out_code(x0, y0)
72
+ else
73
+ x1 = x
74
+ y1 = y
75
+ outcode1 = compute_out_code(x1, y1)
76
+ end
77
+ end
78
+ end
79
+ if accept
80
+ draw_line_direct (x0 + 0.5).to_i, (y0 + 0.5).to_i, (x1 + 0.5).to_i, (y1 + 0.5).to_i
81
+ end
82
+ self
83
+ end
84
+
85
+ # protected
86
+
87
+ # Bresenham's line algorithm
88
+ def draw_line_direct(x0, y0, x1, y1)
89
+ dx = (x1 - x0).abs
90
+ sx = x0 < x1 ? 1 : -1
91
+ dy = -(y1 - y0).abs
92
+ sy = y0 < y1 ? 1 : -1
93
+ err = dx + dy
94
+ while true
95
+ dot(x0, y0)
96
+ break if x0 == x1 && y0 == y1
97
+ e2 = 2 * err
98
+ if e2 > dy
99
+ err += dy
100
+ x0 += sx
101
+ end
102
+ if e2 < dx
103
+ err += dx
104
+ y0 += sy
105
+ end
106
+ end
107
+ self
108
+ end
109
+
110
+ private
111
+
112
+ # clipping area constants
113
+ INSIDE = 0; # 0000
114
+ LEFT = 1; # 0001
115
+ RIGHT = 2; # 0010
116
+ BOTTOM = 4; # 0100
117
+ TOP = 8; # 1000
118
+
119
+ # Compute the bit code for a point (x, y) using the clip rectangle
120
+ # bounded diagonally by (xmin, ymin), and (xmax, ymax)
121
+ def compute_out_code(x, y)
122
+ xmin = 0
123
+ ymin = 0
124
+ xmax = width - 1
125
+ ymax = height - 1
126
+ code = INSIDE # initialised as being inside of clip window
127
+
128
+ if x < xmin # to the left of clip window
129
+ code |= LEFT
130
+ elsif x > xmax # to the right of clip window
131
+ code |= RIGHT
132
+ end
133
+ if y < ymin # below the clip window
134
+ code |= BOTTOM
135
+ elsif y > ymax # above the clip window
136
+ code |= TOP
137
+ end
138
+ code
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,53 @@
1
+ module Vamp
2
+ module Graphic
3
+ # basis class for graphic implementations
4
+ # you have to implement :dot, :undot, :clear and :screen
5
+ class Dotter
6
+ attr_reader :width # number of dots in x direction
7
+ attr_reader :height # number of dots in y direction
8
+
9
+ def initialize(width, height)
10
+ @width = width
11
+ @height = height
12
+ clear
13
+ end
14
+
15
+ # put dot at x, ys
16
+ def dot(x, y)
17
+ check(x, y)
18
+ self
19
+ end
20
+
21
+ # is there a dot at x, y
22
+ def dot?(x, y)
23
+ check(x, y)
24
+ end
25
+
26
+ # remove dot at x, y
27
+ def undot(x, y)
28
+ check(x, y)
29
+ self
30
+ end
31
+
32
+ # clear screen of all dots
33
+ def clear
34
+ self
35
+ end
36
+
37
+ # return complete screen as string representation
38
+ def screen
39
+ end
40
+
41
+ # check if (x, y) is on the screen
42
+ def in?(x, y)
43
+ (0...width) === x && (0...height) === y
44
+ end
45
+
46
+ # check if (x, y) is on the screen, fails if not
47
+ def check(x, y)
48
+ fail "for (#{x}, #{y}}: x not in [0, #{width}[" unless (0...width) === x
49
+ fail "for (#{x}, #{y}}: y not in [0, #{height}[" unless (0...height) === y
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,35 @@
1
+ require_relative "dotter"
2
+
3
+ module Vamp
4
+ module Graphic
5
+ # simple sample implementation for a graphic basic implementation
6
+ class TextDotter < Dotter
7
+ def dot(x, y)
8
+ super
9
+ @data[y][x] = "X"
10
+ self
11
+ end
12
+
13
+ def dot?(x, y)
14
+ super
15
+ @data[y][x] == "X"
16
+ end
17
+
18
+ def undot(x, y)
19
+ super
20
+ @data[y][x] = " "
21
+ self
22
+ end
23
+
24
+ def clear
25
+ @data = Array.new(height) { Array.new(width, " ") }
26
+ self
27
+ end
28
+
29
+ def screen
30
+ line = "+" + "-" * width + "+\n"
31
+ line + @data.map { |x| "|#{x.join('')}|" }.join("\n").to_s + "\n" + line
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,170 @@
1
+ module Vamp
2
+ module Graphic
3
+ # Transfer dotter data into ASCII
4
+ class Transfer
5
+
6
+ attr_reader :char_width
7
+ attr_reader :char_height
8
+ attr_reader :context
9
+ attr_reader :mapping
10
+
11
+ SPACE = <<-'END'
12
+ ___
13
+ ___
14
+ ___
15
+ END
16
+
17
+ SLASH = <<-'END'
18
+ __X
19
+ _X_
20
+ X__
21
+ END
22
+
23
+ BACKSLASH = <<-'END'
24
+ X__
25
+ _X_
26
+ __X
27
+ END
28
+
29
+ BACKTICK = <<-'END'
30
+ X__
31
+ ___
32
+ ___
33
+ END
34
+
35
+ PIPE = <<-'END'
36
+ _X_
37
+ _X_
38
+ _X_
39
+ END
40
+
41
+ MINUS = <<-'END'
42
+ ___
43
+ XXX
44
+ ___
45
+ END
46
+
47
+ UNDERSCORE = <<-'END'
48
+ ___
49
+ ___
50
+ XXX
51
+ END
52
+
53
+ FULLSTOP = <<-'END'
54
+ ___
55
+ ___
56
+ _X_
57
+ END
58
+
59
+ DOUBLEQUOTES = <<-'END'
60
+ X_X
61
+ ___
62
+ ___
63
+ END
64
+
65
+ SINGLEQUOTE = <<-'END'
66
+ _X_
67
+ ___
68
+ ___
69
+ END
70
+
71
+ STAR = <<-'END'
72
+ _X_
73
+ XXX
74
+ _X_
75
+ END
76
+
77
+ HASH = <<-'END'
78
+ XXX
79
+ XXX
80
+ XXX
81
+ END
82
+
83
+ def initialize(context)
84
+ @context = context
85
+ @char_width = 3
86
+ @char_height = 3
87
+ @mapping = {
88
+ " " => create_pattern(SPACE),
89
+ "/" => create_pattern(SLASH),
90
+ "\\" => create_pattern(BACKSLASH),
91
+ "`" => create_pattern(BACKTICK),
92
+ "|" => create_pattern(PIPE),
93
+ "-" => create_pattern(MINUS),
94
+ "_" => create_pattern(UNDERSCORE),
95
+ "." => create_pattern(FULLSTOP),
96
+ "\"" => create_pattern(DOUBLEQUOTES),
97
+ "'" => create_pattern(SINGLEQUOTE),
98
+ "*" => create_pattern(STAR),
99
+ "\#" => create_pattern(HASH),
100
+ }
101
+ end
102
+
103
+ def create_data(pattern)
104
+ a = pattern.split("\n")
105
+ fail "pattern has wrong height" if a.size != char_height
106
+ char_height.times do |dy|
107
+ fail "pattern has wrong width" if a[dy].size != char_width
108
+ end
109
+ a
110
+ end
111
+
112
+ def create_pattern(pattern)
113
+ char = TextDotter.new(char_width, char_height)
114
+ a = create_data(pattern)
115
+ char_height.times do |y|
116
+ char_width.times do |x|
117
+ char.dot(x, y) if a[y][x] == "X"
118
+ end
119
+ end
120
+ char
121
+ end
122
+
123
+ def get_pattern(x, y)
124
+ pattern = ""
125
+ char_height.times do |dy|
126
+ char_width.times do |dx|
127
+ if context.in?(x + dx, y + dy)
128
+ pattern += (context.dot?(x + dx, y + dy) ? "X" : "_")
129
+ else
130
+ pattern += "_"
131
+ end
132
+ end
133
+ pattern += "\n"
134
+ end
135
+ create_pattern(pattern.strip)
136
+ end
137
+
138
+ def difference(pattern1, pattern2)
139
+ m = 0
140
+ char_width.times do |dx|
141
+ char_height.times do |dy|
142
+ m += 1 if pattern1.dot?(dx, dy) != pattern2.dot?(dx, dy)
143
+ end
144
+ end
145
+ m
146
+ end
147
+
148
+ def get_matching(pattern)
149
+ ranking = {}
150
+ mapping.each do |k, v|
151
+ r = difference(pattern, v)
152
+ ranking[k] = r
153
+ end
154
+ ranking.min_by{|k, v| v}[0]
155
+ end
156
+
157
+
158
+ def ascii
159
+ a = ""
160
+ (context.height / char_height).times do |y|
161
+ (context.width / char_width).times do |x|
162
+ a += get_matching(get_pattern(x * char_width, y * char_height))
163
+ end
164
+ a += "\n"
165
+ end
166
+ a.chomp
167
+ end
168
+ end
169
+ end
170
+ end