Skip to content

Instantly share code, notes, and snippets.

@AndreasOM AndreasOM/tmx_tilesort.rb
Last active Nov 18, 2015

Embed
What would you like to do?
$:.unshift File.dirname(__FILE__)
## small ruby :HACK: that sorts tiles by size (height, width)
## only tested with a few files
## only tested on single tileset projects
## only works with object layers
## feel free to enhance, break, hack
## used for @omFiiish https://twitter.com/omFiiish ;)
require 'rubygems'
require 'rexml/document'
require 'rexml/xpath'
include REXML
$pending_changed_tile_gids = {} ## :HACK: needs cleanup
class Tmx
class Map
class Tileset
def self.sortTilesBySize( node )
tiles = []
first_gid = ( node.attributes[ 'first_gid' ] || 1 ).to_i
node.elements.each( 'tile' ) do |child|
tiles << child
node.delete( child )
end
## sort by height, then width
tiles.sort_by!{ |e|
w = 0
h = 0
e.elements.each( 'image' ) do |img|
w = ( img.attributes[ 'width' ] || 0 ).to_i
h = ( img.attributes[ 'height' ] || 0 ).to_i
end
[ h, w ]
}
## now fix the tile ids
new_id = 0
tiles.each do |tile|
old_id = tile.attributes[ 'id' ].to_i + first_gid
tile.attributes[ 'id' ] = new_id
$pending_changed_tile_gids[ old_id ] = new_id + first_gid
new_id += 1
end
tiles.each do |tile|
node.add( tile )
end
node
end
end
def self.sortTilesBySize( node )
node.elements.each( 'tileset' ) do |child|
n = Tileset.sortTilesBySize( child )
next_sib = child.next_sibling_node()
node.delete( child )
node.insert_before( next_sib, n )
end
node
end
def self.fixupTileIds( node, changed_tile_ids )
node.elements.each( 'objectgroup' ) do |group|
group.elements.each( 'object' ) do |obj|
old_id = obj.attributes[ 'gid' ]
if( old_id )
new_id = changed_tile_ids[ old_id.to_i ]
if( new_id )
obj.attributes[ 'gid' ] = new_id.to_s
end
end
end
end
end
end
def initialize()
@doc = nil
@maps = []
@tilesets = []
end
def load( filename )
doc = Document.new( File.new( filename ) )
@doc = doc ## we just cheat here and keep the xml
end
def save( filename )
File.open( filename, 'wb' ) do |f|
@doc.write( f )
end
end
def sortTilesBySize()
@doc.elements.each( 'map' ) do |child|
n = Map.sortTilesBySize( child )
@doc.delete( child )
@doc.add( n )
## fixup the tile ids
Map.fixupTileIds( child, $pending_changed_tile_gids )
end
end
end
input = ARGV.shift
output = ARGV.shift
if input && output
if input == output
puts "Please write to a new file"
else
tmx = Tmx.new
tmx.load( input )
tmx.sortTilesBySize()
tmx.save( output )
end
else
puts "#{$0} [oldfile] [newfile]"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.