Using svg for assets in Openfl

I wanted to use svg for icons in an Openfl project.
It’s not intuitive to do this, so I wrote it down for future use.

What are svg files?

Scalable Vector Graphics (SVG) is an XML-based vector image format for two-dimensional graphics with support for interactivity and animation.

Source: http://en.wikipedia.org/wiki/Scalable_Vector_Graphics

Good to remember that they scale awesomely without lost of resolution!

How to add it to your project

You can find the svg code on github.
But you can install it easier with haxelib

Open your terminal and write:

haxelib install svg

To add it to an OpenFL project, add this to your project file:

<haxelib name="svg" />

Code

import openfl.Assets;
import openfl.display.Shape;
import openfl.display.Sprite;
import format.SVG;
 
class SVGExample extends Sprite
{

  public function new()
  {
    var svg : SVG = new SVG(Assets.getText("assets/openfl.svg"));
    var shape : Shape  = new Shape();
    svg.render(shape.graphics);
    addChild(shape);
  }
}

NICE!!!
But now the cool part: it scales without losing resolution

Check the highlighted line for the changes,

import openfl.Assets;
import openfl.display.Shape;
import openfl.display.Sprite;
import format.SVG;
 
class SVGExample extends Sprite
{

  public function new()
  {
    var svg : SVG = new SVG(Assets.getText("assets/openfl.svg"));
    var shape : Shape  = new Shape();
    svg.render(shape.graphics,0,0,1000,1000);
    addChild(shape);
  }
}

for the curious: https://github.com/openfl/svg/blob/master/format/SVG.hx

Sources

  • I remembered this gist, which explains almost everything.
  • Get icons here, they are free for download as long as you credit freepik
  • Featured image from webdesignerdepot

Custmm Grumm – AI 2 Array

Another experiment towards Custmm Grumm. This time my task was to export/import an Illustrator file to Flash…

Yeah, yeah; I know: you say “import to stage” … correct! 😉
But what I need is the shape converted to code (coordinates in the x-direction and y-direction)..
Ha, you stopped grinning!

Well the first part is correct.
You need to import the file to the stage and give every imported shape it’s own layer.
This is something that you don’t want to do by hand (I didn’t want to 😉 ), so I wrote a jsfl that fixes that for you (read my post about it here: object-to-layer-jsfl)

After that you need to extract the values (x and y-positions form the corners of the shapes) of the files. Some thing, you don’t want that to do by hand: I have written a jsfl who does that. (read more about that here: shape-2-array-jsfl )

If you use these two scripts, you get: all imported shapes in different layers, and you can extract all values.
Example of the array:
[as]
var shapeArrayz:Array = new Array ();
shapeArrayz[0] = [[20.05,169.5,0] , [62.425,169.5,1] , [104.8,169.5,2] , [104.8,169.5,0] , [104.8,211.85,1] , [104.8,254.2,2] , [104.8,254.2,0] , [62.425,254.2,1] , [20.05,254.2,2] , [20.05,254.2,0] , [20.05,211.85,1] , [20.05,169.5,2]];
// etc…
[/as]
This array off point can be used to generate the shape you just “traced”.

Generated shapes from an Array

This script (below) is used to generate the points (every line has 3 points, the beginning, the end and one inbetween), and the generated shape on the right side (no points, only the shape):
[as]
var shapeArray:Array = [];

// visualize the points
function createPoints2 (_pointArray:Array) {
shapeArray = [];
var point:MovieClipInLibraryWithLinkageName;
for (var i=0; i<_pointArray.length; i++) {
point = new MovieClipInLibraryWithLinkageName();
point.x = _pointArray[i][0];
point.y = _pointArray[i][1];
var switchExpression:uint = _pointArray[i][2];
switch (switchExpression) {
case 0 :
//trace (0);
point.alpha = .5;
shapeArray.push ([_pointArray[i][0],_pointArray[i][1]]);
break;
case 1 :
//trace (1);
point.scaleX = point.scaleY = .5;
break;
case 2 :
//trace (2);
point.alpha = .5;
point.scaleX = point.scaleY = .3;
break;
default :
trace ("Not 0, 1, or 2");
}
addChild (point);
}

}

// draw the new extracted image
function drawArray (_arr:Array) {
// trace ("drawArray ");
var _shape:Shape = new Shape();
_shape.graphics.lineStyle (1, 0x333333, 1);
_shape.graphics.beginFill (0xcccccc);
_shape.graphics.moveTo (_arr[0][0], _arr[0][1]); // starting point
for (var i=1; i<=_arr.length; i+=3) {
_shape.graphics.curveTo (_arr[i][0], _arr[i][1] , _arr[i+1][0], _arr[i+1][1]);
// _shape.graphics.lineTo (_arr[i+1][0], _arr[i+1][1]);
}
_shape.graphics.endFill ();
this.drawContainer_mc.addChild (_shape);
}

// jumpstart everything
function init (){
for (var j=0; j<shapeArrayz.length; j++) {
// trace(shapeArrayz[j])
createPoints2 (shapeArrayz[j]);
drawArray (shapeArrayz[j]);
}
}
init ();

[/as]

Update #1: I previously used point_mc in the code. That was a movieClip in the library with a linkage name. I changed it in the code, I hope that helps.

Eventually I will use the points, and generated shapes to modify the shape (move a point, create a new shape) and/or to add points.

Object 2 Layer jsfl

For a project of mine: Custmm Grumm I needed to change a shape into an array (explained in this post shape 2 array jsfl ).

To make this happen I imported the .AI file (Illustrator) to the stage. But it will place everything on one layer, every shape it’s own ‘group’ and the script I wrote (shape 2 array jsfl), needs every shape on one layer.
Flash import screen

So here a script that, as long the different shape are still group individually, will give every shape it’s own layer.

Update #1: This script is not only useful in this project, in every project where you have a lot of objects that need there own layer, this is the JSFL for you!
Remember if you need to animate an movieclip, it needs to have it’s own layer (timeline)

Need a lot of item on there own layer (own timeline) in Flash, don’t copy past it one by one, but just use this JSFL

Not sure how to call this script when you save it: “[mck] object2layer.jsfl
[as]
/**
* Object2Layer, version 1.1
*
* select an item on the timeline and convert it to a layer
*
*

*  ____                   _      ____
* |  __| _ __ ___    ___ | | __ |__  |
* | |   | '_ ` _ \  / __|| |/ /    | |
* | |   | | | | | || (__ |   <     | |
* | |__ |_| |_| |_| \___||_|\_\  __| |
* |____|                        |____|
*
* 

*
* @author Matthijs C. Kamstra [mck]
* @version 1.1
* @since Fri Jun 01 19:43:37 2007
* @langversion: ActionScript 2.0 / 3.0
*
* Changelog:
* v 1.0 [2007-06-01] - Initial release
* v 1.1 [2008-03-20] - Change jsfl name; multiple item at once
*/

var versionString = '1.1';

var currentDoc = fl.getDocumentDOM ();
var timeline = fl.getDocumentDOM ().getTimeline ();
var curLayer = fl.getDocumentDOM ().getTimeline ().currentLayer;
var curFrame = fl.getDocumentDOM ().getTimeline ().currentFrame;
var selectionArray = currentDoc.selection;

// check if something is selected
if (selectionArray.length == 0){
alert ('nothing selected')
} else {
fl.getDocumentDOM().distributeToLayers();
}
// end
[/as]

have fun 🙂

Shape 2 Array jsfl

For a project of mine: Custmm Grumm I needed to change a shape into an array, you could say that I needed to change a Illustrator/vector file into code.

As far as I know there is no other method then the one I created here with jsfl.

To make this happen I imported the .AI file (Illustrator) to the stage.
Flash import screen

This JSFL files has some restrictions, you can read it in the comments:

this script only works under certain conditions:
– everything that is selected must be shapes, if not, this doesn’t work (select all and ctrl+b (break))
– every shape has to be in a different layer, otherwise the script see it as one shape

The result of this jsfl is not always what you expect…
sometimes geometric shapes like squares/rectangles/triangles are all f#$%ed-up (it looks like curves are made to opposite corners)
I have no solution for that in this jsfl (in the code), it seems that Flash ‘reads’ the shape wrong (or in the wrong order)…
But you could try:

  • I used the straighten tool which worked in one case, but not in the other
  • rotated a square 90 degrees
  • both solutions

I’m not making an install file, so if you want to try this script you need to copy it in the correct directory (I’m sorry for the OsX users: I have no idea, but if you do, place a comment)
Windows (on my computer): C:\Documents and Settings\[here you name]\Local Settings\Application Data\Adobe\Flash CS3\en\Configuration\Commands

here is the JSFL (if you need to give it a name; I have a suggestion: “[mck] shape2array 2.jsfl”)

/**
*
* this script only works under certain conditions:
*		- everything that is selected must be shapes, if not, this doesn't work (select all and ctrl+b (break))
* 		- every shape has to be in a different layer, otherwise the script see it as one shape 
*
* The result of this jsfl is not always what you expect...
* 		sometimes geometric shapes like squares/rectangles/triangles are all f#$%ed-up (it looks like curves are made to opposite corners)
* 		I have no solution for that in this jsfl (in the code), it seems that Flash 'reads' the shape wrong (or in the wrong order)... 
*		But you could try: 	- I used the straighten tool which worked in one case, but not in the other 
*						- rotated a square 90 degrees 
*						- both solutions 
*
*
* 
* based upon 		http://ericlin2.tripod.com/bugwire/bugwiret.html
* and			http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00003869.html
*
* <pre>
*  ____                   _      ____
* |  __| _ __ ___    ___ | | __ |__  |
* | |   | '_ ` _ \  / __|| |/ /    | |
* | |   | | | | | || (__ |   <     | |
* | |__ |_| |_| |_| \___||_|\_\  __| |
* |____|                        |____|
*
* </pre>
*
*
* @author			Matthijs C. Kamstra [mck]
* @version		1.1
* @since			10:00 5-5-2008
*
* Changelog:
* 		v1.1 [2008-05-09] - test movie after use of this jsfl
* 		v1.0 [2008-05-05] - Initial release
*
*
*/
var currentVersion = '1.1';

fl.trace ('[mck] shape2Array :: version ' + currentVersion);

// with a shape selected
var ptArray = [];
var doneEdge = [];
var exportString = 'var shapeArrayz:Array = new Array ();\n';
var selectionNumber = 0;

// fl.trace("// start ---------------------------");
function isDrawn(id) {
	for (var k = 0; k<doneEdge.length; k++) {
		if (doneEdge[k] == id) {
			return true;
		}
	}
	return false;
}


sel = fl.getDocumentDOM().selection;
for (var n = 0; n < sel.length; n++) {

	exportString += 'shapeArrayz['+n+'] = [';
	selectionNumber = sel.length;
	
	var elt = sel[n];
	if (elt.elementType != 'shape') {
		continue;
	}
	elt.beginEdit();
	for (i=0; i<elt.contours.length; i++) {
		var cont = elt.contours[i];
		var he = cont.getHalfEdge();
		var startId = he.id;
		var id = 0;
		while (id != startId) {
			var ed = he.getEdge();
			if (!isDrawn(ed.id)) {
				doneEdge.push(ed.id);
				for (var j = 0; j<3; j++) {
					var pt = ed.getControl(j);
					ptArray.push(pt.x, pt.y , j);
					exportString += '[' + pt.x + ',' + pt.y + ',' + j + '] , ';
				}
			}
			he = he.getNext();
			id = he.id;
		}
	}
	elt.endEdit();
	exportString += '];\n';
}
// fl.trace(ptArray);
// fl.trace("// end ---------------------------");


// I'm a lazy bastard, so paste the code in the as layer
// create or place code in 'as' layer
var tl = fl.getDocumentDOM().getTimeline();
if (tl.findLayerIndex("as") == undefined){
	tl.addNewLayer('as', 'normal' , true); 
} else {
	tl.currentLayer = tl.findLayerIndex("as")[0];
}
tl.layers[tl.currentLayer].frames[0].actionScript = exportString.split('] , ];').join(']];') + "\n";

// The following example tests the movie for the current document:
fl.getDocumentDOM().testMovie(); // if you don't want to export to swf after the jsfl is ready, comment this line


// end jsfl

It will create a layer named ‘as’ where the array will be placed that will look something like this:

var shapeArrayz:Array = new Array ();
shapeArrayz[0] = [[20.05,169.5,0] , [62.425,169.5,1] , [104.8,169.5,2] , [104.8,169.5,0] , [104.8,211.85,1] , [104.8,254.2,2] , [104.8,254.2,0] , [62.425,254.2,1] , [20.05,254.2,2] , [20.05,254.2,0] , [20.05,211.85,1] , [20.05,169.5,2]];
shapeArrayz[1] = [[189.55,0,0] , [189.55,42.35,1] , [189.55,84.7,2] , [189.55,84.7,0] , [147.175,84.7,1] , [104.8,84.7,2] , [104.8,84.7,0] , [104.8,42.35,1] , [104.8,0,2] , [104.8,0,0] , [147.175,0,1] , [189.55,0,2]];
shapeArrayz[2] = [[189.55,169.45,0] , [231.9,169.45,1] , [274.25,169.45,2] , [274.25,169.45,0] , [274.25,211.8,1] , [274.25,254.15,2] , [274.25,254.15,0] , [231.9,254.15,1] , [189.55,254.15,2] , [189.55,254.15,0] , [189.55,211.8,1] , [189.55,169.45,2]];
shapeArrayz[3] = [[189.55,84.7,0] , [189.55,127.075,1] , [189.55,169.45,2] , [189.55,169.45,0] , [147.175,169.45,1] , [104.8,169.45,2] , [104.8,169.45,0] , [104.8,127.075,1] , [104.8,84.7,2] , [104.8,84.7,0] , [147.175,84.7,1] , [189.55,84.7,2]];
shapeArrayz[4] = [[104.8,169.45,0] , [147.175,169.45,1] , [189.55,169.45,2] , [189.55,169.45,0] , [189.55,211.8,1] , [189.55,254.15,2] , [189.55,254.15,0] , [147.175,254.15,1] , [104.8,254.15,2] , [104.8,254.15,0] , [104.8,211.8,1] , [104.8,169.45,2]];
shapeArrayz[5] = [[104.8,338.95,0] , [104.8,296.55,1] , [104.8,254.15,2] , [104.8,254.15,0] , [147.175,254.15,1] , [189.55,254.15,2] , [189.55,254.15,0] , [189.55,296.55,1] , [189.55,338.95,2] , [189.55,338.95,0] , [147.175,338.95,1] , [104.8,338.95,2]];
shapeArrayz[6] = [[274.25,169.5,0] , [284.275,179.5,1] , [294.3,189.5,2] , [294.3,189.5,0] , [294.3,211.9,1] , [294.3,234.3,2] , [294.3,234.3,0] , [294.275,234.3,1] , [294.25,234.3,2] , [294.25,234.3,0] , [284.325,244.25,1] , [274.4,254.2,2] , [274.4,254.2,0] , [274.325,254.2,1] , [274.25,254.2,2] , [274.25,254.2,0] , [274.25,211.85,1] , [274.25,169.5,2]];
shapeArrayz[7] = [[0.1,189.45,0] , [10.1,179.45,1] , [20.1,169.45,2] , [20.15,254.2,0] , [20.125,211.825,1] , [20.1,169.45,2] , [20.15,254.2,0] , [20.1,254.2,1] , [20.05,254.2,2] , [20.05,254.2,0] , [10.025,244.2,1] , [0,234.2,2] , [0,234.2,0] , [0.05,211.825,1] , [0.1,189.45,2]];
shapeArrayz[8] = [[124.85,359,0] , [114.825,349,1] , [104.8,339,2] , [189.55,338.85,0] , [147.175,338.925,1] , [104.8,339,2] , [189.55,338.85,0] , [189.55,338.925,1] , [189.55,339,2] , [189.55,339,0] , [179.6,349.05,1] , [169.65,359.1,2] , [169.65,359.1,0] , [147.25,359.05,1] , [124.85,359,2]];
shapeArrayz[9] = [[274.25,254.15,0] , [274.25,254.2,1] , [274.25,254.25,2] , [274.25,254.25,0] , [264.3,264.275,1] , [254.35,274.3,2] , [254.35,274.3,0] , [231.975,274.3,1] , [209.6,274.3,2] , [209.6,274.3,0] , [199.575,264.25,1] , [189.55,254.2,2] , [189.55,254.2,0] , [231.9,254.175,1] , [274.25,254.15,2]];
shapeArrayz[10] = [[104.8,254.15,0] , [104.8,254.2,1] , [104.8,254.25,2] , [104.8,254.25,0] , [94.85,264.275,1] , [84.9,274.3,2] , [84.9,274.3,0] , [62.525,274.3,1] , [40.15,274.3,2] , [40.15,274.3,0] , [30.125,264.25,1] , [20.1,254.2,2] , [20.1,254.2,0] , [62.45,254.175,1] , [104.8,254.15,2]];
shapeArrayz[11] = [[189.6,169.5,0] , [199.625,159.475,1] , [209.65,149.45,2] , [209.65,149.45,0] , [232.025,149.45,1] , [254.4,149.45,2] , [254.4,149.45,0] , [254.4,149.475,1] , [254.4,149.5,2] , [254.4,149.5,0] , [264.325,159.475,1] , [274.25,169.45,2] , [274.25,169.45,0] , [274.25,169.5,1] , [274.25,169.55,2] , [189.6,169.5,0] , [231.925,169.525,1] , [274.25,169.55,2]];
shapeArrayz[12] = [[20.15,169.5,0] , [30.175,159.475,1] , [40.2,149.45,2] , [40.2,149.45,0] , [62.55,149.45,1] , [84.9,149.45,2] , [84.9,149.45,0] , [84.9,149.475,1] , [84.9,149.5,2] , [84.9,149.5,0] , [94.85,159.475,1] , [104.8,169.45,2] , [104.8,169.45,0] , [104.8,169.5,1] , [104.8,169.55,2] , [20.15,169.5,0] , [62.475,169.525,1] , [104.8,169.55,2]];

And if you need some help to convert this Array into a Flash generated shape ?
here is some code that could help you (AS3):

// draw the new extracted image
function drawArray (_arr:Array) {
	// trace ("drawArray ");
	var _shape:Shape = new Shape();
	_shape.graphics.lineStyle (1, 0x333333, 1);
	_shape.graphics.beginFill (0xcccccc);
	_shape.graphics.moveTo (_arr[0][0], _arr[0][1]); // starting point
	for (var i=1; i<=_arr.length; i+=3) {
		_shape.graphics.curveTo (_arr[i][0], _arr[i][1] , _arr[i+1][0], _arr[i+1][1]);
		// _shape.graphics.lineTo (_arr[i+1][0], _arr[i+1][1]);
	}
	_shape.graphics.endFill ();
	this.drawContainer_mc.addChild (_shape);
}

// jumpstart everything
function init (){	
	for (var j=0; j<shapeArrayz.length; j++) {
		// trace(shapeArrayz[j])
		drawArray (shapeArrayz[j]);
	}
}
init ();

have fun 🙂