ascii_paint 0.2.1
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 +7 -0
- data/data/block_characters/0.txt +5 -0
- data/data/block_characters/1.txt +5 -0
- data/data/block_characters/2.txt +5 -0
- data/data/block_characters/3.txt +5 -0
- data/data/block_characters/4.txt +5 -0
- data/data/block_characters/5.txt +5 -0
- data/data/block_characters/6.txt +5 -0
- data/data/block_characters/7.txt +5 -0
- data/data/block_characters/8.txt +5 -0
- data/data/block_characters/9.txt +5 -0
- data/data/block_characters/A.txt +5 -0
- data/data/block_characters/B.txt +5 -0
- data/data/block_characters/C.txt +5 -0
- data/data/block_characters/D.txt +5 -0
- data/data/block_characters/E.txt +5 -0
- data/data/block_characters/F.txt +5 -0
- data/data/block_characters/G.txt +5 -0
- data/data/block_characters/H.txt +5 -0
- data/data/block_characters/I.txt +5 -0
- data/data/block_characters/J.txt +5 -0
- data/data/block_characters/K.txt +5 -0
- data/data/block_characters/L.txt +5 -0
- data/data/block_characters/M.txt +5 -0
- data/data/block_characters/N.txt +5 -0
- data/data/block_characters/O.txt +5 -0
- data/data/block_characters/P.txt +5 -0
- data/data/block_characters/Q.txt +5 -0
- data/data/block_characters/R.txt +5 -0
- data/data/block_characters/S.txt +5 -0
- data/data/block_characters/T.txt +5 -0
- data/data/block_characters/U.txt +5 -0
- data/data/block_characters/V.txt +5 -0
- data/data/block_characters/W.txt +5 -0
- data/data/block_characters/X.txt +5 -0
- data/data/block_characters/Y.txt +5 -0
- data/data/block_characters/Z.txt +5 -0
- data/lib/ascii_paint.rb +113 -0
- data/lib/ascii_paint/block_character.rb +85 -0
- data/lib/ascii_paint/block_string.rb +49 -0
- data/lib/ascii_paint/config.rb +197 -0
- data/lib/ascii_paint/version.rb +3 -0
- data/spec/ascii_paint_spec.rb +108 -0
- data/spec/block_string_spec.rb +75 -0
- data/spec/fixtures/ascii.txt +2 -0
- data/spec/spec_helper.rb +54 -0
- metadata +135 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
!binary "U0hBMQ==":
|
|
3
|
+
metadata.gz: 07fb319a0666e394fce07cca879a6d3c16cf81c9
|
|
4
|
+
data.tar.gz: 07e594e70ac58ebd656b72dabcbda59f2ae3e03b
|
|
5
|
+
!binary "U0hBNTEy":
|
|
6
|
+
metadata.gz: 5e952740658a8fec7f020877d6d2e66e5bdf4bd5d370a2cd2ce507fa4b3f97179b093bf24894e509a2206377614a384c3488f34e4b98e866ca2a0e53fbb3eb76
|
|
7
|
+
data.tar.gz: 4bef3c9a2cc135a6646c7ce6abd52fdee20cb568828b68ffee032f6ebb6ec4b28f71af91665c81e55b7f04b3b439e2b39b8cd61a003c529a3f4b20cd34bad8a0
|
data/lib/ascii_paint.rb
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
require 'bundler/setup'
|
|
2
|
+
require 'chunky_png'
|
|
3
|
+
|
|
4
|
+
module AsciiPaint
|
|
5
|
+
|
|
6
|
+
# Exception raised when ASCII paint encounters a character without a paint
|
|
7
|
+
# color specified.
|
|
8
|
+
class CharacterNotFound < Exception; end
|
|
9
|
+
|
|
10
|
+
# Paints a PNG based on the given ASCII art.
|
|
11
|
+
#
|
|
12
|
+
# Example:
|
|
13
|
+
# text = "
|
|
14
|
+
# !!!!! @@@@@ $$$$$ %%%%% ^^^^^ @@@@@ $$$$$ %%%%% ^ ^ !!!!!
|
|
15
|
+
# ! ! @ $ % ^ @ @ $ $ % ^^ ^ !
|
|
16
|
+
# !!!!! @@@@@ $ % ^ @@@@@ $$$$$ % ^ ^ ^ !
|
|
17
|
+
# ! ! @ $ % ^ @ $ $ % ^ ^^ !
|
|
18
|
+
# ! ! @@@@@ $$$$$ %%%%% ^^^^^ !!!!! @ $ $ %%%%% ^ ^ !
|
|
19
|
+
# "
|
|
20
|
+
#
|
|
21
|
+
# AsciiPaint.paint(text, 'out.png')
|
|
22
|
+
#
|
|
23
|
+
# @param ascii_art [#to_s, Array<String>]
|
|
24
|
+
# multiline string, string array or filename with the ASCII art to paint
|
|
25
|
+
# @param out_filename [#to_s]
|
|
26
|
+
# the name of the painted PNG file
|
|
27
|
+
# @param conf [Hash<Symbol, value>]
|
|
28
|
+
# configuration settings. Keys should be the names of attributes of
|
|
29
|
+
# {AsciiPaint::Config}, such as +:character_height+.
|
|
30
|
+
# @return [String]
|
|
31
|
+
# the name of the painted PNG file
|
|
32
|
+
def self.paint(ascii_art, out_filename, configuration = {})
|
|
33
|
+
global_configuration = self.config
|
|
34
|
+
local_configuration = global_configuration.dup.set_options(configuration)
|
|
35
|
+
|
|
36
|
+
ascii_array = ascii_art_to_array(ascii_art)
|
|
37
|
+
image = ascii_to_image(ascii_array, local_configuration)
|
|
38
|
+
save_image(image, out_filename, local_configuration)
|
|
39
|
+
|
|
40
|
+
if block_given?
|
|
41
|
+
begin
|
|
42
|
+
yield out_filename
|
|
43
|
+
ensure
|
|
44
|
+
FileUtils.rm(out_filename)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
true
|
|
48
|
+
else
|
|
49
|
+
out_filename
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def self.ascii_art_to_array(ascii_art)
|
|
56
|
+
return ascii_art if ascii_art.is_a? Array
|
|
57
|
+
|
|
58
|
+
ascii_art = ascii_art.to_s.split("\n")
|
|
59
|
+
if ascii_art.size == 1
|
|
60
|
+
ascii_art = File.open(ascii_art[0], 'r').to_a.map(&:chomp)
|
|
61
|
+
end
|
|
62
|
+
ascii_art
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.ascii_to_image(ascii_array, configuration)
|
|
66
|
+
colors_grid = ascii_to_colors(ascii_array, configuration)
|
|
67
|
+
image = blank_image(ascii_array, configuration)
|
|
68
|
+
painted_image(image, colors_grid, configuration)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.blank_image(lines, configuration)
|
|
72
|
+
height = lines.size
|
|
73
|
+
width = lines.map(&:size).max
|
|
74
|
+
|
|
75
|
+
width_pixels, height_pixels = configuration.characters_to_pixels(width, height)
|
|
76
|
+
png = ChunkyPNG::Image.new(width_pixels, height_pixels, configuration.color_map[' '])
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.ascii_to_colors(strings, configuration)
|
|
80
|
+
strings.map do |line|
|
|
81
|
+
line.chars.map do |char|
|
|
82
|
+
color = configuration.color_map[char]
|
|
83
|
+
raise AsciiPaint::CharacterNotFound.new "Couldn't find a color mapping for character: #{char}" unless color
|
|
84
|
+
color
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def self.painted_image(image, color_grid, configuration)
|
|
90
|
+
color_grid.each_with_index do |row, y|
|
|
91
|
+
row.each_with_index do |color, x|
|
|
92
|
+
x_pixels, y_pixels = configuration.characters_to_pixels(x, y)
|
|
93
|
+
width_pixels, height_pixels = configuration.characters_to_pixels(1, 1)
|
|
94
|
+
image.rect(x_pixels, y_pixels, x_pixels + width_pixels, y_pixels + height_pixels, configuration.border_color, color)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
image
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def self.save_image(image, filename, _)
|
|
102
|
+
image.save(filename, :interlace => true)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def self.root
|
|
106
|
+
Pathname.new(__FILE__).parent.parent
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
require_relative 'ascii_paint/block_character.rb'
|
|
111
|
+
require_relative 'ascii_paint/block_string.rb'
|
|
112
|
+
require_relative 'ascii_paint/config.rb'
|
|
113
|
+
require_relative 'ascii_paint/version.rb'
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module AsciiPaint
|
|
2
|
+
class BlockCharacter
|
|
3
|
+
attr_reader :ascii
|
|
4
|
+
|
|
5
|
+
# TODO: make customizable
|
|
6
|
+
UNPADDED_HEIGHT = 5
|
|
7
|
+
UNPADDED_WIDTH = 5
|
|
8
|
+
VERTICAL_PADDING = 2
|
|
9
|
+
HORIZONTAL_PADDING = 2
|
|
10
|
+
|
|
11
|
+
def initialize(character)
|
|
12
|
+
raise "Only single characters please! #{character}" unless character.size == 1
|
|
13
|
+
|
|
14
|
+
if character == "\n"
|
|
15
|
+
@newline = true
|
|
16
|
+
return
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
load_ascii(character)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def newline?
|
|
23
|
+
!!@newline
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_s
|
|
27
|
+
ascii.join("\n")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.height
|
|
31
|
+
UNPADDED_HEIGHT + VERTICAL_PADDING
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.width
|
|
35
|
+
UNPADDED_WIDTH + HORIZONTAL_PADDING
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.left_padding
|
|
39
|
+
HORIZONTAL_PADDING / 2
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.right_padding
|
|
43
|
+
(HORIZONTAL_PADDING + 1) / 2
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.top_padding
|
|
47
|
+
VERTICAL_PADDING / 2
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.bottom_padding
|
|
51
|
+
(VERTICAL_PADDING + 1) / 2
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# TODO: filename escaping
|
|
57
|
+
def load_ascii(character)
|
|
58
|
+
path = block_characters_dir.join("#{character}.txt")
|
|
59
|
+
if File.exists? path
|
|
60
|
+
@ascii = File.open(path, 'r').to_a.map(&:chomp)
|
|
61
|
+
@ascii = pad_ascii(@ascii)
|
|
62
|
+
else
|
|
63
|
+
raise "Character not supported: #{character}"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def pad_ascii(ascii)
|
|
68
|
+
left = " " * self.class.left_padding
|
|
69
|
+
right = " " * self.class.right_padding
|
|
70
|
+
padded = ascii.map do |row|
|
|
71
|
+
"#{left}#{row}#{right}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
blank_row = " " * self.class.width
|
|
75
|
+
self.class.top_padding.times { padded.unshift(blank_row) }
|
|
76
|
+
self.class.bottom_padding.times { padded.push(blank_row) }
|
|
77
|
+
|
|
78
|
+
padded
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def block_characters_dir
|
|
82
|
+
AsciiPaint.root.join('data', 'block_characters')
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module AsciiPaint
|
|
2
|
+
class BlockString
|
|
3
|
+
attr_reader :block_characters
|
|
4
|
+
|
|
5
|
+
def initialize(string)
|
|
6
|
+
@block_characters = string.chars.map { |char| BlockCharacter.new(char) }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def each_row
|
|
10
|
+
return to_enum(:each_row) unless block_given?
|
|
11
|
+
|
|
12
|
+
rows =
|
|
13
|
+
block_characters.chunk do |block_char|
|
|
14
|
+
if block_char.newline?
|
|
15
|
+
nil
|
|
16
|
+
else
|
|
17
|
+
:not_newline
|
|
18
|
+
end
|
|
19
|
+
end.map do |symbol, row|
|
|
20
|
+
row
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
rows.each do |row|
|
|
24
|
+
yield row
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_a
|
|
29
|
+
each_row.flat_map do |block_row|
|
|
30
|
+
(0...BlockCharacter.height).map do |h|
|
|
31
|
+
block_row.inject('') do |string_row, block_char|
|
|
32
|
+
string_row + block_char.ascii[h]
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module AsciiPaint
|
|
41
|
+
# TODO: support filename arguments
|
|
42
|
+
# TODO: write tests
|
|
43
|
+
# TODO: write docs
|
|
44
|
+
def self.block_paint(block_string, out_filename, conf = {}, &block)
|
|
45
|
+
block_string = block_string.join("\n") if block_string.respond_to? :join
|
|
46
|
+
ascii_art = BlockString.new(block_string).to_a
|
|
47
|
+
paint(ascii_art, out_filename, conf, &block)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# An instance of class +AsciiPaint::Config+ is used to set global configuration
|
|
2
|
+
# options for ASCII paint. Pass a block to {AsciiPaint.config} to access this
|
|
3
|
+
# object.
|
|
4
|
+
|
|
5
|
+
module AsciiPaint
|
|
6
|
+
class Config
|
|
7
|
+
|
|
8
|
+
TRANSPARENT = ChunkyPNG::Color::TRANSPARENT
|
|
9
|
+
SPECIAL_SYMBOL_MAP = {
|
|
10
|
+
transparent: TRANSPARENT
|
|
11
|
+
}
|
|
12
|
+
private_constant :TRANSPARENT, :SPECIAL_SYMBOL_MAP
|
|
13
|
+
|
|
14
|
+
# The horizontal size in pixels of the rectangle that replaces a single
|
|
15
|
+
# character during painting.
|
|
16
|
+
attr_accessor :character_width
|
|
17
|
+
|
|
18
|
+
# The vertical size in pixels of the rectangle that replaces a single
|
|
19
|
+
# character during painting.
|
|
20
|
+
attr_accessor :character_height
|
|
21
|
+
|
|
22
|
+
attr_accessor :color_map
|
|
23
|
+
|
|
24
|
+
def initialize(settings = {})
|
|
25
|
+
reset!
|
|
26
|
+
set_options(settings)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
alias_method :default, :new
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Merge new mappings into the color map.
|
|
34
|
+
#
|
|
35
|
+
# The color map specifies which color paints over each character. For example,
|
|
36
|
+
# config.color_map = { 'a' => :blue }
|
|
37
|
+
# tells ASCII paint to replace the character 'a' with the color blue.
|
|
38
|
+
#
|
|
39
|
+
# @param hash [Hash<String, Symbol>]
|
|
40
|
+
# the new mappings to be merged into the color map.
|
|
41
|
+
# @return [void]
|
|
42
|
+
def color_map=(hash)
|
|
43
|
+
replace_special_symbols!(hash)
|
|
44
|
+
@color_map = color_map.merge(hash)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Set the color to paint over characters whose color hasn't been defined by
|
|
48
|
+
# the color map.
|
|
49
|
+
#
|
|
50
|
+
# @param default_color [Symbol]
|
|
51
|
+
# the default color to replace characters without a specified color.
|
|
52
|
+
# @return [void]
|
|
53
|
+
def color_for_undefined_character=(default_color)
|
|
54
|
+
color_map.default = replacement_for_special_symbol(default_color)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def characters_to_pixels(x, y)
|
|
58
|
+
[x * character_width, y * character_height]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Set configuration options using a hash instead of using method calls. For
|
|
62
|
+
# example,
|
|
63
|
+
# config.set_options({character_width: 10})
|
|
64
|
+
# is equivalent to
|
|
65
|
+
# config.character_width = 10
|
|
66
|
+
#
|
|
67
|
+
# @param options [Hash<Symbol, value>]
|
|
68
|
+
# settings mapping from attribute name to value.
|
|
69
|
+
# @return self
|
|
70
|
+
def set_options(options)
|
|
71
|
+
options.each do |key, value|
|
|
72
|
+
mutator_name = "#{key}="
|
|
73
|
+
self.send(mutator_name, value)
|
|
74
|
+
end
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def border_color
|
|
79
|
+
TRANSPARENT
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def reset!
|
|
83
|
+
@character_height = Default::CHARACTER_HEIGHT
|
|
84
|
+
@character_width = Default::CHARACTER_WIDTH
|
|
85
|
+
@color_map =
|
|
86
|
+
begin
|
|
87
|
+
map = Default::COLOR_MAP.dup
|
|
88
|
+
map.default = Default::COLOR_FOR_UNDEFINED_CHARACTER
|
|
89
|
+
replace_special_symbols!(map)
|
|
90
|
+
map
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
private
|
|
95
|
+
|
|
96
|
+
def replace_special_symbols!(hash)
|
|
97
|
+
hash.each do |char, color_sym|
|
|
98
|
+
hash[char] = replacement_for_special_symbol(color_sym)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def replacement_for_special_symbol(symbol)
|
|
103
|
+
SPECIAL_SYMBOL_MAP[symbol] || symbol
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
public
|
|
107
|
+
|
|
108
|
+
# Default values for configuration settings.
|
|
109
|
+
module Default
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def self.rainbow_mapping(characters)
|
|
113
|
+
# Thanks jbum (http://krazydad.com/tutorials/makecolors.php)
|
|
114
|
+
hash = {}
|
|
115
|
+
period = Math::PI * 2
|
|
116
|
+
frequency = period / characters.count
|
|
117
|
+
characters.each_with_index do |char, index|
|
|
118
|
+
r = Math.cos(frequency * index + 0) * 127 + 128;
|
|
119
|
+
g = Math.cos(frequency * index + period / 3) * 127 + 128;
|
|
120
|
+
b = Math.cos(frequency * index + period * 2 / 3) * 127 + 128;
|
|
121
|
+
color = ChunkyPNG::Color.rgb(r.to_i, g.to_i, b.to_i)
|
|
122
|
+
hash[char] = color
|
|
123
|
+
end
|
|
124
|
+
hash
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
public
|
|
128
|
+
|
|
129
|
+
CHARACTER_HEIGHT = 16
|
|
130
|
+
CHARACTER_WIDTH = 8
|
|
131
|
+
COLOR_FOR_UNDEFINED_CHARACTER = :black
|
|
132
|
+
COLOR_MAP =
|
|
133
|
+
begin
|
|
134
|
+
map = {
|
|
135
|
+
' ' => :transparent,
|
|
136
|
+
|
|
137
|
+
'!' => :red,
|
|
138
|
+
'@' => :orange,
|
|
139
|
+
'#' => :yellow,
|
|
140
|
+
'$' => :green,
|
|
141
|
+
'%' => :blue,
|
|
142
|
+
'^' => :purple,
|
|
143
|
+
|
|
144
|
+
'_' => :white,
|
|
145
|
+
'~' => :black,
|
|
146
|
+
|
|
147
|
+
'<' => :lightgrey,
|
|
148
|
+
'>' => :grey,
|
|
149
|
+
'?' => :darkgrey,
|
|
150
|
+
',' => :lightslategrey,
|
|
151
|
+
'.' => :slategrey,
|
|
152
|
+
'/' => :darkslategrey,
|
|
153
|
+
'\\' => :dimgrey,
|
|
154
|
+
|
|
155
|
+
':' => :chocolate,
|
|
156
|
+
';' => :blanchedalmond,
|
|
157
|
+
'\'' => :coral,
|
|
158
|
+
'"' => :deepskyblue,
|
|
159
|
+
'{' => :indigo,
|
|
160
|
+
'}' => :ivory,
|
|
161
|
+
'[' => :khaki,
|
|
162
|
+
']' => :lavender,
|
|
163
|
+
'|' => :hotpink,
|
|
164
|
+
'&' => :darksalmon,
|
|
165
|
+
'*' => :lime,
|
|
166
|
+
'(' => :lightyellow,
|
|
167
|
+
')' => :honeydew,
|
|
168
|
+
'-' => :azure,
|
|
169
|
+
'+' => :crimson,
|
|
170
|
+
'=' => :antiquewhite,
|
|
171
|
+
'`' => :cornsilk
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
letters = ('a'..'z').zip('A'..'Z').flatten # ['a', 'A', 'b', 'B', ...]
|
|
175
|
+
map.merge! rainbow_mapping(letters)
|
|
176
|
+
|
|
177
|
+
numbers = ('0'..'9')
|
|
178
|
+
map.merge! rainbow_mapping(numbers)
|
|
179
|
+
|
|
180
|
+
map
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
module AsciiPaint
|
|
188
|
+
# Passes an instance of {AsciiPaint::Config} to the given block. Used to set
|
|
189
|
+
# global configuration.
|
|
190
|
+
#
|
|
191
|
+
# @return [Config] the global configuration
|
|
192
|
+
def self.config
|
|
193
|
+
@configuration ||= Config.default
|
|
194
|
+
yield(@configuration) if block_given?
|
|
195
|
+
@configuration
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
require 'ascii_paint'
|
|
2
|
+
require 'pry'
|
|
3
|
+
|
|
4
|
+
describe AsciiPaint do
|
|
5
|
+
|
|
6
|
+
let(:input_filename) { Helpers.fixtures_directory.join('ascii.txt') }
|
|
7
|
+
|
|
8
|
+
before(:each) do
|
|
9
|
+
Helpers.cd_work_directory
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
after(:each) do
|
|
13
|
+
Helpers.rm_rf_work_directory
|
|
14
|
+
AsciiPaint.config.reset!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "does math good" do
|
|
18
|
+
(1 + 1).should eq(2)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "writes a file" do
|
|
22
|
+
AsciiPaint.paint("ASCII\npaint", OUTPUT_FILENAME)
|
|
23
|
+
File.should be_a_file OUTPUT_FILENAME
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "paints the correct dimensions" do
|
|
27
|
+
AsciiPaint.paint("ASCII\npaint", OUTPUT_FILENAME, character_width: 10, character_height: 10)
|
|
28
|
+
result = ChunkyPNG::Image.from_file(OUTPUT_FILENAME)
|
|
29
|
+
result.height.should eq(2 * 10)
|
|
30
|
+
result.width.should eq(5 * 10)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "paints the correct colors" do
|
|
34
|
+
AsciiPaint.paint("ASCII\npaint", OUTPUT_FILENAME, character_width: 10, color_map: {'S' => :blue})
|
|
35
|
+
result = ChunkyPNG::Image.from_file(OUTPUT_FILENAME)
|
|
36
|
+
color = result.get_pixel(15, 0)
|
|
37
|
+
red = ChunkyPNG::Color.r(color)
|
|
38
|
+
green = ChunkyPNG::Color.g(color)
|
|
39
|
+
blue = ChunkyPNG::Color.b(color)
|
|
40
|
+
[red, green, blue].should eq([0, 0, 255])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "accepts a multiline string input" do
|
|
44
|
+
multiline_string = "ASCII\npaint"
|
|
45
|
+
AsciiPaint.paint(multiline_string, OUTPUT_FILENAME, character_width: 10, character_height: 10)
|
|
46
|
+
result = ChunkyPNG::Image.from_file(OUTPUT_FILENAME)
|
|
47
|
+
result.height.should eq(2 * 10)
|
|
48
|
+
result.width.should eq(5 * 10)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "accepts a string array input" do
|
|
52
|
+
string_array = ["ASCII", "paint"]
|
|
53
|
+
AsciiPaint.paint(string_array, OUTPUT_FILENAME, character_width: 10, character_height: 10)
|
|
54
|
+
result = ChunkyPNG::Image.from_file(OUTPUT_FILENAME)
|
|
55
|
+
result.height.should eq(2 * 10)
|
|
56
|
+
result.width.should eq(5 * 10)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "accepts a filename input" do
|
|
60
|
+
AsciiPaint.paint(input_filename, OUTPUT_FILENAME, character_width: 10, character_height: 10)
|
|
61
|
+
result = ChunkyPNG::Image.from_file(OUTPUT_FILENAME)
|
|
62
|
+
result.height.should eq(2 * 10)
|
|
63
|
+
result.width.should eq(5 * 10)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "obeys AsciiPaint.config configuration" do
|
|
67
|
+
AsciiPaint.config do |config|
|
|
68
|
+
config.character_height = 100
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
AsciiPaint.paint(input_filename, OUTPUT_FILENAME)
|
|
72
|
+
result = ChunkyPNG::Image.from_file(OUTPUT_FILENAME)
|
|
73
|
+
result.height.should eq(2 * 100)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "obeys per-method-call configuration" do
|
|
77
|
+
AsciiPaint.paint("ASCII\npaint", OUTPUT_FILENAME, character_width: 10, character_height: 10)
|
|
78
|
+
result = ChunkyPNG::Image.from_file(OUTPUT_FILENAME)
|
|
79
|
+
result.height.should eq(2 * 10)
|
|
80
|
+
result.width.should eq(5 * 10)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "overrides global config with local config" do
|
|
84
|
+
AsciiPaint.config do |config|
|
|
85
|
+
config.character_height = 100
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
AsciiPaint.paint(input_filename, OUTPUT_FILENAME, character_height: 77)
|
|
89
|
+
result = ChunkyPNG::Image.from_file(OUTPUT_FILENAME)
|
|
90
|
+
result.height.should eq(2 * 77)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it "paints transparent paint" do
|
|
94
|
+
AsciiPaint.paint("ASCII\npaint", OUTPUT_FILENAME, character_width: 10, color_map: {'S' => :transparent})
|
|
95
|
+
result = ChunkyPNG::Image.from_file(OUTPUT_FILENAME)
|
|
96
|
+
color = result.get_pixel(15, 0)
|
|
97
|
+
alpha = ChunkyPNG::Color.a(color)
|
|
98
|
+
alpha.should eq(0)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "deletes temporary files" do
|
|
102
|
+
AsciiPaint.paint("ASCII\npaint", OUTPUT_FILENAME) do |f|
|
|
103
|
+
File.should be_a_file OUTPUT_FILENAME
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
File.should_not be_a_file OUTPUT_FILENAME
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
require 'ascii_paint'
|
|
2
|
+
require 'pry'
|
|
3
|
+
|
|
4
|
+
describe AsciiPaint::BlockString do
|
|
5
|
+
|
|
6
|
+
before(:each) do
|
|
7
|
+
Helpers.cd_work_directory
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
after(:each) do
|
|
11
|
+
Helpers.rm_rf_work_directory
|
|
12
|
+
AsciiPaint.config.reset!
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "writes a file" do
|
|
16
|
+
AsciiPaint.block_paint("ASCII", OUTPUT_FILENAME)
|
|
17
|
+
File.should be_a_file OUTPUT_FILENAME
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "obeys local configuration" do
|
|
21
|
+
AsciiPaint.block_paint("ASCII", OUTPUT_FILENAME, character_height: 100)
|
|
22
|
+
result = ChunkyPNG::Image.from_file(OUTPUT_FILENAME)
|
|
23
|
+
result.height.should eq(100 * AsciiPaint::BlockCharacter.height)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "obeys global configuration" do
|
|
27
|
+
AsciiPaint.config do |config|
|
|
28
|
+
config.character_height = 100
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
AsciiPaint.block_paint("ASCII", OUTPUT_FILENAME)
|
|
32
|
+
result = ChunkyPNG::Image.from_file(OUTPUT_FILENAME)
|
|
33
|
+
result.height.should eq(100 * AsciiPaint::BlockCharacter.height)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "deletes temporary files" do
|
|
37
|
+
AsciiPaint.block_paint("ASCII\npaint", OUTPUT_FILENAME) do |f|
|
|
38
|
+
File.should be_a_file OUTPUT_FILENAME
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
File.should_not be_a_file OUTPUT_FILENAME
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context "data directory" do
|
|
45
|
+
it "has character files of the correct width" do
|
|
46
|
+
Helpers.each_block_character_path do |path|
|
|
47
|
+
File.open(path, 'r') do |file|
|
|
48
|
+
file.each_line do |line|
|
|
49
|
+
line.chomp.size.should eq(AsciiPaint::BlockCharacter::UNPADDED_WIDTH), file.path
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "has character files of the correct height" do
|
|
56
|
+
Helpers.each_block_character_path do |path|
|
|
57
|
+
File.open(path, 'r') do |file|
|
|
58
|
+
file.each_line.count.should eq(AsciiPaint::BlockCharacter::UNPADDED_HEIGHT), file.path
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "has character files for all alphanumeric characters" do
|
|
64
|
+
desired_characters = ('A'..'Z').to_a + ('0'..'9').to_a
|
|
65
|
+
found_characters = []
|
|
66
|
+
|
|
67
|
+
Helpers.each_block_character_path do |path|
|
|
68
|
+
character = path.to_s.match(/\/(.)\.txt/)[1]
|
|
69
|
+
found_characters << character
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
found_characters.should =~ desired_characters
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
|
4
|
+
# loaded once.
|
|
5
|
+
#
|
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
|
7
|
+
RSpec.configure do |config|
|
|
8
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
|
9
|
+
config.run_all_when_everything_filtered = true
|
|
10
|
+
config.filter_run :focus
|
|
11
|
+
|
|
12
|
+
# Run specs in random order to surface order dependencies. If you find an
|
|
13
|
+
# order dependency and want to debug it, you can fix the order by providing
|
|
14
|
+
# the seed, which is printed after each run.
|
|
15
|
+
# --seed 1234
|
|
16
|
+
config.order = 'random'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
require 'ascii_paint'
|
|
20
|
+
|
|
21
|
+
OUTPUT_FILENAME = 'painted.png'
|
|
22
|
+
|
|
23
|
+
module Helpers
|
|
24
|
+
def self.cd_work_directory
|
|
25
|
+
create_work_directory
|
|
26
|
+
Dir.chdir(Helpers.work_directory)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.rm_rf_work_directory
|
|
30
|
+
Dir.chdir(work_directory.parent)
|
|
31
|
+
FileUtils.rm_rf(Helpers.work_directory)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.create_work_directory
|
|
35
|
+
FileUtils.mkdir_p(work_directory)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.work_directory
|
|
39
|
+
AsciiPaint.root.join('tmp', 'spec')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.fixtures_directory
|
|
43
|
+
AsciiPaint.root.join('spec', 'fixtures')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# TODO: split into separate helper
|
|
47
|
+
def self.each_block_character_path
|
|
48
|
+
block_characters_dir = AsciiPaint.root.join('data', 'block_characters')
|
|
49
|
+
block_characters_dir.each_child(false) do |path|
|
|
50
|
+
full_path = block_characters_dir.join(path)
|
|
51
|
+
yield full_path
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ascii_paint
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Nate Sullivan
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2013-12-14 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: chunky_png
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ~>
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 1.2.9
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ~>
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 1.2.9
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ~>
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 2.14.1
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ~>
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 2.14.1
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: pry
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ! '>='
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ! '>='
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
description: Convert ASCII art to PNGs in pure Ruby.
|
|
56
|
+
email: nathanielsullivan00+ascii_paint@gmail.com
|
|
57
|
+
executables: []
|
|
58
|
+
extensions: []
|
|
59
|
+
extra_rdoc_files: []
|
|
60
|
+
files:
|
|
61
|
+
- lib/ascii_paint/block_character.rb
|
|
62
|
+
- lib/ascii_paint/block_string.rb
|
|
63
|
+
- lib/ascii_paint/config.rb
|
|
64
|
+
- lib/ascii_paint/version.rb
|
|
65
|
+
- lib/ascii_paint.rb
|
|
66
|
+
- data/block_characters/0.txt
|
|
67
|
+
- data/block_characters/1.txt
|
|
68
|
+
- data/block_characters/2.txt
|
|
69
|
+
- data/block_characters/3.txt
|
|
70
|
+
- data/block_characters/4.txt
|
|
71
|
+
- data/block_characters/5.txt
|
|
72
|
+
- data/block_characters/6.txt
|
|
73
|
+
- data/block_characters/7.txt
|
|
74
|
+
- data/block_characters/8.txt
|
|
75
|
+
- data/block_characters/9.txt
|
|
76
|
+
- data/block_characters/A.txt
|
|
77
|
+
- data/block_characters/B.txt
|
|
78
|
+
- data/block_characters/C.txt
|
|
79
|
+
- data/block_characters/D.txt
|
|
80
|
+
- data/block_characters/E.txt
|
|
81
|
+
- data/block_characters/F.txt
|
|
82
|
+
- data/block_characters/G.txt
|
|
83
|
+
- data/block_characters/H.txt
|
|
84
|
+
- data/block_characters/I.txt
|
|
85
|
+
- data/block_characters/J.txt
|
|
86
|
+
- data/block_characters/K.txt
|
|
87
|
+
- data/block_characters/L.txt
|
|
88
|
+
- data/block_characters/M.txt
|
|
89
|
+
- data/block_characters/N.txt
|
|
90
|
+
- data/block_characters/O.txt
|
|
91
|
+
- data/block_characters/P.txt
|
|
92
|
+
- data/block_characters/Q.txt
|
|
93
|
+
- data/block_characters/R.txt
|
|
94
|
+
- data/block_characters/S.txt
|
|
95
|
+
- data/block_characters/T.txt
|
|
96
|
+
- data/block_characters/U.txt
|
|
97
|
+
- data/block_characters/V.txt
|
|
98
|
+
- data/block_characters/W.txt
|
|
99
|
+
- data/block_characters/X.txt
|
|
100
|
+
- data/block_characters/Y.txt
|
|
101
|
+
- data/block_characters/Z.txt
|
|
102
|
+
- spec/ascii_paint_spec.rb
|
|
103
|
+
- spec/block_string_spec.rb
|
|
104
|
+
- spec/fixtures/ascii.txt
|
|
105
|
+
- spec/spec_helper.rb
|
|
106
|
+
homepage: https://github.com/nate00/ascii_paint
|
|
107
|
+
licenses:
|
|
108
|
+
- MIT
|
|
109
|
+
metadata: {}
|
|
110
|
+
post_install_message:
|
|
111
|
+
rdoc_options: []
|
|
112
|
+
require_paths:
|
|
113
|
+
- lib
|
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
115
|
+
requirements:
|
|
116
|
+
- - ! '>='
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: '0'
|
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ! '>='
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
124
|
+
requirements: []
|
|
125
|
+
rubyforge_project:
|
|
126
|
+
rubygems_version: 2.0.3
|
|
127
|
+
signing_key:
|
|
128
|
+
specification_version: 4
|
|
129
|
+
summary: Paint in ASCII!
|
|
130
|
+
test_files:
|
|
131
|
+
- spec/ascii_paint_spec.rb
|
|
132
|
+
- spec/block_string_spec.rb
|
|
133
|
+
- spec/fixtures/ascii.txt
|
|
134
|
+
- spec/spec_helper.rb
|
|
135
|
+
has_rdoc:
|