Main menu

You are here

Creating levels in Box2D part 1 - Parsing Inkscape SVG files in Java

Submitted by bensmiley on Wed, 09/14/2011 - 18:05

Welcome to the first in a series of guides on how to create Box2D levels using by loading SVG images from Inkscape.

In this series I'm going to cover the whole process: loading the load the SVG document into the java enviroment, to parsing the SVG data using the open source library Batik to extract object, generating Benzier curves different levels of accuracy, triangulating your shapes using a constrained Delaunay triangulation and finally creating the Box2D objects and adding them to your Box2D world.

Firstly, what's the motivation for doing this? Designing levels for games manually is time consuming. Firstly you need to design the level then work out the coordinates for each point. Because Box2D can only work with convex objects with a maximum of 8 sides you need to decompose your level into convex objects.

The motivation is simple, it's much easier to design your levels in a paint program and then load them into Box2D than to manually enter the coordinates. You can buy software to do this for you but it's much better to know how to do it yourself than using a black box solution.

One word of warning. To follow this guide you will need to be proficient using Java and understand the basics of XML and the Document Object model.

Part 1: Loading your SVG document into Java

For this tutorial I'll be using Inkscape which is available to download for free here. The first step in the process is loading your SVG document into Java after that it will be possible to extract the path and shape data. You could load the file as a string but then you would have to write code to parse it to extract the path data. A better solution is to use pre-existing functionality to load the file as an XML document. After that you will be able to traverse the DOM to extract the relevant tags.

I tried using the XML loading functionality included in Batik but it was very slow - usually taking 3 - 4 seconds to load a simple SVG file. In the end I found it was better to use the standard Java XML parsing functionality. Below is the function I used:

The code above loads the resource from the specified URI (if the svg file is in your "src" file then you just need to put "src/[filename].svg" otherwise a path in the form "file:///Users/bensmiley45/Documents/workspace...." to the location of the file on your computer). The code returns the document object model representation of the file.

Parsing the Inkscape path information

Now we have the XML loaded into an object we need to think about parsing the SVG data to extract the information we're interested in. For a basic implementation we need four pieces of information:
- Global translation: When you move a path in Inkscape sometimes a global transformation takes place. This means that all object coordinates are translated by a certain amount. To render the paths in their correct positions we need to parse this global translation.
- Path point data: We need to extract the path point coordinates
- Path translation: Each path can also be translated individually
- Canvas dimensions: Internally Inkscape uses a coordinate system with the origin in the top right. This means that x increases left to right and y increases top to bottom. Box2D however uses a coordinate system with the origin in the bottom left hand corner. To convert the Inkscape coordinates to the Box2D coordinates we need to know the height of the Inkscape canvas.

The strategy I used was to create one class responsible for loading the SVG file, extracting and parsing the necessary data, applying the coordinate transform and generating the paths as Cubic Splines. I'll cover more of this in later sections but the basic structure of this class is as follows:

Affine Transform

A this point I think it's worth describing why I'm using an Affine Transformation object to store transformations. An Affine Transform is just a technical name for a a linear transformation which maps one 2D region to another 2D region. A linear transform is a transform is a transform where if you drew a straight line through three points in the original image you would be able to draw a straight line through the same three points in the transformed image.

The line might be at a different angle, shorter or longer but it would still be straight and still go through the three points. The advantage of using an Affine Transformation object is that it does a lot of the hard work for us. All transformations can be stored in one object and we can add simply add global transformations from the canvas to the local transformations for out line.

at.scale(1,-1);

at.translate(0, height);

This code uses the canvas height to move the origin from the top left corner of the screen to the bottom left corner of the screen. First the y-coordinate is inverted then the origin is moved down by the height of the canvas.

There we have it. We have successfully loaded an SVG file and started to extract the information we need. In the next section I'll describe how we can use the open source library Batik parse the transform and path data.