Skip to content

Instantly share code, notes, and snippets.

@thisredone
Last active September 16, 2018 18:14
Show Gist options
  • Save thisredone/cd903dfed45821f4d84efa7b508f72ab to your computer and use it in GitHub Desktop.
Save thisredone/cd903dfed45821f4d84efa7b508f72ab to your computer and use it in GitHub Desktop.
Cut images into tiles of the same size and glue together into a tileset
#!/usr/bin/env ruby
require 'fileutils'
require 'pry'
SIZE = ARGV[0].to_i
max_tileset_width = 7
wanted_geometry = "#{SIZE}x#{SIZE}"
FileUtils.mkdir_p 'tiles'
items = []
Dir['*.png'].each do |file|
next if file == 'tileset.png'
dims = `identify #{file}`[/\d+x\d+/].split('x').map(&:to_i)
tiling = dims.map { |d| (d.to_f / 128).ceil.to_i }
max_tileset_width += 1 if tiling[1] > max_tileset_width
if dims == [SIZE, SIZE]
items << { tile: tiling, item: 'tiles/' + file}
`cp #{file} tiles/`
next
end
virtual_canvas_dims = tiling.map { |d| d * 128 }
offsets = dims.zip(virtual_canvas_dims).map { |(size, full)| full - size }
repage = [virtual_canvas_dims.join('x'), offsets.join('+')].join('+')
basename = File.basename(file, '.png')
puts "#{file} (#{dims.join 'x'}): #{virtual_canvas_dims.join 'x'}+#{offsets.join 'x'}"
`convert #{file} -alpha on -repage #{repage} -crop #{wanted_geometry} +repage tiles/#{basename}-crop-%02d.png`
cropped = Dir["tiles/#{basename}-crop*"]
cropped.each do |cropped_file|
`convert -size #{wanted_geometry} xc:none #{cropped_file} -gravity south -composite #{cropped_file}`
end
`montage -background none #{cropped.join ' '} -geometry +0+0 -tile #{tiling.join 'x'} tiles/#{file}`
items << { tile: tiling, item: 'tiles/' + file }
`rm #{cropped.join ' '}`
end
sorted = items.group_by { |x| x[:tile][1] }.sort_by(&:first).map(&:last).map { |group| group.sort_by { |x| x[:tile][0] } }
matrix = [Array.new(max_tileset_width)]
row_index = 0
y = 0
add_row = -> prev_height do
matrix << Array.new(max_tileset_width)
row_index += 1
y += prev_height
end
sorted.each.with_index do |group, group_index|
group_height = group.first[:tile][1]
group.each do |item|
width = item[:tile][0]
add_row.(group_height) if matrix[row_index].count(nil) < width
cell_index = matrix[row_index].index(nil)
(0...width).each do |offset|
matrix[row_index][cell_index + offset] = true
end
item[:place] = [cell_index, y]
end
add_row.(group_height) if matrix[row_index].first && group_index != sorted.size - 1
end
items = sorted.flatten
result = items.map do |x|
[
x[:item],
'-geometry',
'+' + x[:place].map { |t| t * SIZE }.join('+'),
'-composite'
].join(' ')
end.join(' ')
height = items.last[:tile][1] * SIZE + y * SIZE
width = max_tileset_width * SIZE
`convert -size #{width}x#{height} xc:none #{result} tileset.png`
@thisredone
Copy link
Author

Takes PNG images from the current directory, cuts them to be the same size and places the resulting tiles in tiles/ directory. Tiles are then combined into the tileset.png

Installation

Needs Ruby and Imagemagick. Can be used in Windows but only with "Windows Subsystem for Linux" (or Cygwin).

cp tileset_from_frames.rb /usr/local/bin/tileset_from_frames
chmod +x /usr/local/bin/tileset_from_frames

Usage

cd some_dir_with_frames
tileset_from_frames 64 # size to cut the tiles into

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment