Archive for the Category Python

 
 

I put pypacker up on github

Probably the most read post on this blog is about sprite packing in Python, here.

At the time I didn’t have a github account and was planning to make a small desktop application out of the algorithm as a way of learning Qt, which maybe I really will do some day, but in the meantime here is the latest version of this code and that I mention in the comments of the original post:

https://github.com/jwezorek/pypacker

The main thing that this version adds besides fixing the bug with growing that I had mentioned is an “–padding” command line option that will pad each sprite by an integer amount (usually you want 1) in the sprite sheet. This turns out to often be necessary for cocos2d-x/iOS games as if you don’t do it one sprite can bleed into adjacent sprites — although the issue may have been something that Apple has now fixed at the OpenGL ES layer or that was fixed in cocos2d-x — I was never sure whose bug it was but padding by a pixel makes it go away.

So usage would now be like:

pypacker.py -i C:\foo\sprites -o C:\foo\output\spritesheet -m grow -p 1

which would do packing on the images in C:\foo\sprites and make two files in C:\foo\output, spritesheet.png and spritesheet.plist, with each sprite padded by 1 pixel. If you need power-of-two square padding on the out put include a “-x” option on the command.

Snurtle and My Greatest Contribution to Mathematics, Such That It Was

This summer coming up will be 20 years since the summer after I graduated from college. I’m currently in that golden period in between software jobs — starting a new one in March — and, given the free time, have just done something which I have been meaning to do since that summer.

I generated the following image in a non-ad hoc manner, with re-useable software that I developed myself.

That year after college I was kind of shiftless. I chose to just not participate in the whole interview circus that normally accompanies one’s senior year of MIT and let myself graduate without having plans of any kind for the future. I think that I was tired mostly and was kind of sick of the universe of engineering and all of its hurdles and dog and pony shows, and I think I kind of half-understood that this would be the last time in my life in which it would be possible for me to be totally free of that universe — until, I guess, retirement.

Anyway, I lived in the slums of Boston — that is, Roxbury — with a friend of mine from high school and some roommates we found, one of whom ended going to jail (but that is another story), worked temp jobs, and didn’t do much of anything … except for some reason I became obsessed with aperiodic tilings of the plane and substitution tilings generally and put a lot of effort into coming up with one of my own. I wanted to find, for reasons that aren’t clear to me now, an aperiodic analog of the normal regular hexagon tiling.

It’s kind of a blur — I don’t really remember where the above came from as a sequence of steps — but the above is a patch of the best tiling I discovered during this period. It is generated via the following substitutions. The lengths of the sides of triangles and trapezoids are multiples of ϕ, the golden ratio.

As far as I know, the above is original and has not been discussed in the literature but I never was able to come up with local matching rules on the hexagons to enforce aperiodicity.

At that time I did develop software for working on these sorts of structures but what I came up with in retrospect wasn’t The Right Thing. This wasn’t all together my fault. Those were different times: no GitHub, no free code. If I wanted to output into a vector format it would have to be my own vector format. If I wanted to render that format to the screen I would have to write code to render that vector format to the screen, and so on. Also GUI applications were all the rage and were still new and shiny, so I was biased in that direction. I never really liked what I came up with then, and it wasn’t portable anyway; it was a black-and-white Macintosh application in C.

Having the negative example of that project all those years ago made it easy to see what I actually needed: not a GUI application but a programming language. So last week, I wrote one: a little language for specifying recursive structures like the above and rendering them in SVG. I’m calling it Snurtle because it is basically a combination of Python (a snake) and the turtle-based graphics of Logo. I chose Python syntax because I wrote the Snurtle interpreter in Python and thus got tokenization for free using Python’s “tokenize” module.

So, for example, the following simple substitution

is represented by the following Snurtle script:

sub square(n):
    terminal:
        poly("bisque", "orangered", 1 ):
            forward(n)
            turn(PI/2)
            forward(n)
            turn(PI/2)
            forward(n)
    nonterminal:
        rectangle(n)
        branch:
            turn(PI)
            forward(n/2)
            turn(-PI/2)
            forward(n/2)
            square(n/2)
            square(n/2)

sub rectangle(n):
    terminal:
        poly("lightskyblue", "orangered", 1 ):
            forward(n/2)
            turn(PI/2)
            forward(n)
            turn(PI/2)
            forward(n/2)
    nonterminal:
        square(n/2)
        turn(PI)
        square(n/2)

yielding

which, I think, is self-explanatory except for the “branch” block. Branch blocks tell Snurtle to push the state of the turtle on to a stack and then pop it when exiting the branch i.e. branch works like parentheses operators in L-systems. Also the following Snurtle constructs are not illustrated in the above:

  • flip blocks: similar in syntax to branch above. Tell Snurtle to multiply the angle arguments passed to turn statements by -1. (i.e. flipping them)
  • stroke and fill blocks: similar to “poly” above.

Anyway, here is my snurtle source code. Usage to generate the above would be:

snurtle.py -w -s square -k 500 -m 8 -o squares.html -c “10,10” “snurtle_scripts\squares.snu

where

  • -w : wrap the generated SVG in HTML (so it can be viewed in browser)
  • -s : initital substitution used to kick off the recursion
  • -k : scale factor in SVG output
  • -m : max stack depth before substituting in terminal blocks rather than nonterminal and ending the recursion
  • -o : output filename
  • c : starting coordinate of the turtle.
  • -d : Comma delimited string as -c, that provides width and height attributes for the SVG. (not shown)

Snurtle is pretty rough at this point, but I plan to continue working on it, especially if there is interest. Check back here, The Curiously Recurring Gimlet Pattern, for updates — or on this Quora blog which I will try to keep in sync — if you are interested. In particular, I plan on adding the following features / dealing with the following issues:

  • Substitutions can’t currently have more than one argument. I just never got around to adding this functionality as I never had a use-case in all the sample scripts I have tried, but there is no reason to limit “sub” blocks in this way.
  • Color, stroke color, and stroke thickness parameters to poly, stroke and fill blocks should be optional with intelligent defaults but aren’t currently.
  • Maybe add a z-order parameter to poly, stroke, and fill.
  • Add variables.
  • Possibly add “mirror” blocks which would be syntactic sugar for executing the block’s body in a flipped branch and then executing it again normally. This would be handy in definitions of complicated structures like my golden hexagon substitution.
  • Add “reverse” blocks which would cause the statements in a block to be executed in last to first order, recursively running compound statements this way too.
  • Add some kind of loop control structure, “repeat” or something.
  • Built-in system variables for (like “PI” above) for current stack depth and max stack depth.
  • Add exponentiation to the set of operations that can be performed in expressions.

Marble Madness Madness

gamedev.net recently linked to this video about the making of Marble Madness, which got me thinking about the raster-to-vector via contour extraction script I wrote in Python last year and the fact that, it being the future and all, I can probably find all of the art from Marble Madness unrolled into a single image file. So three clicks later and, oh yeah:

(Click the image for full size)

So I ran the above through my raster-to-vector converter. Here are the results (zipped, this is a huge file, over 20,000 SVG paths)  This file kills Adobe Illustrator. It took 15 minutes just to open it.

SVG for a single level is more manageable.  Here’s  the 2nd level as unzipped SVG … (curious to see if various browsers can handle this) Illustrator could handle this one pretty well so I experimented with applying various vector filters. Below is a bit of it with the corners rounded on the paths (click for a larger version):


Not sure what this all amounts to … just some stuff I did today. However I did learn

  • Python is slow. It took a really long time to generate the big file, seemed too long. I think C++ would’ve been like an order of magnitude faster — that’s my intuition anyway.
  • My contour extraction program really works which is kind of surprising — I thought for sure running it on something like this would crash it. (It does still have the problem that it can’t handle paletted raster image formats, but that’s the only bug I encountered)

Sprite Packing in Python…

(update: newer version mentioned in comments is on github here.)

I’ve been working on my puzzle game Syzygy again, after a long hiatus, and am now writing to iOS/cocos2d-x rather than just working on the prototype I had implemented to Win32.

The way that you get sprite data into cocos2d is by including as a resource a sprite sheet image and a .plist file which is XML that specifies which sprite is where. Plists are apparently an old Mac thing — I had never heard of this format. .plists describing a lot of sprites would be a chore to write by hand so there is a cottage industry of sprite packing applications.

I tried out one called TexturePacker and liked it a lot — except that it is crippleware; I need a few features that are only in the full version; plus I can’t stand crippleware; and I think $30 is too much for something that I can write myself over the weekend. So I decided to write my own sprite packer over the weekend.

The result is pypacker, a python script: source code here. Usage is like

pypacker -i [input] -o [output] -m [mode] -p

where

  • [input] = a path to a directory containing image files. (In any format supported by the python PIL module.)
  • [output] = a path + filename prefix for the two output files e.g. given C:\foo\bar the script will generate C:\foo\bar.png and c:\foo\bar.plist
  • [mode] = the packing mode. Can be either “grow” or fixed dimensions such as “256×256”. “grow” tells the algorithm to begin packing rectangles from a blank slate expanding the packing as necessary. “256×256” et. al. tell the algorithm to start with the given image size and pack sprites into it by subdivision, throwing an error if they all won’t fit.
  • -p = optional flag indicating you want the output image file dimensions padded to the nearest power-of-two-sized square.

The algorithm I used is a recursive bin packing algorithm in which sprites are placed one-by-one into a binary tree. I based it directly on Jake Gordon’s work in Javascript for generating sprite sheets for use in CSS, described here, only my algorithm is sort of like version 2 of his i.e. I fixed an issue that bugged me about his algorithm.

The core of the algorithm is a function that looks like this:

def pack_images( named_images, grow_mode, max_dim):
    root=()
    while named_images:
        named_image = named_images.pop()
        if not root:
            if (grow_mode):
                root = rect_node((), rectangle(0, 0, named_image.img.size[0], named_image.img.size[1]))
            else:
                root = rect_node((), rectangle(0, 0, max_dim[0], max_dim[1]))
            root.split_node(named_image)
            continue
        leaf = find_empty_leaf(root, named_image.img)
        if (leaf):
            leaf.split_node(named_image)
        else:
            if (grow_mode):
                root.grow_node(named_image)
            else:
                raise Exception("Can't pack images into a %d by %d rectangle." % max_dim)
    return root

We iterate through the images we want to pack. For each image, try to find a rectangular node in the tree that can contain the image. If one exists, place the image in the node and subdivide the node such that the remaining space, not taken up by the image, is available in the tree (this is what ‘split_node’ does). If such a node cannot be found, throw an exception if we are not in ‘grow’ mode or expand the root rectangle node to accommodate the new image if we are in ‘grow’ mode.

This routine is very similar to the Javascript implementation I linked to above. The difference is in the details about the structure of the binary tree. Jake Gordon’s Javascript implementation uses a node type that stores an image in the upper left and has children that he calls ‘right’ and ‘down’  like this:

Since actual data is always burnt into the upper left, it means that the tree can never subdivide into this space; we can never recurse into the upper left. This results in the grow_node routine being awkward to write. When we grow the root we either want to extend to the right or extend down, if the upper left can be a node and not image data this is a simple matter of creating a new node and making the the existing root its upper or left child. Anyway, Jake Gordon’s implementation results in a packing tree that cannot both grow right and grow down simultaneously because it would have been complicated to implement this. This limitation is not a problem practically as long as you sort the images from largest to smallest before running the packing algorithm —  a standard heuristic from the bin packing literature.

I however wanted to see if the standard sorting heuristic is really accomplishing anything. I wanted to be able to pack rectangles in random order. I therefore simplified the trinary node structure of the Javascript implementation into true binary nodes either oriented horizontally or vertically like this:

Further now only leafs can contain images and if a node is not a leaf it always has two valid, that is non-null, children. Using this type of tree structure makes the full grow_node routine more or less trivial.

Beyond that, I’m using the following heuristics:

  • If the orientation (horizontally or vertically) of a split is not forced, split with the orientation that will result in the new empty node having the largest area
  • If the orientation of growing the root rect is not forced, grow in the direction that leads to the smallest increase in the maximum side length of the root rectangle. (This heuristic enforces squarishness and is extremely important. Without doing this the grow version of the algorithm is basically unusable, and in this sense this grow heuristic can be considered part of the algorithm rather than a heuristic that can be swapped out)

Sorting by size (max side length) turns out be about a 6% improvement with this algorithm. Here’s 500 rects packed with sorting (top) and without (bottom):