the-architect-sprite_generator 0.1.12 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,29 +39,24 @@ class SpriteGenerator
39
39
  def initialize(files_or_paths, output, root, options = {})
40
40
  @files = find_files(files_or_paths)
41
41
  return if @files.nil? || @files.empty?
42
- @root = root || ''
43
- @output = output
44
- @delimiter = options[:delimiter] || '-'
45
- @analyzed = analyze_filenames(@files, @delimiter)
46
- @template = Liquid::Template.parse(options[:template] || '')
47
-
48
- @sprite_location = options[:sprite_location] || @output
49
- @background = options[:background] || '#FFFFFF00'
50
- @tile_size = options[:tile]
51
- @alignment = options[:alignment] ? Magick.const_get("#{camelize(options[:alignment])}Gravity") : Magick::CenterGravity
42
+ @root = root || ''
43
+ @output = output
44
+ @delimiter = options[:delimiter] || '-'
45
+ @analyzed = find_files_for_mode(options[:distribution] || :smart)
46
+ @template = Liquid::Template.parse(options[:template] || '')
47
+ @sprite_location = options[:sprite_location] || @output
48
+ @background = options[:background] || '#FFFFFF00'
49
+ @tile_size = options[:tile]
50
+ @alignment = options[:alignment] ? Magick.const_get("#{camelize(options[:alignment])}Gravity") : Magick::CenterGravity
52
51
  end
53
52
 
54
53
 
55
54
  def create
56
55
  raise 'No files found.' if @files.nil? || @files.empty?
57
- background = @background
58
- unless @tile_size.nil?
59
- size_x, size_y = @tile_size.split('x').first(2).map{|dim| dim.to_i}
60
- tile = Magick::Image.new(size_x, size_y){ self.background_color = background }
61
- tile.format = "PNG"
62
- end
56
+ build_tile
63
57
  destination = @root.nil? || @root.empty? ? @output : File.join(@root, @output)
64
- image, css = build(tile)
58
+ image, css = build_sprite_and_css
59
+ background = @background
65
60
  image.write(destination){ self.background_color = background }
66
61
  css
67
62
  end
@@ -69,66 +64,99 @@ class SpriteGenerator
69
64
 
70
65
  protected
71
66
 
72
- # simplyfied version of active_supports camelize version
73
- def camelize(lower_case_and_underscored_word)
74
- lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
75
- end
76
-
77
- def build(tile = nil)
78
- background = @background
79
- images = ImageList.new{ self.background_color = background }
80
- context = { 'sprite_location' => @sprite_location, 'tile' => tile }
81
- css = []
67
+ def build_sprite_and_css
68
+ background = @background
69
+ @images = ImageList.new{ self.background_color = background }
70
+ context = { 'sprite_location' => @sprite_location }
71
+ @css = []
82
72
 
83
- @analyzed.each do |key, value|
84
- if tile
85
- context['left'] = images.length > 0 ? tile.columns : 0
73
+ @analyzed.keys.sort.each do |key|
74
+ value = @analyzed[key]
75
+ if @tile
76
+ context['left'] = @images.length > 0 ? @tile.columns : 0
86
77
  else
87
- context['left'] = images.length > 0 ? images.append(false).columns : 0
78
+ context['left'] = @images.length > 0 ? @images.append(false).columns : 0
88
79
  end
89
- context['top'] = 0
80
+ context['top'] = 0
90
81
  context['basename'] = key
91
- context['overall'] = images.length
82
+ context['overall'] = @images.length
92
83
 
93
- if value.size > 1
94
- image_list = ImageList.new(*value){ self.background_color = background }
95
- if tile
96
- tiles = ImageList.new{ self.background_color = background }
97
- image_list.each do |image|
98
- tiles << tile.composite(image, @alignment, Magick::OverCompositeOp)
99
- end
100
- images.from_blob(tiles.append(true).to_blob){ self.background_color = background }
101
- else
102
- images.from_blob(image_list.append(true).to_blob){ self.background_color = background }
103
- end
104
- context['variations'] = image_list.length
105
- context['type'] = :list
106
- context['images'] = image_list
107
- context['filenames'] = value
108
- else
109
-
110
- filename = value.flatten.first
111
- image = Image.read(filename){ self.background_color = background }
112
-
113
- context['variations'] = 0
114
- context['variation_name'] = ''
115
- context['variation_number'] = 0
116
- context['full_filename'] = filename
117
- context['file_basename'] = File.basename(filename, '.*')
118
- context['variation_name'] = context['file_basename'].gsub(/^#{context['basename']}#{@delimiter}/, '')
119
-
120
- context['type'] = :image
121
-
122
- if tile
123
- images.from_blob(tile.composite(image.first, @alignment, Magick::OverCompositeOp).to_blob){ self.background_color = background }
124
- else
125
- images.from_blob(image.first.to_blob){ self.background_color = background }
126
- end
127
- end
128
- css << build_css(context)
84
+ value.size > 1 ? build_image_list(context, value.sort) : build_single_image(context, value.flatten.first)
85
+ @css << build_css(context)
129
86
  end
130
87
 
131
- [images.append(false), css.join("\n")]
88
+ [@images.append(false), @css.join("\n")]
89
+ end
90
+
91
+
92
+ def build_single_image(context, filename)
93
+ background = @background
94
+ image = Image.read(filename){ self.background_color = background }.flatten.first
95
+
96
+ context.merge!(build_context_for_single_image(image, filename, context['basename']))
97
+
98
+ if @tile
99
+ @images.from_blob(@tile.composite(image, @alignment, Magick::OverCompositeOp).to_blob){ self.background_color = background }
100
+ else
101
+ @images.from_blob(image.to_blob){ self.background_color = background }
102
+ end
103
+ end
104
+
105
+
106
+ def build_context_for_single_image(image, filename, basename)
107
+ file_basename = File.basename(filename, '.*')
108
+ {
109
+ 'variations' => 0,
110
+ 'variation_name' => '',
111
+ 'variation_number' => 0,
112
+ 'full_filename' => filename,
113
+ 'file_basename' => file_basename,
114
+ 'variation_name' => file_basename.gsub(/^#{basename}#{@delimiter}/, ''),
115
+ 'width' => @tile ? tile.columns : image.columns,
116
+ 'height' => @tile ? tile.rows : image.rows,
117
+ 'type' => :image
118
+ }
119
+ end
120
+
121
+
122
+ def build_image_list(context, image_filenames)
123
+ background = @background
124
+ image_list = ImageList.new(*image_filenames){ self.background_color = background }
125
+ if @tile
126
+ tiles = ImageList.new{ self.background_color = background }
127
+ image_list.each do |image|
128
+ tiles << @tile.composite(image, @alignment, Magick::OverCompositeOp)
129
+ end
130
+ append_to_sprite(tiles)
131
+ else
132
+ append_to_sprite(image_list)
133
+ end
134
+ context.merge!(build_context_for_image_list(image_list, image_filenames))
135
+ end
136
+
137
+
138
+ def append_to_sprite(images)
139
+ background = @background
140
+ @images.from_blob(images.append(true).to_blob){ self.background_color = background }
141
+ end
142
+
143
+
144
+ def build_context_for_image_list(image_list, image_filenames)
145
+ {
146
+ 'variations' => image_list.length,
147
+ 'type' => :list,
148
+ 'images' => image_list,
149
+ 'filenames' => image_filenames
150
+ }
151
+ end
152
+
153
+
154
+ def build_tile
155
+ return if @tile_size.nil?
156
+ background = @background
157
+ size_x, size_y = @tile_size.split('x').first(2).map{|dim| dim.to_i}
158
+ @tile = Magick::Image.new(size_x, size_y){ self.background_color = background }
159
+ @tile.format = "PNG"
132
160
  end
133
161
 
134
162
 
@@ -147,21 +175,19 @@ protected
147
175
 
148
176
  def build_css_for_list(context)
149
177
  new_context = context.dup
150
- tile = new_context.delete('tile')
151
- image_list = context.delete('images')
178
+ image_list = context.delete('images')
152
179
  new_context['type'] = :image
153
180
  css = image_list.inject([]) do |css, image|
154
- new_context['width'] = tile ? tile.columns : image.columns
155
- new_context['height'] = tile ? tile.rows : image.rows
181
+ new_context['width'] = @tile ? @tile.columns : image.columns
182
+ new_context['height'] = @tile ? @tile.rows : image.rows
156
183
  new_context['variation_number'] = css.size
157
-
158
- new_context['full_filename'] = context['filenames'].shift
159
- new_context['filename'] = File.basename(new_context['full_filename'])
160
- new_context['file_basename'] = File.basename(new_context['full_filename'], '.*')
161
- new_context['variation_name'] = new_context['file_basename'].gsub(/^#{new_context['basename']}#{@delimiter}/, '')
184
+ new_context['full_filename'] = context['filenames'].shift
185
+ new_context['filename'] = File.basename(new_context['full_filename'])
186
+ new_context['file_basename'] = File.basename(new_context['full_filename'], '.*')
187
+ new_context['variation_name'] = new_context['file_basename'].gsub(/^#{new_context['basename']}#{@delimiter}/, '')
162
188
 
163
189
  css << build_css(new_context.dup)
164
- new_context['top'] += tile ? tile.columns : new_context['height']
190
+ new_context['top'] += @tile ? @tile.columns : new_context['height']
165
191
  css
166
192
  end.join("\n")
167
193
  end
@@ -181,17 +207,53 @@ protected
181
207
  end
182
208
 
183
209
 
210
+ # put filenames in format for each mode
211
+ def find_files_for_mode(mode)
212
+ case mode
213
+ when :smart
214
+ smart_distribution
215
+ when :horizontal
216
+ horizontal_distribution
217
+ when :vertical
218
+ vertical_distribution
219
+ end
220
+ end
221
+
222
+
184
223
  # gather information about the selected files
185
224
  # check for variations by using a delimiter as an indicator
186
- def analyze_filenames(file_names, delimiter = '_')
187
- file_names.inject(Hash.new{|hash, key| hash[key] = Array.new;}) do |h, file|
188
- basename = File.basename(file).split('.').first
189
- without_variation = basename.split(delimiter)[0..-2].join(delimiter)
190
- basename = without_variation.nil? || without_variation == '' ? basename : without_variation
225
+ def smart_distribution
226
+ @files.inject(Hash.new{|hash, key| hash[key] = Array.new;}) do |h, file|
227
+ basename = File.basename(file, '.*')
228
+ without_variation = basename.split(@delimiter)[0..-2].join(@delimiter)
229
+ basename = without_variation.nil? || without_variation == '' ? basename : without_variation
191
230
  h[basename] << file
192
231
  h
193
232
  end
194
233
  end
195
234
 
196
235
 
236
+ def horizontal_distribution
237
+ @files.inject(Hash.new{|hash, key| hash[key] = Array.new;}) do |h, file|
238
+ basename = File.basename(file)
239
+ h[basename] << file
240
+ h
241
+ end
242
+ end
243
+
244
+
245
+ def vertical_distribution
246
+ @files.inject(Hash.new{|hash, key| hash[key] = Array.new;}) do |h, file|
247
+ basename = File.basename(file)
248
+ h['all'] << file
249
+ h
250
+ end
251
+ end
252
+
253
+
254
+ # simplyfied version of active_support's camelize version
255
+ def camelize(lower_case_and_underscored_word)
256
+ lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
257
+ end
258
+
197
259
  end
@@ -18,13 +18,42 @@ class SpriteGeneratorTest < Test::Unit::TestCase
18
18
  Dir.glob('test/output/*').each{|f| File.delete f }
19
19
  end
20
20
 
21
- def test_should_set_correct_context_for_images_without_variations
21
+ def test_should_user_horizontal_distribution
22
+ template = %q{ {{left}} }
23
+ generator = SpriteGenerator.new(@all_images_path, @output, nil, { :template => template, :distribution => :horizontal })
24
+ css = generator.create
25
+ assert css.include?('240')
26
+ end
27
+
28
+ def test_should_user_vertical_distribution
29
+ template = %q{ {{top}} }
30
+ generator = SpriteGenerator.new(@all_images_path, @output, nil, { :template => template, :distribution => :vertical })
31
+ css = generator.create
32
+ `open #{@output}`
33
+ sleep 1
34
+ assert css.include?('240')
35
+ end
36
+
37
+ def test_should_set_correct_context_filebasename_for_images_without_variations
22
38
  template = %q{ {{file_basename}} }
23
39
  generator = SpriteGenerator.new(@all_images_path, @output, nil, { :template => template, :delimiter => '_' })
24
40
  css = generator.create
25
41
  assert css.include?('emoticon-evilgrin')
26
42
  end
27
43
 
44
+ def test_should_set_correct_context_width_for_images_without_variations
45
+ template = %q{ {{width}} }
46
+ generator = SpriteGenerator.new(@all_images_path, @output, nil, { :template => template, :delimiter => '_' })
47
+ css = generator.create
48
+ assert css.include?('16')
49
+ end
50
+
51
+ def test_should_set_correct_context_top_for_images_without_variations
52
+ template = %q{ {{top}} }
53
+ generator = SpriteGenerator.new(@all_images_path, @output, nil, { :template => template, :delimiter => '_' })
54
+ css = generator.create
55
+ assert !css.include?('-16')
56
+ end
28
57
 
29
58
  def test_should_create_correct_context
30
59
  template = %q{
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: the-architect-sprite_generator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcel Scherf