From 75a946afaac252f9c976f5121d7c8d36fb6f1da4 Mon Sep 17 00:00:00 2001 From: Jono Brandel Date: Mon, 13 May 2013 17:28:07 -0700 Subject: [PATCH] added a matrix property to all shapes for advanced transformations --- build/two.clean.js | 22 ++++++++++++++-------- build/two.js | 22 ++++++++++++++-------- build/two.min.js | 19 ++++++++++--------- src/matrix.js | 8 ++++---- src/shape.js | 10 ++++++++-- src/vector.js | 4 ++-- 6 files changed, 52 insertions(+), 33 deletions(-) diff --git a/build/two.clean.js b/build/two.clean.js index a10d38f78..5c5c2bae9 100644 --- a/build/two.clean.js +++ b/build/two.clean.js @@ -1210,7 +1210,7 @@ }, set: function(v) { x = v; - this.trigger('change', 'x'); + this.trigger(Two.Events.change, 'x'); } }); @@ -1220,7 +1220,7 @@ }, set: function(v) { y = v; - this.trigger('change', 'y'); + this.trigger(Two.Events.change, 'y'); } }); @@ -1440,7 +1440,7 @@ }); - _.extend(Matrix.prototype, { + _.extend(Matrix.prototype, Backbone.Events, { /** * Takes an array of elements or the arguments list itself to @@ -1460,7 +1460,7 @@ } }, this); - return this; + return this.trigger(Two.Events.change); }, @@ -1490,7 +1490,7 @@ this.elements[i] = v * a; }, this); - return this; + return this.trigger(Two.Events.change); } @@ -1536,7 +1536,7 @@ this.elements[7] = A6 * B1 + A7 * B4 + A8 * B7; this.elements[8] = A6 * B2 + A7 * B5 + A8 * B8; - return this; + return this.trigger(Two.Events.change); }, @@ -3215,11 +3215,12 @@ .identity() .translate(this.translation.x, this.translation.y) .scale(this.scale) - .rotate(this.rotation); + .rotate(this.rotation) + .multiply.apply(this._matrix, this.matrix.elements); this.trigger(Two.Events.change, this.id, 'matrix', transform, this.scale); }, this), 0); - this._rotation = 'rotation'; + this._rotation = 0; Object.defineProperty(this, 'rotation', { get: function() { @@ -3249,6 +3250,11 @@ this.translation.bind(Two.Events.change, updateMatrix); + // Add a public matrix for advanced transformations. + // Only edit this if you're a *boss* + this.matrix = new Two.Matrix(); + this.matrix.bind(Two.Events.change, updateMatrix); + if (!!limited) { return this; } diff --git a/build/two.js b/build/two.js index 3c81f00d6..b163e6bcc 100644 --- a/build/two.js +++ b/build/two.js @@ -2426,7 +2426,7 @@ var Backbone = Backbone || {}; }, set: function(v) { x = v; - this.trigger('change', 'x'); + this.trigger(Two.Events.change, 'x'); } }); @@ -2436,7 +2436,7 @@ var Backbone = Backbone || {}; }, set: function(v) { y = v; - this.trigger('change', 'y'); + this.trigger(Two.Events.change, 'y'); } }); @@ -2656,7 +2656,7 @@ var Backbone = Backbone || {}; }); - _.extend(Matrix.prototype, { + _.extend(Matrix.prototype, Backbone.Events, { /** * Takes an array of elements or the arguments list itself to @@ -2676,7 +2676,7 @@ var Backbone = Backbone || {}; } }, this); - return this; + return this.trigger(Two.Events.change); }, @@ -2706,7 +2706,7 @@ var Backbone = Backbone || {}; this.elements[i] = v * a; }, this); - return this; + return this.trigger(Two.Events.change); } @@ -2752,7 +2752,7 @@ var Backbone = Backbone || {}; this.elements[7] = A6 * B1 + A7 * B4 + A8 * B7; this.elements[8] = A6 * B2 + A7 * B5 + A8 * B8; - return this; + return this.trigger(Two.Events.change); }, @@ -4431,11 +4431,12 @@ var Backbone = Backbone || {}; .identity() .translate(this.translation.x, this.translation.y) .scale(this.scale) - .rotate(this.rotation); + .rotate(this.rotation) + .multiply.apply(this._matrix, this.matrix.elements); this.trigger(Two.Events.change, this.id, 'matrix', transform, this.scale); }, this), 0); - this._rotation = 'rotation'; + this._rotation = 0; Object.defineProperty(this, 'rotation', { get: function() { @@ -4465,6 +4466,11 @@ var Backbone = Backbone || {}; this.translation.bind(Two.Events.change, updateMatrix); + // Add a public matrix for advanced transformations. + // Only edit this if you're a *boss* + this.matrix = new Two.Matrix(); + this.matrix.bind(Two.Events.change, updateMatrix); + if (!!limited) { return this; } diff --git a/build/two.min.js b/build/two.min.js index 05f9a2c20..d02cac775 100644 --- a/build/two.min.js +++ b/build/two.min.js @@ -48,15 +48,15 @@ this.frameCount++;a&&(this.timeDelta=parseFloat((b-this._lastFrame).toFixed(3))) (e-b)/2;var c=[new j.Vector(-d,-e),new j.Vector(d,e)],c=(new j.Polygon(c)).noFill();c.translation.set(a+d,b+e);this.scene.add(c);return c},makeRectangle:function(a,b,d,e){d/=2;e/=2;e=[new j.Vector(d,e),new j.Vector(-d,e),new j.Vector(-d,-e),new j.Vector(d,-e)];e=new j.Polygon(e,!0);e.translation.set(a,b);this.scene.add(e);return e},makeCircle:function(a,b,d){return this.makeEllipse(a,b,d,d)},makeEllipse:function(a,b,d,c){var l=j.Resolution,f=_.map(_.range(l),function(a){var b=a/l*e;a=d*m(b);b=c*p(b); return new j.Vector(a,b)},this),f=new j.Polygon(f,!0,!0);f.translation.set(a,b);this.scene.add(f);return f},makeCurve:function(a){var b=arguments.length,d=a;if(!_.isArray(a))for(var d=[],e=0;ethis.distanceTo(f)},lerp:function(f,n){return this.set((f.x-this.x)* n+this.x,(f.y-this.y)*n+this.y)},isZero:function(){return 1E-4>this.length()}})})();(function(){_.range(6);var q=Math.cos,f=Math.sin,n=Math.tan,p=Two.Matrix=function(m,c,b,l,a,e){this.elements=new Two.Array(9);var f=m;_.isArray(f)||(f=_.toArray(arguments));this.identity().set(f)};_.extend(p,{Identity:[1,0,0,0,1,0,0,0,1],Multiply:function(m,c){if(3>=c.length){var b=c[0]||0,l=c[1]||0,a=c[2]||0;return{x:m[0]*b+m[1]*l+m[2]*a,y:m[3]*b+m[4]*l+m[5]*a,z:m[6]*b+m[7]*l+m[8]*a}}var b=m[0],l=m[1],a=m[2],e=m[3],f=m[4],A=m[5],s=m[6],j=m[7],n=m[8],p=c[0],q=c[1],r=c[2],w=c[3],t=c[4],v=c[5],d=c[6], -y=c[7],D=c[8];return[b*p+l*w+a*d,b*q+l*t+a*y,b*r+l*v+a*D,e*p+f*w+A*d,e*q+f*t+A*y,e*r+f*v+A*D,s*p+j*w+n*d,s*q+j*t+n*y,s*r+j*v+n*D]}});_.extend(p.prototype,{set:function(m,c,b,l,a,e){var f=m;_.isArray(f)||(f=_.toArray(arguments));_.each(f,function(a,b){_.isNumber(a)&&(this.elements[b]=a)},this);return this},identity:function(){this.set(p.Identity);return this},multiply:function(m,c,b,l,a,e,f,A,s){var j=arguments,n=j.length;if(1>=n)return _.each(this.elements,function(a,b){this.elements[b]=a*m},this), -this;if(3>=n)return m=m||0,c=c||0,b=b||0,a=this.elements,{x:a[0]*m+a[1]*c+a[2]*b,y:a[3]*m+a[4]*c+a[5]*b,z:a[6]*m+a[7]*c+a[8]*b};n=this.elements;A0=n[0];A1=n[1];A2=n[2];A3=n[3];A4=n[4];A5=n[5];A6=n[6];A7=n[7];A8=n[8];B0=j[0];B1=j[1];B2=j[2];B3=j[3];B4=j[4];B5=j[5];B6=j[6];B7=j[7];B8=j[8];this.elements[0]=A0*B0+A1*B3+A2*B6;this.elements[1]=A0*B1+A1*B4+A2*B7;this.elements[2]=A0*B2+A1*B5+A2*B8;this.elements[3]=A3*B0+A4*B3+A5*B6;this.elements[4]=A3*B1+A4*B4+A5*B7;this.elements[5]=A3*B2+A4*B5+A5*B8;this.elements[6]= -A6*B0+A7*B3+A8*B6;this.elements[7]=A6*B1+A7*B4+A8*B7;this.elements[8]=A6*B2+A7*B5+A8*B8;return this},scale:function(f,c){1>=arguments.length&&(c=f);return this.multiply(f,0,0,0,c,0,0,0,1)},rotate:function(m){var c=q(m);m=f(m);return this.multiply(c,-m,0,m,c,0,0,0,1)},translate:function(f,c){return this.multiply(1,0,f,0,1,c,0,0,1)},skewX:function(f){f=n(f);return this.multiply(1,f,0,0,1,0,0,0,1)},skewY:function(f){f=n(f);return this.multiply(1,0,0,f,1,0,0,0,1)},toString:function(){return this.toArray().join(" ")}, -toArray:function(f){var c=this.elements,b=parseFloat(c[0].toFixed(3)),l=parseFloat(c[1].toFixed(3)),a=parseFloat(c[2].toFixed(3)),e=parseFloat(c[3].toFixed(3)),u=parseFloat(c[4].toFixed(3)),n=parseFloat(c[5].toFixed(3));if(f){f=parseFloat(c[6].toFixed(3));var s=parseFloat(c[7].toFixed(3)),c=parseFloat(c[8].toFixed(3));return[b,e,f,l,u,s,a,n,c]}return[b,e,l,u,a,n]},clone:function(){var f=this.elements[0],c=this.elements[1],b=this.elements[2],l=this.elements[3],a=this.elements[4],e=this.elements[5]; -g=this.elements[6];h=this.elements[7];i=this.elements[8];return new Two.Matrix(f,c,b,l,a,e,g,h,i)}})})();(function(){function q(c){var b={},l=c.id,a=c.translation,e=c.rotation,f=c.scale,n=c.stroke,s=c.linewidth,j=c.fill,x=c.opacity,q=c.visible,z=c.cap,r=c.join,w=c.miter,t=c.curved,v=c.closed,d=c.vertices;l&&(b.id=m.Identifier+l);a&&(_.isNumber(f)&&_.isNumber(e))&&(b.transform="matrix("+c._matrix.toString()+")");n&&(b.stroke=n);j&&(b.fill=j);x&&(b["stroke-opacity"]=b["fill-opacity"]=x);b.visibility=q?"visible":"hidden";z&&(b["stroke-linecap"]=z);r&&(b["stroke-linejoin"]=r);w&&(b["stroke-miterlimit"]= +y=c[7],D=c[8];return[b*p+l*w+a*d,b*q+l*t+a*y,b*r+l*v+a*D,e*p+f*w+A*d,e*q+f*t+A*y,e*r+f*v+A*D,s*p+j*w+n*d,s*q+j*t+n*y,s*r+j*v+n*D]}});_.extend(p.prototype,Backbone.Events,{set:function(m,c,b,l,a,e){var f=m;_.isArray(f)||(f=_.toArray(arguments));_.each(f,function(a,b){_.isNumber(a)&&(this.elements[b]=a)},this);return this.trigger(Two.Events.change)},identity:function(){this.set(p.Identity);return this},multiply:function(m,c,b,l,a,e,f,A,s){var j=arguments,n=j.length;if(1>=n)return _.each(this.elements, +function(a,b){this.elements[b]=a*m},this),this.trigger(Two.Events.change);if(3>=n)return m=m||0,c=c||0,b=b||0,a=this.elements,{x:a[0]*m+a[1]*c+a[2]*b,y:a[3]*m+a[4]*c+a[5]*b,z:a[6]*m+a[7]*c+a[8]*b};n=this.elements;A0=n[0];A1=n[1];A2=n[2];A3=n[3];A4=n[4];A5=n[5];A6=n[6];A7=n[7];A8=n[8];B0=j[0];B1=j[1];B2=j[2];B3=j[3];B4=j[4];B5=j[5];B6=j[6];B7=j[7];B8=j[8];this.elements[0]=A0*B0+A1*B3+A2*B6;this.elements[1]=A0*B1+A1*B4+A2*B7;this.elements[2]=A0*B2+A1*B5+A2*B8;this.elements[3]=A3*B0+A4*B3+A5*B6;this.elements[4]= +A3*B1+A4*B4+A5*B7;this.elements[5]=A3*B2+A4*B5+A5*B8;this.elements[6]=A6*B0+A7*B3+A8*B6;this.elements[7]=A6*B1+A7*B4+A8*B7;this.elements[8]=A6*B2+A7*B5+A8*B8;return this.trigger(Two.Events.change)},scale:function(f,c){1>=arguments.length&&(c=f);return this.multiply(f,0,0,0,c,0,0,0,1)},rotate:function(m){var c=q(m);m=f(m);return this.multiply(c,-m,0,m,c,0,0,0,1)},translate:function(f,c){return this.multiply(1,0,f,0,1,c,0,0,1)},skewX:function(f){f=n(f);return this.multiply(1,f,0,0,1,0,0,0,1)},skewY:function(f){f= +n(f);return this.multiply(1,0,0,f,1,0,0,0,1)},toString:function(){return this.toArray().join(" ")},toArray:function(f){var c=this.elements,b=parseFloat(c[0].toFixed(3)),l=parseFloat(c[1].toFixed(3)),a=parseFloat(c[2].toFixed(3)),e=parseFloat(c[3].toFixed(3)),u=parseFloat(c[4].toFixed(3)),n=parseFloat(c[5].toFixed(3));if(f){f=parseFloat(c[6].toFixed(3));var s=parseFloat(c[7].toFixed(3)),c=parseFloat(c[8].toFixed(3));return[b,e,f,l,u,s,a,n,c]}return[b,e,l,u,a,n]},clone:function(){var f=this.elements[0], +c=this.elements[1],b=this.elements[2],l=this.elements[3],a=this.elements[4],e=this.elements[5];g=this.elements[6];h=this.elements[7];i=this.elements[8];return new Two.Matrix(f,c,b,l,a,e,g,h,i)}})})();(function(){function q(c){var b={},l=c.id,a=c.translation,e=c.rotation,f=c.scale,n=c.stroke,s=c.linewidth,j=c.fill,x=c.opacity,q=c.visible,z=c.cap,r=c.join,w=c.miter,t=c.curved,v=c.closed,d=c.vertices;l&&(b.id=m.Identifier+l);a&&(_.isNumber(f)&&_.isNumber(e))&&(b.transform="matrix("+c._matrix.toString()+")");n&&(b.stroke=n);j&&(b.fill=j);x&&(b["stroke-opacity"]=b["fill-opacity"]=x);b.visibility=q?"visible":"hidden";z&&(b["stroke-linecap"]=z);r&&(b["stroke-linejoin"]=r);w&&(b["stroke-miterlimit"]= w);s&&(b["stroke-width"]=s);d&&(b.d=p.toString(d,v,t));return b}var f=Two.Utils.getCurveFromPoints,n=Two.Utils.mod,p={version:1.1,ns:"http://www.w3.org/2000/svg",xlink:"http://www.w3.org/1999/xlink",createElement:function(c,b){var l=c.toLowerCase(),a=document.createElementNS(this.ns,l);"svg"===l&&(b=_.defaults(b||{},{version:this.version}));_.isObject(b)&&this.setAttributes(a,b);return a},setAttributes:function(c,b){_.each(b,function(b,a){this.setAttribute(a,b)},c);return this},removeAttributes:function(c, b){_.each(b,function(b){this.removeAttribute(b)},c);return this},toString:function(c,b,l){var a=c.length,e=a-1;if(!l)return _.map(c,function(a,c){var j;j=(0>=c?"M":"L")+(" "+a.x.toFixed(3)+" "+a.y.toFixed(3));c>=e&&b&&(j+=" Z");return j}).join(" ");var m=f(c,b);return _.map(m,function(c,l){var j,f=b?n(l-1,a):Math.max(l-1,0),p=b?n(l+1,a):Math.min(l+1,e);j=m[f];var p=m[p],f=j.v.x.toFixed(3),q=j.v.y.toFixed(3),r=c.u.x.toFixed(3),w=c.u.y.toFixed(3),t=c.x.toFixed(3),v=c.y.toFixed(3);j=0>=l?"M "+t+" "+ v:"C "+f+" "+q+" "+r+" "+w+" "+t+" "+v;l>=e&&b&&(f=c.v.x.toFixed(3),q=c.v.y.toFixed(3),r=p.u.x.toFixed(3),w=p.u.y.toFixed(3),t=p.x.toFixed(3),v=p.y.toFixed(3),j=j+(" C "+f+" "+q+" "+r+" "+w+" "+t+" "+v)+" Z");return j}).join(" ")}},m=Two[Two.Types.svg]=function(){this.count=0;this.domElement=p.createElement("svg");this.elements=[];this.domElement.style.visibility="hidden";this.unveil=_.once(_.bind(function(){this.domElement.style.visibility="visible"},this))};_.extend(m,{Identifier:"two-"});_.extend(m.prototype, @@ -84,9 +84,10 @@ a);if(!this.ctx)throw new Two.Utils.Error("unable to create a webgl context. Try a.disable(a.DEPTH_TEST);a.enable(a.BLEND);a.blendEquationSeparate(a.FUNC_ADD,a.FUNC_ADD);a.blendFuncSeparate(a.SRC_ALPHA,a.ONE_MINUS_SRC_ALPHA,a.ONE,a.ONE_MINUS_SRC_ALPHA)};_.extend(l,{Group:m,Element:c,getStyles:function(a){var e={},f=a.id,l=a._matrix,m=a.stroke,j=a.linewidth,p=a.fill,q=a.opacity,z=a.visible,r=a.cap,w=a.join,t=a.miter,v=a.curved,d=a.closed,y=a.vertices;f&&(e.id=f);_.isObject(l)&&(e.matrix=e._matrix=l.toArray(!0),e.scale=e._scale=1);m&&(e.stroke=m);p&&(e.fill=p);_.isNumber(q)&&(e.opacity= q);r&&(e.cap=r);w&&(e.join=w);t&&(e.miter=t);j&&(e.linewidth=j);y&&(e.vertices=n(y,v,d),e.commands=e.vertices,e.rect=b.getBoundingClientRect(e.commands,e.linewidth,e.curved),e.triangles=b.getTriangles(e.rect));e.visible=!!z;e.curved=!!v;e.closed=!!d;a instanceof Two.Polygon&&(b.updateBuffer(this.ctx,e,this.program),c.prototype.updateTexture.call(e,this.ctx));return e},setStyles:function(a,c,f,l,m,j){var p=!1;/matrix/.test(c)?(a[c]=f.toArray(!0),_.isNumber(l)&&(p=a.scale!==l,a.scale=l),a.updateMatrix()): /(stroke|fill|opacity|cap|join|miter|linewidth)/.test(c)?(a[c]=f,a.rect=b.getBoundingClientRect(a.commands,a.linewidth,a.curved),a.triangles=b.getTriangles(a.rect),b.updateBuffer(this.ctx,a,this.program),p=!0):"vertices"===c?(_.isUndefined(l)||(a.closed=l),_.isUndefined(m)||(a.curved=m),j?a.commands=n(f,a.curved,a.closed):(a.vertices=n(f,a.curved,a.closed),a.commands=a.vertices),a.rect=b.getBoundingClientRect(a.vertices,a.linewidth,a.curved),a.triangles=b.getTriangles(a.rect),b.updateBuffer(this.ctx, -a,this.program),p=!0):a[c]=f;p&&a.updateTexture(this.ctx)}});_.extend(l.prototype,Backbone.Events,q.prototype,{setSize:function(a,b){q.prototype.setSize.apply(this,arguments);this.ctx.viewport(0,0,a,b);var c=this.ctx.getUniformLocation(this.program,"u_resolution");this.ctx.uniform2f(c,a,b);return this},render:function(){if(_.isNull(this.stage))return this;this.stage.render(this.ctx,this.program);return this}})})();(function(){var q=Two.Shape=function(f){this._matrix=new Two.Matrix;var n=_.debounce(_.bind(function(){var f=this._matrix.identity().translate(this.translation.x,this.translation.y).scale(this.scale).rotate(this.rotation);this.trigger(Two.Events.change,this.id,"matrix",f,this.scale)},this),0);this._rotation="rotation";Object.defineProperty(this,"rotation",{get:function(){return this._rotation},set:function(f){this._rotation=f;n()}});this._scale="scale";Object.defineProperty(this,"scale",{get:function(){return this._scale}, -set:function(f){this._scale=f;n()}});this.translation=new Two.Vector;this.rotation=0;this.scale=1;this.translation.bind(Two.Events.change,n);if(f)return this;q.MakeGetterSetter(this,q.Properties);this.fill="#fff";this.stroke="#000";this.opacity=this.linewidth=1;this.visible=!0;this.join=this.cap="round";this.miter=1};_.extend(q,{Properties:"fill stroke linewidth opacity visible cap join miter".split(" "),MakeGetterSetter:function(f,n){_.isArray(n)||(n=[n]);_.each(n,function(n){var m="_"+n;Object.defineProperty(f, -n,{get:function(){return this[m]},set:function(c){this[m]=c;this.trigger(Two.Events.change,this.id,n,c,this)}})})}});_.extend(q.prototype,Backbone.Events,{addTo:function(f){f.add(this);return this},noFill:function(){this.fill="transparent";return this},noStroke:function(){this.stroke="transparent";return this},clone:function(){var f=new q;f.translation.copy(this.translation);_.each(q.Properties,function(n){f[n]=this[n]},this);return this}})})();(function(){var q=Two.Group=function(){Two.Shape.call(this,!0);delete this.stroke;delete this.fill;delete this.linewidth;delete this.opacity;delete this.cap;delete this.join;delete this.miter;q.MakeGetterSetter(this,Two.Shape.Properties);this.children={}};_.extend(q,{MakeGetterSetter:function(f,n){_.isArray(n)||(n=[n]);_.each(n,function(n){var m="_"+n;Object.defineProperty(f,n,{get:function(){return this[m]},set:function(c){this[m]=c;_.each(this.children,function(b){b[n]=c})}})})}});_.extend(q.prototype, +a,this.program),p=!0):a[c]=f;p&&a.updateTexture(this.ctx)}});_.extend(l.prototype,Backbone.Events,q.prototype,{setSize:function(a,b){q.prototype.setSize.apply(this,arguments);this.ctx.viewport(0,0,a,b);var c=this.ctx.getUniformLocation(this.program,"u_resolution");this.ctx.uniform2f(c,a,b);return this},render:function(){if(_.isNull(this.stage))return this;this.stage.render(this.ctx,this.program);return this}})})();(function(){var q=Two.Shape=function(f){this._matrix=new Two.Matrix;var n=_.debounce(_.bind(function(){var f=this._matrix.identity().translate(this.translation.x,this.translation.y).scale(this.scale).rotate(this.rotation).multiply.apply(this._matrix,this.matrix.elements);this.trigger(Two.Events.change,this.id,"matrix",f,this.scale)},this),0);this._rotation=0;Object.defineProperty(this,"rotation",{get:function(){return this._rotation},set:function(f){this._rotation=f;n()}});this._scale="scale";Object.defineProperty(this, +"scale",{get:function(){return this._scale},set:function(f){this._scale=f;n()}});this.translation=new Two.Vector;this.rotation=0;this.scale=1;this.translation.bind(Two.Events.change,n);this.matrix=new Two.Matrix;this.matrix.bind(Two.Events.change,n);if(f)return this;q.MakeGetterSetter(this,q.Properties);this.fill="#fff";this.stroke="#000";this.opacity=this.linewidth=1;this.visible=!0;this.join=this.cap="round";this.miter=1};_.extend(q,{Properties:"fill stroke linewidth opacity visible cap join miter".split(" "), +MakeGetterSetter:function(f,n){_.isArray(n)||(n=[n]);_.each(n,function(n){var m="_"+n;Object.defineProperty(f,n,{get:function(){return this[m]},set:function(c){this[m]=c;this.trigger(Two.Events.change,this.id,n,c,this)}})})}});_.extend(q.prototype,Backbone.Events,{addTo:function(f){f.add(this);return this},noFill:function(){this.fill="transparent";return this},noStroke:function(){this.stroke="transparent";return this},clone:function(){var f=new q;f.translation.copy(this.translation);_.each(q.Properties, +function(n){f[n]=this[n]},this);return this}})})();(function(){var q=Two.Group=function(){Two.Shape.call(this,!0);delete this.stroke;delete this.fill;delete this.linewidth;delete this.opacity;delete this.cap;delete this.join;delete this.miter;q.MakeGetterSetter(this,Two.Shape.Properties);this.children={}};_.extend(q,{MakeGetterSetter:function(f,n){_.isArray(n)||(n=[n]);_.each(n,function(n){var m="_"+n;Object.defineProperty(f,n,{get:function(){return this[m]},set:function(c){this[m]=c;_.each(this.children,function(b){b[n]=c})}})})}});_.extend(q.prototype, Two.Shape.prototype,{clone:function(f){f=f||this.parent;var n=_.map(this.children,function(m){return m.clone(f)}),p=new q;f.add(p);p.add(n);p.translation.copy(this.translation);p.rotation=this.rotation;p.scale=this.scale;return p},center:function(){var f=this.getBoundingClientRect();f.centroid={x:f.left+f.width/2,y:f.top+f.height/2};_.each(this.children,function(n){n.translation.subSelf(f.centroid)});this.translation.copy(f.centroid);return this},add:function(f){var n=f,p=this.children,m=this.parent, c=[];_.isArray(f)||(n=_.toArray(arguments));var b=_.bind(function(b,a,c,f,m,n){this.trigger(Two.Events.change,b,a,c,f,m,n)},this);_.each(n,function(f){var a=f.id,e=f.parent;_.isUndefined(a)&&(m.add(f),a=f.id);_.isUndefined(p[a])&&(e&&delete e.children[a],p[a]=f,f.parent=this,f.unbind(Two.Events.change).bind(Two.Events.change,b),c.push(a))},this);0=arguments.length&&m)return m.remove(this),this;_.isArray(f)||(n=_.toArray(arguments));_.each(n,function(b){var f=b.id;f in p&&(delete p[f],b.unbind(Two.Events.change),c.push(f))});0