The horrors of loading SVG files

Inkscape is a great free application for making vector graphics. I use it to design interface and to create some of the graphics for my games. Since it is so easy to use I have decided some time ago to use it also for the placement of interface elements and as a level editor. I will write a few articles about the strugles to do so because it is a long story without a real end.

Right now I have a set of classess that load SVG files and allow me to setup the interface in level in an easy way inside Inkscape. Why was it so hard to do though? The main reason is…

Coordinates versus Matrix

The world would be a wonderful place if only coordinates in Inkscape files were the real position of objects as they should appear on the screen. Unfortunately it is not so. Sometimes you get x, y values, sometimes you get transform attributes (with translate or scale) and sometimes you get matrix:

inkscape-matrix

In order to use Inkscape as a level/interface editor I had to load SVG files made in it and calculate the positions of text, images, rectangles and (recently) paths. I use name (xlink:href attribute) of the image to get the proper image from my TextureAtlas (I use libGDX library), but I also need x, y coordinates and angle of rotation. To do so I have to transform the matrix into coordinates which is much harder than it looks.

You can find the current code to do so on the libGDX forum. The part of the code that loads the matrix is short but took me many, many hours to master:

float[] fl = getMatrixFloats(trans);
x+=fl[4];
y+=fl[5];

if(fl[1]!=0||fl[0]!=0) 
	r+=(float)(Math.toDegrees(Math.atan2(fl[1], fl[0])));				
else
	r+=90;

sign = ((fl[0]*fl[3] - fl[2]*fl[1])<0) ? -1 : 1;
r2 = (float)(Math.toDegrees(Math.atan2(fl[3], fl[2])));

float oldsx = sx;
float oldsy = sy;

if(sign<0)
{
	if(r2<0&&r<0) 	{ 		sx*=(fl[0])/Math.cos( Math.toRadians(r) ); 		sy*=(fl[3])/Math.cos( Math.toRadians(r) ); 		 		if(sy>=0)
		{
			sx=1/sx;
			sy=1/sy;
		}
	}
	else
	{
		sx*=(fl[0])/Math.cos( Math.toRadians(r) );
		sy*=(fl[3])/Math.cos( Math.toRadians(r) );

		if(sx==0||sy==0) // not zero!
		{
			sx=(float) (oldsx*(fl[1])/Math.sin( Math.toRadians(r) ));
			sy=(float) (oldsy*-(fl[2])/Math.sin( Math.toRadians(r) ));
		}

	}
}

I don’t understand the need for some of the special conditions in that code – I had to add them to make the image on screen pixel perfect with what the Inkscape shows. It works for most cases – there are some exceptions though that I still have to fix – the biggest one seems to be with groups. Sometimes – but very rarely now, thankfully – grouping objects and rotating those groups will cause the objects to show up in a completely wrong place when the SVG is loaded through my class. The only solution for now is to ungroup them before saving…

Well enough though!

It works well enough though for me to use it widely in most of my games. As you can see on the screenshot below I use the object properties Inkscape allows to set to define actions for when the image first shows up, when the screen is removed, for clicking on buttons (images make the buttons) and those actions include different animations.

inkscape-lostheroes-interface

You can see the results in my games – The Lost Heroes is the first one that uses SVG for all interface elements and levels while Space Bubble Shooter uses it for levels and some screens.