Source: two/Transform2d.js

/**
 *  @class Transform2d
 *  @memberof SQR
 *
 *  @description Similar to {SQR.Transform} but specialized to work with 2d rendeirng on a 2d canvas element.
 *  
 */
SQR.Transform2d = function(name) {

	var t = {};

	t.name = name || 'sqr.transform.' + SQR.Transform2dCount++;

    /** 
     *  @var {SQR.V3} position - the position of this transform relative to it's parent.
     *  It's a 3d vector, because z is used for depth indexing.
     *  @memberof SQR.Transform2d.prototype
     */
	t.position = new SQR.V3(0, 0, 0);

    /**
     *  @var {SQR.V3} rotation - the rotation of the transform in radians
     *  @memberof SQR.Transform2d.prototype
     */
	t.rotation = 0;

    /**
     *  @var {SQR.V2} scale - the scale of the object on x and y axis
     *  @memberof SQR.Transform2d.prototype
     */
	t.scale = new SQR.V2(1, 1);

    /**
     *  @var {Number} alpha - the transparency of this element. 
     *  @memberof SQR.Transform2d.prototype
     *  0 = transparent, 1 = opaque, default 1
     */
    t.alpha = 1;

	t.children = [], t.numChildren = 0;

   /**
    *   @method add
    *   @memberof SQR.Transform2d.prototype
    *   
    *   @description Add a child transform. Accepts multiple arguments but all of them need to be of type {SQR.Transform2D}.
    *   It doesn't do any sort of type checking so if you add non object that are not {SQR.Transform2D} 
    *   it will result in errors when the scene is rendered.
    */
    t.add = function() {
        for (var i = 0; i < arguments.length; i++) {
            var c = arguments[i];
            c.parent = t;
            if (t.children.indexOf(c) == -1) t.children.push(c);
        }
        t.numChildren = t.children.length;
        return t;
    }

    /**
     *  @method remove
     *  @memberof SQR.Transform2d.prototype
     *   
     *  @description Removes a child transform. Accepts multiple arguments 
     *  but all of them need to be of type {SQR.Transform2D}
     */
    t.remove = function() {
        for (var i = 0; i < arguments.length; i++) {
            var c = arguments[i];
            var j = t.children.indexOf(c);
            if (j == -1) return false;
            c.parent = null;
            t.children.splice(j, 1);
        }
        t.numChildren = t.children.length;
        return t;
    }

    /**
     *  @method removeAll
     *  @memberof SQR.Transform2d.prototype
     *   
     *  @description Removes all children transform.
     */
    t.removeAll = function() {
        t.children.length = 0;
        t.numChildren = 0;
    }

    /**
     *  @method contains
     *  @memberof SQR.Transform2d.prototype
     *   
     *  @description Checks if transform is child of this transfom
     *  @param {SQR.Transform2D} c the transform to look for
     */
    t.contains = function(c) {
        return t.children.indexOf(c) > -1;
    }

    /**
     *  @method recurse
     *  @memberof SQR.Transform2d.prototype
     *   
     *  @description Execute this function on all the child transforms including this current one.
     *
     *  @param {function} f the function that will be called on each child. 
     *  This function will receive the transform as argument.
     *
     *  @param {boolean} excludeSelf if set to true, the function will only be called for all 
     *  the ancestors of the Transform.
     */
    t.recurse = function(f, excludeSelf) {
       if(!excludeSelf) f(t);
        for (var i = 0; i < t.numChildren; i++) {
            t.children[i].recurse(f);
        }
    }

    t.draw = function(context) {
        var c = context;

    	c.save();
    	c.translate(t.position.x, t.position.y);
    	c.rotate(t.rotation);

        c.scale(t.scale.x, t.scale.y);

    	// First draw the children, then self, so that alpha/scale do not affect children
        // * Not sure why scale should not affect children, so moving this before drawing children (check again)

    	for(var i = 0; i < t.numChildren; i++) t.children[i].draw(c);

        if(t.alpha < 1) c.globalAlpha = t.alpha;
        // c.scale(t.scale.x, t.scale.y);
        if(t.shape) t.shape(c, t);

    	c.restore();
	}

	return t;
}

SQR.Transform2dCount = 0;