/* The MIT License Copyright (c) 2010-2011-2012-2013-2014-2015 Abdul.R.Alargha [alargha1970@gmail.com] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Version: 0.4 build: 68 Created on: DATE: 2015-04-01 TIME: 01:03:47 */ /** * See LICENSE file. * * Library namespace. * CAAT stands for: Canvas Advanced Animation Toolkit. */ /** * @namespace */ var CAAT= CAAT || {}; /** * Common bind function. Allows to set an object's function as callback. Set for every function in the * javascript context. */ Function.prototype.bind= Function.prototype.bind || function() { var fn= this; // the function var args= Array.prototype.slice.call(arguments); // copy the arguments. var obj= args.shift(); // first parameter will be context 'this' return function() { return fn.apply( obj, args.concat(Array.prototype.slice.call(arguments))); } }; isArray= function(input) { return typeof(input)=='object'&&(input instanceof Array); }; isString= function(input){ return typeof(input)=='string'; }; /** * See LICENSE file. * * Extend a prototype with another to form a classical OOP inheritance procedure. * * @param subc {object} Prototype to define the base class * @param superc {object} Prototype to be extended (derived class). */ function extend(subc, superc) { var subcp = subc.prototype; // Class pattern. var F = function() { }; F.prototype = superc.prototype; subc.prototype = new F(); // chain prototypes. subc.superclass = superc.prototype; subc.prototype.constructor = subc; // Reset constructor. See Object Oriented Javascript for an in-depth explanation of this. if (superc.prototype.constructor === Object.prototype.constructor) { superc.prototype.constructor = superc; } // los metodos de superc, que no esten en esta clase, crear un metodo que // llama al metodo de superc. for (var method in subcp) { if (subcp.hasOwnProperty(method)) { subc.prototype[method] = subcp[method]; /** * Sintactic sugar to add a __super attribute on every overriden method. * Despite comvenient, it slows things down by 5fps. * * Uncomment at your own risk. * // tenemos en super un metodo con igual nombre. if ( superc.prototype[method]) { subc.prototype[method]= (function(fn, fnsuper) { return function() { var prevMethod= this.__super; this.__super= fnsuper; var retValue= fn.apply( this, Array.prototype.slice.call(arguments) ); this.__super= prevMethod; return retValue; }; })(subc.prototype[method], superc.prototype[method]); } */ } } } /** * Dynamic Proxy for an object or wrap/decorate a function. * * @param object * @param preMethod * @param postMethod * @param errorMethod */ function proxy(object, preMethod, postMethod, errorMethod) { // proxy a function if ( typeof object==='function' ) { if ( object.__isProxy ) { return object; } return (function(fn) { var proxyfn= function() { if ( preMethod ) { preMethod({ fn: fn, arguments: Array.prototype.slice.call(arguments)} ); } var retValue= null; try { // apply original function call with itself as context retValue= fn.apply(fn, Array.prototype.slice.call(arguments)); // everything went right on function call, then call // post-method hook if present if ( postMethod ) { retValue= postMethod({ fn: fn, arguments: Array.prototype.slice.call(arguments)} ); } } catch(e) { // an exeception was thrown, call exception-method hook if // present and return its result as execution result. if( errorMethod ) { retValue= errorMethod({ fn: fn, arguments: Array.prototype.slice.call(arguments), exception: e} ); } else { // since there's no error hook, just throw the exception throw e; } } // return original returned value to the caller. return retValue; }; proxyfn.__isProxy= true; for( var method in fn ) { if ( typeof fn[method]!=="function" ) { if (method!=="__object" && method!=="__isProxy") { (function(proxyfn, fn) { proxyfn.__defineGetter__( method, function() { return fn[method]; }); proxyfn.__defineSetter__( method, function(vale) { fn[method]= vale; }); })(proxyfn, fn); } } } return proxyfn; })(object); } /** * If not a function then only non privitive objects can be proxied. * If it is a previously created proxy, return the proxy itself. */ if ( !typeof object==='object' || isArray(object) || isString(object) || object.__isProxy ) { return object; } // Our proxy object class. var cproxy= function() {}; // A new proxy instance. var proxy= new cproxy(); // hold the proxied object as member. Needed to assign proper // context on proxy method call. proxy.__object= object; proxy.__isProxy= true; // For every element in the object to be proxied for( var method in object ) { // only function members if ( typeof object[method]==='function' ) { // add to the proxy object a method of equal signature to the // method present at the object to be proxied. // cache references of object, function and function name. proxy[method]= (function(proxy,fn,method) { return function() { // call pre-method hook if present. if ( preMethod ) { preMethod({ object: proxy.__object, method: method, arguments: Array.prototype.slice.call(arguments)} ); } var retValue= null; try { // apply original object call with proxied object as // function context. retValue= fn.apply( proxy.__object, arguments ); // everything went right on function call, the call // post-method hook if present if ( postMethod ) { postMethod({ object: proxy.__object, method: method, arguments: Array.prototype.slice.call(arguments)} ); } } catch(e) { // an exeception was thrown, call exception-method hook if // present and return its result as execution result. if( errorMethod ) { retValue= errorMethod({ object: proxy.__object, method: method, arguments: Array.prototype.slice.call(arguments), exception: e} ); } else { // since there's no error hook, just throw the exception throw e; } } // return original returned value to the caller. return retValue; }; })(proxy,object[method],method); } else { if (method!=="__object" && method!=="__isProxy") { (function(proxy, method) { proxy.__defineGetter__( method, function() { return proxy.__object[method]; }); proxy.__defineSetter__( method, function(vale) { proxy.__object[method]= vale; }); })(proxy, method); } } } // return our newly created and populated of functions proxy object. return proxy; } /** proxy sample usage var c0= new Meetup.C1(5); var cp1= proxy( c1, function() { console.log('pre method on object: ', arguments[0].object.toString(), arguments[0].method, arguments[0].arguments ); }, function() { console.log('post method on object: ', arguments[0].object.toString(), arguments[0].method, arguments[0].arguments ); }, function() { console.log('exception on object: ', arguments[0].object.toString(), arguments[0].method, arguments[0].arguments, arguments[0].exception); return -1; }); **/ function proxify( ns, preMethod, postMethod, errorMethod, getter, setter ) { var nns= "__"+ns+"__"; var obj= window; var path= ns.split("."); while( path.length>1) { obj= obj[ path.shift() ]; } window[nns] = obj[path]; (function(obj,path, nns,ns) { var newC= function() { console.log("Creating object of type proxy["+ns+"]"); var obj= new window[nns]( Array.prototype.slice.call(arguments) ); obj.____name= ns; return proxyObject( obj, preMethod, postMethod, errorMethod, getter, setter ); }; // set new constructor function prototype as previous one. newC.prototype= window[nns].prototype; for( var method in obj[path] ) { if ( typeof obj[path][method]!=="function" ) { if (method!=="__object" && method!=="__isProxy") { (function(prevConstructor, method, newC) { newC.__defineGetter__( method, function() { return prevConstructor[method]; }); newC.__defineSetter__( method, function(vale) { prevConstructor[method]= vale; }); })(obj[path],method,newC); } } } obj[path]= newC; })(obj,path,nns,ns); } function proxyObject(object, preMethod, postMethod, errorMethod, getter, setter) { /** * If not a function then only non privitive objects can be proxied. * If it is a previously created proxy, return the proxy itself. */ if ( !typeof object==='object' || isArray(object) || isString(object) || object.__isProxy ) { return object; } // hold the proxied object as member. Needed to assign proper // context on proxy method call. object.$proxy__isProxy= true; // For every element in the object to be proxied for( var method in object ) { if ( method==="constructor" ) { continue; } // only function members if ( typeof object[method]==='function' ) { var fn= object[method]; object["$proxy__"+method]= fn; object[method]= (function(object,fn,fnname) { return function() { var args= Array.prototype.slice.call(arguments); // call pre-method hook if present. if ( preMethod ) { preMethod({ object: object, objectName: object.____name, method: fnname, arguments: args } ); } var retValue= null; try { // apply original object call with proxied object as // function context. retValue= fn.apply( object, args ); // everything went right on function call, the call // post-method hook if present if ( postMethod ) { var rr= postMethod({ object: object, objectName: object.____name, method: fnname, arguments: args } ); if ( typeof rr!=="undefined" ) { //retValue= rr; } } } catch(e) { // an exeception was thrown, call exception-method hook if // present and return its result as execution result. if( errorMethod ) { retValue= errorMethod({ object: object, objectName: object.____name, method: fnname, arguments: args, exception: e} ); } else { // since there's no error hook, just throw the exception throw e; } } // return original returned value to the caller. return retValue; }; })(object,fn,method); } else { if ( method!=="____name" ) { (function(object, attribute, getter, setter) { object["$proxy__"+attribute]= object[attribute]; object.__defineGetter__( attribute, function() { if ( getter) { getter( object.____name, attribute ); } return object["$proxy__"+attribute]; }); object.__defineSetter__( attribute, function (value) { object["$proxy__"+attribute] = value; if ( setter ) { setter( object.____name, attribute, value ); } }); })( object, method, getter, setter ); } } } // return our newly created and populated with functions proxied object. return object; }/** * See LICENSE file. * * Manages every Actor affine transformations. * Take into account that Canvas' renderingContext computes postive rotation clockwise, so hacks * to handle it properly are hardcoded. * * Contained classes are CAAT.Matrix and CAAT.MatrixStack. * **/ (function() { /** * * Define a matrix to hold three dimensional affine transforms. * * @constructor */ CAAT.Matrix3= function() { this.matrix= [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ]; this.fmatrix= [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]; return this; }; CAAT.Matrix3.prototype= { matrix: null, fmatrix:null, transformCoord : function(point) { var x= point.x; var y= point.y; var z= point.z; point.x= x*this.matrix[0][0] + y*this.matrix[0][1] + z*this.matrix[0][2] + this.matrix[0][3]; point.y= x*this.matrix[1][0] + y*this.matrix[1][1] + z*this.matrix[1][2] + this.matrix[1][3]; point.z= x*this.matrix[2][0] + y*this.matrix[2][1] + z*this.matrix[2][2] + this.matrix[2][3]; return point; }, initialize : function( x0,y0,z0, x1,y1,z1, x2,y2,z2 ) { this.identity( ); this.matrix[0][0]= x0; this.matrix[0][1]= y0; this.matrix[0][2]= z0; this.matrix[1][0]= x1; this.matrix[1][1]= y1; this.matrix[1][2]= z1; this.matrix[2][0]= x2; this.matrix[2][1]= y2; this.matrix[2][2]= z2; return this; }, initWithMatrix : function(matrixData) { this.matrix= matrixData; return this; }, flatten : function() { var d= this.fmatrix; var s= this.matrix; d[ 0]= s[0][0]; d[ 1]= s[1][0]; d[ 2]= s[2][0]; d[ 3]= s[3][0]; d[ 4]= s[0][1]; d[ 5]= s[1][1]; d[ 6]= s[2][1]; d[ 7]= s[2][1]; d[ 8]= s[0][2]; d[ 9]= s[1][2]; d[10]= s[2][2]; d[11]= s[3][2]; d[12]= s[0][3]; d[13]= s[1][3]; d[14]= s[2][3]; d[15]= s[3][3]; return this.fmatrix; }, /** * Set this matrix to identity matrix. * @return this */ identity : function() { for( var i=0; i<4; i++ ) { for( var j=0; j<4; j++ ) { this.matrix[i][j]= (i===j) ? 1.0 : 0.0; } } return this; }, /** * Get this matri'x internal representation data. The bakced structure is a 4x4 array of number. */ getMatrix : function() { return this.matrix; }, /** * Multiply this matrix by a created rotation matrix. The rotation matrix is set up to rotate around * xy axis. * * @param xy {Number} radians to rotate. * * @return this */ rotateXY : function( xy ) { return this.rotate( xy, 0, 0 ); }, /** * Multiply this matrix by a created rotation matrix. The rotation matrix is set up to rotate around * xz axis. * * @param xz {Number} radians to rotate. * * @return this */ rotateXZ : function( xz ) { return this.rotate( 0, xz, 0 ); }, /** * Multiply this matrix by a created rotation matrix. The rotation matrix is set up to rotate aroind * yz axis. * * @param yz {Number} radians to rotate. * * @return this */ rotateYZ : function( yz ) { return this.rotate( 0, 0, yz ); }, /** * * @param xy * @param xz * @param yz */ setRotate : function( xy, xz, yz ) { var m= this.rotate(xy,xz,yz); this.copy(m); return this; }, /** * Creates a matrix to represent arbitrary rotations around the given planes. * @param xy {number} radians to rotate around xy plane. * @param xz {number} radians to rotate around xz plane. * @param yz {number} radians to rotate around yz plane. * * @return {CAAT.Matrix3} a newly allocated matrix. * @static */ rotate : function( xy, xz, yz ) { var res=new CAAT.Matrix3(); var s,c,m; if (xy!==0) { m =new CAAT.Matrix3( ); s=Math.sin(xy); c=Math.cos(xy); m.matrix[1][1]=c; m.matrix[1][2]=-s; m.matrix[2][1]=s; m.matrix[2][2]=c; res.multiply(m); } if (xz!==0) { m =new CAAT.Matrix3( ); s=Math.sin(xz); c=Math.cos(xz); m.matrix[0][0]=c; m.matrix[0][2]=-s; m.matrix[2][0]=s; m.matrix[2][2]=c; res.multiply(m); } if (yz!==0) { m =new CAAT.Matrix3( ); s=Math.sin(yz); c=Math.cos(yz); m.matrix[0][0]=c; m.matrix[0][1]=-s; m.matrix[1][0]=s; m.matrix[1][1]=c; res.multiply(m); } return res; }, /** * Creates a new matrix being a copy of this matrix. * @return {CAAT.Matrix3} a newly allocated matrix object. */ getClone : function() { var m= new CAAT.Matrix3( ); m.copy(this); return m; }, /** * Multiplies this matrix by another matrix. * * @param n {CAAT.Matrix3} a CAAT.Matrix3 object. * @return this */ multiply : function( m ) { var n= this.getClone( ); var nm= n.matrix; var n00= nm[0][0]; var n01= nm[0][1]; var n02= nm[0][2]; var n03= nm[0][3]; var n10= nm[1][0]; var n11= nm[1][1]; var n12= nm[1][2]; var n13= nm[1][3]; var n20= nm[2][0]; var n21= nm[2][1]; var n22= nm[2][2]; var n23= nm[2][3]; var n30= nm[3][0]; var n31= nm[3][1]; var n32= nm[3][2]; var n33= nm[3][3]; var mm= m.matrix; var m00= mm[0][0]; var m01= mm[0][1]; var m02= mm[0][2]; var m03= mm[0][3]; var m10= mm[1][0]; var m11= mm[1][1]; var m12= mm[1][2]; var m13= mm[1][3]; var m20= mm[2][0]; var m21= mm[2][1]; var m22= mm[2][2]; var m23= mm[2][3]; var m30= mm[3][0]; var m31= mm[3][1]; var m32= mm[3][2]; var m33= mm[3][3]; this.matrix[0][0] = n00*m00 + n01*m10 + n02*m20 + n03*m30; this.matrix[0][1] = n00*m01 + n01*m11 + n02*m21 + n03*m31; this.matrix[0][2] = n00*m02 + n01*m12 + n02*m22 + n03*m32; this.matrix[0][3] = n00*m03 + n01*m13 + n02*m23 + n03*m33; this.matrix[1][0] = n10*m00 + n11*m10 + n12*m20 + n13*m30; this.matrix[1][1] = n10*m01 + n11*m11 + n12*m21 + n13*m31; this.matrix[1][2] = n10*m02 + n11*m12 + n12*m22 + n13*m32; this.matrix[1][3] = n10*m03 + n11*m13 + n12*m23 + n13*m33; this.matrix[2][0] = n20*m00 + n21*m10 + n22*m20 + n23*m30; this.matrix[2][1] = n20*m01 + n21*m11 + n22*m21 + n23*m31; this.matrix[2][2] = n20*m02 + n21*m12 + n22*m22 + n23*m32; this.matrix[2][3] = n20*m03 + n21*m13 + n22*m23 + n23*m33; return this; }, /** * Pre multiplies this matrix by a given matrix. * * @param m {CAAT.Matrix3} a CAAT.Matrix3 object. * * @return this */ premultiply : function(m) { var n= this.getClone( ); var nm= n.matrix; var n00= nm[0][0]; var n01= nm[0][1]; var n02= nm[0][2]; var n03= nm[0][3]; var n10= nm[1][0]; var n11= nm[1][1]; var n12= nm[1][2]; var n13= nm[1][3]; var n20= nm[2][0]; var n21= nm[2][1]; var n22= nm[2][2]; var n23= nm[2][3]; var n30= nm[3][0]; var n31= nm[3][1]; var n32= nm[3][2]; var n33= nm[3][3]; var mm= m.matrix; var m00= mm[0][0]; var m01= mm[0][1]; var m02= mm[0][2]; var m03= mm[0][3]; var m10= mm[1][0]; var m11= mm[1][1]; var m12= mm[1][2]; var m13= mm[1][3]; var m20= mm[2][0]; var m21= mm[2][1]; var m22= mm[2][2]; var m23= mm[2][3]; var m30= mm[3][0]; var m31= mm[3][1]; var m32= mm[3][2]; var m33= mm[3][3]; this.matrix[0][0] = n00*m00 + n01*m10 + n02*m20; this.matrix[0][1] = n00*m01 + n01*m11 + n02*m21; this.matrix[0][2] = n00*m02 + n01*m12 + n02*m22; this.matrix[0][3] = n00*m03 + n01*m13 + n02*m23 + n03; this.matrix[1][0] = n10*m00 + n11*m10 + n12*m20; this.matrix[1][1] = n10*m01 + n11*m11 + n12*m21; this.matrix[1][2] = n10*m02 + n11*m12 + n12*m22; this.matrix[1][3] = n10*m03 + n11*m13 + n12*m23 + n13; this.matrix[2][0] = n20*m00 + n21*m10 + n22*m20; this.matrix[2][1] = n20*m01 + n21*m11 + n22*m21; this.matrix[2][2] = n20*m02 + n21*m12 + n22*m22; this.matrix[2][3] = n20*m03 + n21*m13 + n22*m23 + n23; return this; }, /** * Set this matrix translation values to be the given parameters. * * @param x {number} x component of translation point. * @param y {number} y component of translation point. * @param z {number} z component of translation point. * * @return this */ setTranslate : function(x,y,z) { this.identity(); this.matrix[0][3]=x; this.matrix[1][3]=y; this.matrix[2][3]=z; return this; }, /** * Create a translation matrix. * @param x {number} * @param y {number} * @param z {number} * @return {CAAT.Matrix3} a new matrix. */ translate : function( x,y,z ) { var m= new CAAT.Matrix3(); m.setTranslate( x,y,z ); return m; }, setScale : function( sx, sy, sz ) { this.identity(); this.matrix[0][0]= sx; this.matrix[1][1]= sy; this.matrix[2][2]= sz; return this; }, scale : function( sx, sy, sz ) { var m= new CAAT.Matrix3(); m.setScale(sx,sy,sz); return m; }, /** * Set this matrix as the rotation matrix around the given axes. * @param xy {number} radians of rotation around z axis. * @param xz {number} radians of rotation around y axis. * @param yz {number} radians of rotation around x axis. * * @return this */ rotateModelView : function( xy, xz, yz ) { var sxy= Math.sin( xy ); var sxz= Math.sin( xz ); var syz= Math.sin( yz ); var cxy= Math.cos( xy ); var cxz= Math.cos( xz ); var cyz= Math.cos( yz ); this.matrix[0][0]= cxz*cxy; this.matrix[0][1]= -cxz*sxy; this.matrix[0][2]= sxz; this.matrix[0][3]= 0; this.matrix[1][0]= syz*sxz*cxy+sxy*cyz; this.matrix[1][1]= cyz*cxy-syz*sxz*sxy; this.matrix[1][2]= -syz*cxz; this.matrix[1][3]= 0; this.matrix[2][0]= syz*sxy-cyz*sxz*cxy; this.matrix[2][1]= cyz*sxz*sxy+syz*cxy; this.matrix[2][2]= cyz*cxz; this.matrix[2][3]= 0; this.matrix[3][0]= 0; this.matrix[3][1]= 0; this.matrix[3][2]= 0; this.matrix[3][3]= 1; return this; }, /** * Copy a given matrix values into this one's. * @param m {CAAT.Matrix} a matrix * * @return this */ copy : function( m ) { for( var i=0; i<4; i++ ) { for( var j=0; j<4; j++ ) { this.matrix[i][j]= m.matrix[i][j]; } } return this; }, /** * Calculate this matrix's determinant. * @return {number} matrix determinant. */ calculateDeterminant: function () { var mm= this.matrix; var m11= mm[0][0], m12= mm[0][1], m13= mm[0][2], m14= mm[0][3], m21= mm[1][0], m22= mm[1][1], m23= mm[1][2], m24= mm[1][3], m31= mm[2][0], m32= mm[2][1], m33= mm[2][2], m34= mm[2][3], m41= mm[3][0], m42= mm[3][1], m43= mm[3][2], m44= mm[3][3]; return m14 * m22 * m33 * m41 + m12 * m24 * m33 * m41 + m14 * m23 * m31 * m42 + m13 * m24 * m31 * m42 + m13 * m21 * m34 * m42 + m11 * m23 * m34 * m42 + m14 * m21 * m32 * m43 + m11 * m24 * m32 * m43 + m13 * m22 * m31 * m44 + m12 * m23 * m31 * m44 + m12 * m21 * m33 * m44 + m11 * m22 * m33 * m44 + m14 * m23 * m32 * m41 - m13 * m24 * m32 * m41 - m13 * m22 * m34 * m41 - m12 * m23 * m34 * m41 - m14 * m21 * m33 * m42 - m11 * m24 * m33 * m42 - m14 * m22 * m31 * m43 - m12 * m24 * m31 * m43 - m12 * m21 * m34 * m43 - m11 * m22 * m34 * m43 - m13 * m21 * m32 * m44 - m11 * m23 * m32 * m44; }, /** * Return a new matrix which is this matrix's inverse matrix. * @return {CAAT.Matrix3} a new matrix. */ getInverse : function() { var mm= this.matrix; var m11 = mm[0][0], m12 = mm[0][1], m13 = mm[0][2], m14 = mm[0][3], m21 = mm[1][0], m22 = mm[1][1], m23 = mm[1][2], m24 = mm[1][3], m31 = mm[2][0], m32 = mm[2][1], m33 = mm[2][2], m34 = mm[2][3], m41 = mm[3][0], m42 = mm[3][1], m43 = mm[3][2], m44 = mm[3][3]; var m2= new CAAT.Matrix3(); m2.matrix[0][0]= m23*m34*m42 + m24*m32*m43 + m22*m33*m44 - m24*m33*m42 - m22*m34*m43 - m23*m32*m44; m2.matrix[0][1]= m14*m33*m42 + m12*m34*m43 + m13*m32*m44 - m12*m33*m44 - m13*m34*m42 - m14*m32*m43; m2.matrix[0][2]= m13*m24*m42 + m12*m23*m44 + m14*m22*m43 - m12*m24*m43 - m13*m22*m44 - m14*m23*m42; m2.matrix[0][3]= m14*m23*m32 + m12*m24*m33 + m13*m22*m34 - m13*m24*m32 - m14*m22*m33 - m12*m23*m34; m2.matrix[1][0]= m24*m33*m41 + m21*m34*m43 + m23*m31*m44 - m23*m34*m41 - m24*m31*m43 - m21*m33*m44; m2.matrix[1][1]= m13*m34*m41 + m14*m31*m43 + m11*m33*m44 - m14*m33*m41 - m11*m34*m43 - m13*m31*m44; m2.matrix[1][2]= m14*m23*m41 + m11*m24*m43 + m13*m21*m44 - m13*m24*m41 - m14*m21*m43 - m11*m23*m44; m2.matrix[1][3]= m13*m24*m31 + m14*m21*m33 + m11*m23*m34 - m14*m23*m31 - m11*m24*m33 - m13*m21*m34; m2.matrix[2][0]= m22*m34*m41 + m24*m31*m42 + m21*m32*m44 - m24*m32*m41 - m21*m34*m42 - m22*m31*m44; m2.matrix[2][1]= m14*m32*m41 + m11*m34*m42 + m12*m31*m44 - m11*m32*m44 - m12*m34*m41 - m14*m31*m42; m2.matrix[2][2]= m13*m24*m41 + m14*m21*m42 + m11*m22*m44 - m14*m22*m41 - m11*m24*m42 - m12*m21*m44; m2.matrix[2][3]= m14*m22*m31 + m11*m24*m32 + m12*m21*m34 - m11*m22*m34 - m12*m24*m31 - m14*m21*m32; m2.matrix[3][0]= m23*m32*m41 + m21*m33*m42 + m22*m31*m43 - m22*m33*m41 - m23*m31*m42 - m21*m32*m43; m2.matrix[3][1]= m12*m33*m41 + m13*m31*m42 + m11*m32*m43 - m13*m32*m41 - m11*m33*m42 - m12*m31*m43; m2.matrix[3][2]= m13*m22*m41 + m11*m23*m42 + m12*m21*m43 - m11*m22*m43 - m12*m23*m41 - m13*m21*m42; m2.matrix[3][3]= m12*m23*m31 + m13*m21*m32 + m11*m22*m33 - m13*m22*m31 - m11*m23*m32 - m12*m21*m33; return m2.multiplyScalar( 1/this.calculateDeterminant() ); }, /** * Multiply this matrix by a scalar. * @param scalar {number} scalar value * * @return this */ multiplyScalar : function( scalar ) { var i,j; for( i=0; i<4; i++ ) { for( j=0; j<4; j++ ) { this.matrix[i][j]*=scalar; } } return this; } }; })(); (function() { /** * 2D affinetransform matrix represeantation. * It includes matrices for * * */ CAAT.Matrix = function() { this.matrix= [ 1.0,0.0,0.0, 0.0,1.0,0.0, 0.0,0.0,1.0 ]; if ( Float32Array ) { this.matrix= new Float32Array(this.matrix); } return this; }; CAAT.Matrix.prototype= { matrix: null, /** * Transform a point by this matrix. The parameter point will be modified with the transformation values. * @param point {CAAT.Point}. * @return {CAAT.Point} the parameter point. */ transformCoord : function(point) { var x= point.x; var y= point.y; var tm= this.matrix; point.x= x*tm[0] + y*tm[1] + tm[2]; point.y= x*tm[3] + y*tm[4] + tm[5]; return point; }, /** * Create a new rotation matrix and set it up for the specified angle in radians. * @param angle {number} * @return {CAAT.Matrix} a matrix object. * * @static */ rotate : function(angle) { var m= new CAAT.Matrix(); m.setRotation(angle); return m; }, setRotation : function( angle ) { this.identity(); var tm= this.matrix; var c= Math.cos( angle ); var s= Math.sin( angle ); tm[0]= c; tm[1]= -s; tm[3]= s; tm[4]= c; return this; }, /** * Create a scale matrix. * @param scalex {number} x scale magnitude. * @param scaley {number} y scale magnitude. * * @return {CAAT.Matrix} a matrix object. * * @static */ scale : function(scalex, scaley) { var m= new CAAT.Matrix(); m.matrix[0]= scalex; m.matrix[4]= scaley; return m; }, setScale : function(scalex, scaley) { this.identity(); this.matrix[0]= scalex; this.matrix[4]= scaley; return this; }, /** * Create a translation matrix. * @param x {number} x translation magnitude. * @param y {number} y translation magnitude. * * @return {CAAT.Matrix} a matrix object. * @static * */ translate : function( x, y ) { var m= new CAAT.Matrix(); m.matrix[2]= x; m.matrix[5]= y; return m; }, /** * Sets this matrix as a translation matrix. * @param x * @param y */ setTranslate : function( x, y ) { this.identity(); this.matrix[2]= x; this.matrix[5]= y; return this; }, /** * Copy into this matrix the given matrix values. * @param matrix {CAAT.Matrix} * @return this */ copy : function( matrix ) { matrix= matrix.matrix; var tmatrix= this.matrix; tmatrix[0]= matrix[0]; tmatrix[1]= matrix[1]; tmatrix[2]= matrix[2]; tmatrix[3]= matrix[3]; tmatrix[4]= matrix[4]; tmatrix[5]= matrix[5]; tmatrix[6]= matrix[6]; tmatrix[7]= matrix[7]; tmatrix[8]= matrix[8]; return this; }, /** * Set this matrix to the identity matrix. * @return this */ identity : function() { var m= this.matrix; m[0]= 1.0; m[1]= 0.0; m[2]= 0.0; m[3]= 0.0; m[4]= 1.0; m[5]= 0.0; m[6]= 0.0; m[7]= 0.0; m[8]= 1.0; return this; }, /** * Multiply this matrix by a given matrix. * @param m {CAAT.Matrix} * @return this */ multiply : function( m ) { var tm= this.matrix; var mm= m.matrix; var tm0= tm[0]; var tm1= tm[1]; var tm2= tm[2]; var tm3= tm[3]; var tm4= tm[4]; var tm5= tm[5]; var tm6= tm[6]; var tm7= tm[7]; var tm8= tm[8]; var mm0= mm[0]; var mm1= mm[1]; var mm2= mm[2]; var mm3= mm[3]; var mm4= mm[4]; var mm5= mm[5]; var mm6= mm[6]; var mm7= mm[7]; var mm8= mm[8]; tm[0]= tm0*mm0 + tm1*mm3 + tm2*mm6; tm[1]= tm0*mm1 + tm1*mm4 + tm2*mm7; tm[2]= tm0*mm2 + tm1*mm5 + tm2*mm8; tm[3]= tm3*mm0 + tm4*mm3 + tm5*mm6; tm[4]= tm3*mm1 + tm4*mm4 + tm5*mm7; tm[5]= tm3*mm2 + tm4*mm5 + tm5*mm8; tm[6]= tm6*mm0 + tm7*mm3 + tm8*mm6; tm[7]= tm6*mm1 + tm7*mm4 + tm8*mm7; tm[8]= tm6*mm2 + tm7*mm5 + tm8*mm8; return this; }, /** * Premultiply this matrix by a given matrix. * @param m {CAAT.Matrix} * @return this */ premultiply : function(m) { var m00= m.matrix[0]*this.matrix[0] + m.matrix[1]*this.matrix[3] + m.matrix[2]*this.matrix[6]; var m01= m.matrix[0]*this.matrix[1] + m.matrix[1]*this.matrix[4] + m.matrix[2]*this.matrix[7]; var m02= m.matrix[0]*this.matrix[2] + m.matrix[1]*this.matrix[5] + m.matrix[2]*this.matrix[8]; var m10= m.matrix[3]*this.matrix[0] + m.matrix[4]*this.matrix[3] + m.matrix[5]*this.matrix[6]; var m11= m.matrix[3]*this.matrix[1] + m.matrix[4]*this.matrix[4] + m.matrix[5]*this.matrix[7]; var m12= m.matrix[3]*this.matrix[2] + m.matrix[4]*this.matrix[5] + m.matrix[5]*this.matrix[8]; var m20= m.matrix[6]*this.matrix[0] + m.matrix[7]*this.matrix[3] + m.matrix[8]*this.matrix[6]; var m21= m.matrix[6]*this.matrix[1] + m.matrix[7]*this.matrix[4] + m.matrix[8]*this.matrix[7]; var m22= m.matrix[6]*this.matrix[2] + m.matrix[7]*this.matrix[5] + m.matrix[8]*this.matrix[8]; this.matrix[0]= m00; this.matrix[1]= m01; this.matrix[2]= m02; this.matrix[3]= m10; this.matrix[4]= m11; this.matrix[5]= m12; this.matrix[6]= m20; this.matrix[7]= m21; this.matrix[8]= m22; return this; }, /** * Creates a new inverse matrix from this matrix. * @return {CAAT.Matrix} an inverse matrix. */ getInverse : function() { var tm= this.matrix; var m00= tm[0]; var m01= tm[1]; var m02= tm[2]; var m10= tm[3]; var m11= tm[4]; var m12= tm[5]; var m20= tm[6]; var m21= tm[7]; var m22= tm[8]; var newMatrix= new CAAT.Matrix(); var determinant= m00* (m11*m22 - m21*m12) - m10*(m01*m22 - m21*m02) + m20 * (m01*m12 - m11*m02); if ( determinant===0 ) { return null; } var m= newMatrix.matrix; m[0]= m11*m22-m12*m21; m[1]= m02*m21-m01*m22; m[2]= m01*m12-m02*m11; m[3]= m12*m20-m10*m22; m[4]= m00*m22-m02*m20; m[5]= m02*m10-m00*m12; m[6]= m10*m21-m11*m20; m[7]= m01*m20-m00*m21; m[8]= m00*m11-m01*m10; newMatrix.multiplyScalar( 1/determinant ); return newMatrix; }, /** * Multiply this matrix by a scalar. * @param scalar {number} scalar value * * @return this */ multiplyScalar : function( scalar ) { var i; for( i=0; i<9; i++ ) { this.matrix[i]*=scalar; } return this; }, transformRenderingContextSet : null, transformRenderingContext : null, /** * * @param ctx */ transformRenderingContextSet_NoClamp : function(ctx) { var m= this.matrix; ctx.setTransform( m[0], m[3], m[1], m[4], m[2], m[5] ); return this; }, /** * * @param ctx */ transformRenderingContext_NoClamp : function(ctx) { var m= this.matrix; ctx.transform( m[0], m[3], m[1], m[4], m[2], m[5] ); return this; }, /** * * @param ctx */ transformRenderingContextSet_Clamp : function(ctx) { var m= this.matrix; ctx.setTransform( m[0], m[3], m[1], m[4], m[2]>>0, m[5]>>0 ); return this; }, /** * * @param ctx */ transformRenderingContext_Clamp : function(ctx) { var m= this.matrix; ctx.transform( m[0], m[3], m[1], m[4], m[2]>>0, m[5]>>0 ); return this; } }; CAAT.Matrix.prototype.transformRenderingContext= CAAT.Matrix.prototype.transformRenderingContext_Clamp; CAAT.Matrix.prototype.transformRenderingContextSet= CAAT.Matrix.prototype.transformRenderingContextSet_Clamp; })(); (function() { /** * Implementation of a matrix stack. Each CAAT.Actor instance contains a MatrixStack to hold of its affine * transformations. The Canvas rendering context will be fed with this matrix stack values to keep a homogeneous * transformation process. * * @constructor */ CAAT.MatrixStack= function() { this.stack= []; this.saved= []; return this; }; CAAT.MatrixStack.prototype= { stack: null, saved: null, /** * Add a matrix to the transformation stack. * @return this */ pushMatrix : function(matrix) { this.stack.push(matrix); return this; }, /** * Remove the last matrix from this stack. * @return {CAAT.Matrix} the poped matrix. */ popMatrix : function() { return this.stack.pop(); }, /** * Create a restoration point of pushed matrices. * @return this */ save : function() { this.saved.push(this.stack.length); return this; }, /** * Restore from the last restoration point set. * @return this */ restore : function() { var pos= this.saved.pop(); while( this.stack.length!==pos ) { this.popMatrix(); } return this; }, /** * Return the concatenation (multiplication) matrix of all the matrices contained in this stack. * @return {CAAT.Matrix} a new matrix. */ getMatrix : function() { var matrix= new CAAT.Matrix(); for( var i=0; i * H runs from 0 to 360 degrees
* S and V run from 0 to 100 *

* Ported from the excellent java algorithm by Eugene Vishnevsky at: * http://www.cs.rit.edu/~ncs/color/t_convert.html * * @static */ hsvToRgb: function(h, s, v) { var r, g, b; var i; var f, p, q, t; // Make sure our arguments stay in-range h = Math.max(0, Math.min(360, h)); s = Math.max(0, Math.min(100, s)); v = Math.max(0, Math.min(100, v)); // We accept saturation and value arguments from 0 to 100 because that's // how Photoshop represents those values. Internally, however, the // saturation and value are calculated from a range of 0 to 1. We make // That conversion here. s /= 100; v /= 100; if(s === 0) { // Achromatic (grey) r = g = b = v; return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; } h /= 60; // sector 0 to 5 i = Math.floor(h); f = h - i; // factorial part of h p = v * (1 - s); q = v * (1 - s * f); t = v * (1 - s * (1 - f)); switch(i) { case 0: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; default: // case 5: r = v; g = p; b = q; } return new CAAT.Color.RGB(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)); }, /** * Enumeration to define types of color ramps. * @enum {number} */ RampEnumeration : { RAMP_RGBA: 0, RAMP_RGB: 1, RAMP_CHANNEL_RGB: 2, RAMP_CHANNEL_RGBA: 3, RAMP_CHANNEL_RGB_ARRAY: 4, RAMP_CHANNEL_RGBA_ARRAY:5 }, /** * Interpolate the color between two given colors. The return value will be a calculated color * among the two given initial colors which corresponds to the 'step'th color of the 'nsteps' * calculated colors. * @param r0 {number} initial color red component. * @param g0 {number} initial color green component. * @param b0 {number} initial color blue component. * @param r1 {number} final color red component. * @param g1 {number} final color green component. * @param b1 {number} final color blue component. * @param nsteps {number} number of colors to calculate including the two given colors. If 16 is passed as value, * 14 colors plus the two initial ones will be calculated. * @param step {number} return this color index of all the calculated colors. * * @return { r{number}, g{number}, b{number} } return an object with the new calculated color components. * @static */ interpolate : function( r0, g0, b0, r1, g1, b1, nsteps, step) { if ( step<=0 ) { return { r:r0, g:g0, b:b0 }; } else if ( step>=nsteps ) { return { r:r1, g:g1, b:b1 }; } var r= (r0+ (r1-r0)/nsteps*step)>>0; var g= (g0+ (g1-g0)/nsteps*step)>>0; var b= (b0+ (b1-b0)/nsteps*step)>>0; if ( r>255 ) {r=255;} else if (r<0) {r=0;} if ( g>255 ) {g=255;} else if (g<0) {g=0;} if ( b>255 ) {b=255;} else if (b<0) {b=0;} return { r:r, g:g, b:b }; }, /** * Generate a ramp of colors from an array of given colors. * @param fromColorsArray {[number]} an array of colors. each color is defined by an integer number from which * color components will be extracted. Be aware of the alpha component since it will also be interpolated for * new colors. * @param rampSize {number} number of colors to produce. * @param returnType {CAAT.ColorUtils.RampEnumeration} a value of CAAT.ColorUtils.RampEnumeration enumeration. * * @return { [{number},{number},{number},{number}] } an array of integers each of which represents a color of * the calculated color ramp. * * @static */ makeRGBColorRamp : function( fromColorsArray, rampSize, returnType ) { var ramp= []; var nc= fromColorsArray.length-1; var chunk= rampSize/nc; for( var i=0; i>24)&0xff; var r0= (c&0xff0000)>>16; var g0= (c&0xff00)>>8; var b0= c&0xff; var c1= fromColorsArray[i+1]; var a1= (c1>>24)&0xff; var r1= (c1&0xff0000)>>16; var g1= (c1&0xff00)>>8; var b1= c1&0xff; var da= (a1-a0)/chunk; var dr= (r1-r0)/chunk; var dg= (g1-g0)/chunk; var db= (b1-b0)/chunk; for( var j=0; j>0; var nr= (r0+dr*j)>>0; var ng= (g0+dg*j)>>0; var nb= (b0+db*j)>>0; switch( returnType ) { case this.RampEnumeration.RAMP_RGBA: ramp.push( 'argb('+na+','+nr+','+ng+','+nb+')' ); break; case this.RampEnumeration.RAMP_RGB: ramp.push( 'rgb('+nr+','+ng+','+nb+')' ); break; case this.RampEnumeration.RAMP_CHANNEL_RGB: ramp.push( 0xff000000 | nr<<16 | ng<<8 | nb ); break; case this.RampEnumeration.RAMP_CHANNEL_RGBA: ramp.push( na<<24 | nr<<16 | ng<<8 | nb ); break; case this.RampEnumeration.RAMP_CHANNEL_RGBA_ARRAY: ramp.push([ nr, ng, nb, na ]); break; case this.RampEnumeration.RAMP_CHANNEL_RGB_ARRAY: ramp.push([ nr, ng, nb ]); break; } } } return ramp; } }; })(); (function() { /** * RGB color implementation * @param r {number} an integer in the range 0..255 * @param g {number} an integer in the range 0..255 * @param b {number} an integer in the range 0..255 * * @constructor */ CAAT.Color.RGB = function(r, g, b) { this.r = r || 255; this.g = g || 255; this.b = b || 255; return this; }; CAAT.Color.RGB.prototype= { r: 255, g: 255, b: 255, /** * Get color hexadecimal representation. * @return {string} a string with color hexadecimal representation. */ toHex: function() { // See: http://jsperf.com/rgb-decimal-to-hex/5 return ('000000' + ((this.r << 16) + (this.g << 8) + this.b).toString(16)).slice(-6); } }; })(); /** * See LICENSE file. * * Rectangle Class. * Needed to compute Curve bounding box. * Needed to compute Actor affected area on change. * **/ (function() { /** * A Rectangle implementation, which defines an area positioned somewhere. * * @constructor */ CAAT.Rectangle= function() { return this; }; CAAT.Rectangle.prototype= { x: 0, y: 0, x1: 0, y1: 0, width: -1, height: -1, setEmpty : function() { this.width= -1; this.height= -1; this.x= 0; this.y= 0; this.x1= 0; this.y1= 0; return this; }, /** * Set this rectangle's location. * @param x {number} * @param y {number} */ setLocation: function( x,y ) { this.x= x; this.y= y; this.x1= this.x+this.width; this.y1= this.y+this.height; return this; }, /** * Set this rectangle's dimension. * @param w {number} * @param h {number} */ setDimension : function( w,h ) { this.width= w; this.height= h; this.x1= this.x+this.width; this.y1= this.y+this.height; return this; }, setBounds : function( x,y,w,h ) { this.setLocation( x, y ) this.setDimension( w, h ); return this; }, /** * Return whether the coordinate is inside this rectangle. * @param px {number} * @param py {number} * * @return {boolean} */ contains : function(px,py) { return px>=0 && px=0 && pythis.y1 ) { this.y1= py; } if ( px>this.x1 ){ this.x1= px; } this.width= this.x1-this.x; this.height= this.y1-this.y; }, unionRectangle : function( rectangle ) { this.union( rectangle.x , rectangle.y ); this.union( rectangle.x1, rectangle.y ); this.union( rectangle.x, rectangle.y1 ); this.union( rectangle.x1, rectangle.y1 ); return this; }, intersects : function( r ) { if ( r.isEmpty() || this.isEmpty() ) { return false; } if ( r.x1<= this.x ) { return false; } if ( r.x >= this.x1 ) { return false; } if ( r.y1<= this.y ) { return false; } if ( r.y>= this.y1 ) { return false; } return true; }, intersectsRect : function( x,y,w,h ) { if ( -1===w || -1===h ) { return false; } var x1= x+w-1; var y1= y+h-1; if ( x1< this.x ) { return false; } if ( x > this.x1 ) { return false; } if ( y1< this.y ) { return false; } if ( y> this.y1 ) { return false; } return true; }, intersect : function( i, r ) { if ( typeof r==='undefined' ) { r= new CAAT.Rectangle(); } r.x= Math.max( this.x, i.x ); r.y= Math.max( this.y, i.y ); r.x1=Math.min( this.x1, i.x1 ); r.y1=Math.min( this.y1, i.y1 ); r.width= r.x1-r.x; r.height=r.y1-r.y; return r; } }; })();/** * See LICENSE file. * * Classes to solve and draw curves. * Curve is the superclass of * + Bezier (quadric and cubic) * + CatmullRom * * **/ (function() { /** * * Curve class is the base for all curve solvers available in CAAT. * * @constructor */ CAAT.Curve= function() { return this; }; CAAT.Curve.prototype= { coordlist: null, k: 0.05, length: -1, interpolator: false, HANDLE_SIZE: 20, drawHandles: true, /** * Paint the curve control points. * @param director {CAAT.Director} */ paint: function(director) { if ( false===this.drawHandles ) { return; } var cl= this.coordlist; var ctx= director.ctx; // control points ctx.save(); ctx.beginPath(); ctx.strokeStyle='#a0a0a0'; ctx.moveTo( cl[0].x, cl[0].y ); ctx.lineTo( cl[1].x, cl[1].y ); ctx.stroke(); if ( this.cubic ) { ctx.moveTo( cl[2].x, cl[2].y ); ctx.lineTo( cl[3].x, cl[3].y ); ctx.stroke(); } ctx.globalAlpha=0.5; for( var i=0; i=0 && index */ applyAsPath : function( director ) { } }; })(); (function() { /** * Bezier quadric and cubic curves implementation. * * @constructor * @extends CAAT.Curve */ CAAT.Bezier= function() { CAAT.Bezier.superclass.constructor.call(this); return this; }; CAAT.Bezier.prototype= { cubic: false, applyAsPath : function( director ) { var cc= this.coordlist; if ( this.cubic ) { director.ctx.bezierCurveTo( cc[1].x, cc[1].y, cc[2].x, cc[2].y, cc[3].x, cc[3].y ); } else { director.ctx.quadraticCurveTo( cc[1].x, cc[1].y, cc[2].x, cc[2].y ); } return this; }, isQuadric : function() { return !this.cubic; }, isCubic : function() { return this.cubic; }, /** * Set this curve as a cubic bezier defined by the given four control points. * @param cp0x {number} * @param cp0y {number} * @param cp1x {number} * @param cp1y {number} * @param cp2x {number} * @param cp2y {number} * @param cp3x {number} * @param cp3y {number} */ setCubic : function( cp0x,cp0y, cp1x,cp1y, cp2x,cp2y, cp3x,cp3y ) { this.coordlist= []; this.coordlist.push( new CAAT.Point().set(cp0x, cp0y ) ); this.coordlist.push( new CAAT.Point().set(cp1x, cp1y ) ); this.coordlist.push( new CAAT.Point().set(cp2x, cp2y ) ); this.coordlist.push( new CAAT.Point().set(cp3x, cp3y ) ); this.cubic= true; this.update(); return this; }, /** * Set this curve as a quadric bezier defined by the three control points. * @param cp0x {number} * @param cp0y {number} * @param cp1x {number} * @param cp1y {number} * @param cp2x {number} * @param cp2y {number} */ setQuadric : function(cp0x,cp0y, cp1x,cp1y, cp2x,cp2y ) { this.coordlist= []; this.coordlist.push( new CAAT.Point().set(cp0x, cp0y ) ); this.coordlist.push( new CAAT.Point().set(cp1x, cp1y ) ); this.coordlist.push( new CAAT.Point().set(cp2x, cp2y ) ); this.cubic= false; this.update(); return this; }, setPoints : function( points ) { if ( points.length===3 ) { this.coordlist= points; this.cubic= false; this.update(); } else if (points.length===4 ) { this.coordlist= points; this.cubic= true; this.update(); } else { throw 'points must be an array of 3 or 4 CAAT.Point instances.' } return this; }, /** * Paint this curve. * @param director {CAAT.Director} */ paint : function( director ) { if ( this.cubic ) { this.paintCubic(director); } else { this.paintCuadric( director ); } CAAT.Bezier.superclass.paint.call(this,director); }, /** * Paint this quadric Bezier curve. Each time the curve is drawn it will be solved again from 0 to 1 with * CAAT.Bezier.k increments. * * @param director {CAAT.Director} * @private */ paintCuadric : function( director ) { var x1,y1; x1 = this.coordlist[0].x; y1 = this.coordlist[0].y; var ctx= director.ctx; ctx.save(); ctx.beginPath(); ctx.moveTo(x1,y1); var point= new CAAT.Point(); for(var t=this.k;t<=1+this.k;t+=this.k){ this.solve(point,t); ctx.lineTo(point.x, point.y ); } ctx.stroke(); ctx.restore(); }, /** * Paint this cubic Bezier curve. Each time the curve is drawn it will be solved again from 0 to 1 with * CAAT.Bezier.k increments. * * @param director {CAAT.Director} * @private */ paintCubic : function( director ) { var x1,y1; x1 = this.coordlist[0].x; y1 = this.coordlist[0].y; var ctx= director.ctx; ctx.save(); ctx.beginPath(); ctx.moveTo(x1,y1); var point= new CAAT.Point(); for(var t=this.k;t<=1+this.k;t+=this.k){ this.solve(point,t); ctx.lineTo(point.x, point.y ); } ctx.stroke(); ctx.restore(); }, /** * Solves the curve for any given parameter t. * @param point {CAAT.Point} the point to store the solved value on the curve. * @param t {number} a number in the range 0..1 */ solve : function(point,t) { if ( this.cubic ) { return this.solveCubic(point,t); } else { return this.solveQuadric(point,t); } }, /** * Solves a cubic Bezier. * @param point {CAAT.Point} the point to store the solved value on the curve. * @param t {number} the value to solve the curve for. */ solveCubic : function(point,t) { var t2= t*t; var t3= t*t2; var cl= this.coordlist; var cl0= cl[0]; var cl1= cl[1]; var cl2= cl[2]; var cl3= cl[3]; point.x=( cl0.x + t * (-cl0.x * 3 + t * (3 * cl0.x- cl0.x*t)))+t*(3*cl1.x+t*(-6*cl1.x+ cl1.x*3*t))+t2*(cl2.x*3-cl2.x*3*t)+ cl3.x * t3; point.y=( cl0.y+t*(-cl0.y*3+t*(3*cl0.y- cl0.y*t)))+t*(3*cl1.y+t*(-6*cl1.y+ cl1.y*3*t))+t2*(cl2.y*3-cl2.y*3*t)+ cl3.y * t3; return point; }, /** * Solves a quadric Bezier. * @param point {CAAT.Point} the point to store the solved value on the curve. * @param t {number} the value to solve the curve for. */ solveQuadric : function(point,t) { var cl= this.coordlist; var cl0= cl[0]; var cl1= cl[1]; var cl2= cl[2]; var t1= 1-t; point.x= t1*t1*cl0.x + 2*t1*t*cl1.x + t*t*cl2.x; point.y= t1*t1*cl0.y + 2*t1*t*cl1.y + t*t*cl2.y; return point; } }; extend(CAAT.Bezier, CAAT.Curve, null); })(); (function() { /** * CatmullRom curves solver implementation. *

* This object manages one single catmull rom segment, that is 4 points. * A complete spline should be managed with CAAT.Path.setCatmullRom with a complete list of points. * * @constructor * @extends CAAT.Curve */ CAAT.CatmullRom = function() { CAAT.CatmullRom.superclass.constructor.call(this); return this; }; CAAT.CatmullRom.prototype= { /** * Set curve control points. * @param p0 * @param p1 * @param p2 * @param p3 */ setCurve : function( p0, p1, p2, p3 ) { this.coordlist= []; this.coordlist.push( p0 ); this.coordlist.push( p1 ); this.coordlist.push( p2 ); this.coordlist.push( p3 ); this.update(); return this; }, /** * Paint the contour by solving again the entire curve. * @param director {CAAT.Director} */ paint: function(director) { var x1,y1; // Catmull rom solves from point 1 !!! x1 = this.coordlist[1].x; y1 = this.coordlist[1].y; var ctx= director.ctx; ctx.save(); ctx.beginPath(); ctx.moveTo(x1,y1); var point= new CAAT.Point(); for(var t=this.k;t<=1+this.k;t+=this.k){ this.solve(point,t); ctx.lineTo(point.x,point.y); } ctx.stroke(); ctx.restore(); CAAT.CatmullRom.superclass.paint.call(this,director); }, /** * Solves the curve for any given parameter t. * @param point {CAAT.Point} the point to store the solved value on the curve. * @param t {number} a number in the range 0..1 */ solve: function(point,t) { var c= this.coordlist; // Handy from CAKE. Thanks. var af = ((-t+2)*t-1)*t*0.5 var bf = (((3*t-5)*t)*t+2)*0.5 var cf = ((-3*t+4)*t+1)*t*0.5 var df = ((t-1)*t*t)*0.5 point.x= c[0].x * af + c[1].x * bf + c[2].x * cf + c[3].x * df; point.y= c[0].y * af + c[1].y * bf + c[2].y * cf + c[3].y * df; return point; }, applyAsPath : function( director ) { var ctx= director.ctx; var point= new CAAT.Point(); for(var t=this.k;t<=1+this.k;t+=this.k){ this.solve(point,t); ctx.lineTo(point.x,point.y); } return this; }, /** * Return the first curve control point. * @return {CAAT.Point} */ endCurvePosition : function() { return this.coordlist[ this.coordlist.length-2 ]; }, /** * Return the last curve control point. * @return {CAAT.Point} */ startCurvePosition : function() { return this.coordlist[ 1 ]; } }; extend(CAAT.CatmullRom, CAAT.Curve, null); })();/** * See LICENSE file. * * Hold a 2D point information. * Think about the possibility of turning CAAT.Point into {x:,y:}. * **/ (function() { /** * * A point defined by two coordinates. * * @param xpos {number} * @param ypos {number} * * @constructor */ CAAT.Point= function(xpos, ypos, zpos) { this.x= xpos; this.y= ypos; this.z= zpos||0; return this; }; CAAT.Point.prototype= { x: 0, y: 0, z: 0, /** * Sets this point coordinates. * @param x {number} * @param y {number} * * @return this */ set : function(x,y,z) { this.x= x; this.y= y; this.z= z||0; return this; }, /** * Create a new CAAT.Point equal to this one. * @return {CAAT.Point} */ clone : function() { var p= new CAAT.Point(this.x, this.y, this.z ); return p; }, /** * Translate this point to another position. The final point will be (point.x+x, point.y+y) * @param x {number} * @param y {number} * * @return this */ translate : function(x,y,z) { this.x+= x; this.y+= y; this.z+= z; return this; }, /** * Translate this point to another point. * @param aPoint {CAAT.Point} * @return this */ translatePoint: function(aPoint) { this.x += aPoint.x; this.y += aPoint.y; this.z += aPoint.z; return this; }, /** * Substract a point from this one. * @param aPoint {CAAT.Point} * @return this */ subtract: function(aPoint) { this.x -= aPoint.x; this.y -= aPoint.y; this.z -= aPoint.z; return this; }, /** * Multiply this point by a scalar. * @param factor {number} * @return this */ multiply: function(factor) { this.x *= factor; this.y *= factor; this.z *= factor; return this; }, /** * Rotate this point by an angle. The rotation is held by (0,0) coordinate as center. * @param angle {number} * @return this */ rotate: function(angle) { var x = this.x, y = this.y; this.x = x * Math.cos(angle) - Math.sin(angle) * y; this.y = x * Math.sin(angle) + Math.cos(angle) * y; this.z = 0; return this; }, /** * * @param angle {number} * @return this */ setAngle: function(angle) { var len = this.getLength(); this.x = Math.cos(angle) * len; this.y = Math.sin(angle) * len; this.z = 0; return this; }, /** * * @param length {number} * @return this */ setLength: function(length) { var len = this.getLength(); if (len)this.multiply(length / len); else this.x = this.y = this.z = length; return this; }, /** * Normalize this point, that is, both set coordinates proportionally to values raning 0..1 * @return this */ normalize: function() { var len = this.getLength(); this.x /= len; this.y /= len; this.z /= len; return this; }, /** * Return the angle from -Pi to Pi of this point. * @return {number} */ getAngle: function() { return Math.atan2(this.y, this.x); }, /** * Set this point coordinates proportinally to a maximum value. * @param max {number} * @return this */ limit: function(max) { var aLenthSquared = this.getLengthSquared(); if(aLenthSquared+0.01 > max*max) { var aLength = Math.sqrt(aLenthSquared); this.x= (this.x/aLength) * max; this.y= (this.y/aLength) * max; this.z= (this.z/aLength) * max; } return this; }, /** * Get this point's lenght. * @return {number} */ getLength: function() { var length = Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z); if ( length < 0.005 && length > -0.005) return 0.000001; return length; }, /** * Get this point's squared length. * @return {number} */ getLengthSquared: function() { var lengthSquared = this.x*this.x + this.y*this.y + this.z*this.z; if ( lengthSquared < 0.005 && lengthSquared > -0.005) return 0; return lengthSquared; }, /** * Get the distance between two points. * @param point {CAAT.Point} * @return {number} */ getDistance: function(point) { var deltaX = this.x - point.x; var deltaY = this.y - point.y; var deltaZ = this.z - point.z; return Math.sqrt( deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ ); }, /** * Get the squared distance between two points. * @param point {CAAT.Point} * @return {number} */ getDistanceSquared: function(point) { var deltaX = this.x - point.x; var deltaY = this.y - point.y; var deltaZ = this.z - point.z; return deltaX*deltaX + deltaY*deltaY + deltaZ*deltaZ; }, /** * Get a string representation. * @return {string} */ toString: function() { return "(CAAT.Point)" + " x:" + String(Math.round(Math.floor(this.x*10))/10) + " y:" + String(Math.round(Math.floor(this.y*10))/10) + " z:" + String(Math.round(Math.floor(this.z*10))/10); } }; })();/** * See LICENSE file. * * This file contains the definition for objects QuadTree and HashMap. * Quadtree offers an exact list of collisioning areas, while HashMap offers a list of potentially colliding elements. * **/ (function() { CAAT.QuadTree= function() { return this; }; var QT_MAX_ELEMENTS= 1; var QT_MIN_WIDTH= 32; CAAT.QuadTree.prototype= { bgActors : null, quadData : null, create : function( l,t, r,b, backgroundElements, minWidth, maxElements ) { if ( typeof minWidth==='undefined' ) { minWidth= QT_MIN_WIDTH; } if ( typeof maxElements==='undefined' ) { maxElements= QT_MAX_ELEMENTS; } var cx= (l+r)/2; var cy= (t+b)/2; this.x= l; this.y= t; this.x1= r; this.y1= b; this.width= r-l; this.height= b-t; this.bgActors= this.__getOverlappingActorList( backgroundElements ); if ( this.bgActors.length <= maxElements || this.width <= minWidth ) { return this; } this.quadData= new Array(4); this.quadData[0]= new CAAT.QuadTree().create( l,t,cx,cy, this.bgActors ); // TL this.quadData[1]= new CAAT.QuadTree().create( cx,t,r,cy, this.bgActors ); // TR this.quadData[2]= new CAAT.QuadTree().create( l,cy,cx,b, this.bgActors ); // BL this.quadData[3]= new CAAT.QuadTree().create( cx,cy,r,b, this.bgActors ); return this; }, __getOverlappingActorList : function( actorList ) { var tmpList= []; for( var i=0, l=actorList.length; i>0 ); } this.ycache= []; for( i=0; i>0 ); } this.xycache=[]; for( i=0; i1 ) { // at least 2 elements could collide this._solveCollisionCell( cell, callback ); } } }, _solveCollisionCell : function( cell, callback ) { var i,j; for( i=0; i *

  • The input values must be between 0 and 1. *
  • Output values will be between 0 and 1. *
  • Every Interpolator has at least an entering boolean parameter called pingpong. if set to true, the Interpolator * will set values from 0..1 and back from 1..0. So half the time for each range. * * *

    * CAAt.Interpolator is defined by a createXXXX method which sets up an internal getPosition(time) * function. You could set as an Interpolator up any object which exposes a method getPosition(time) * and returns a CAAT.Point or an object of the form {x:{number}, y:{number}}. *

    * In the return value, the x attribute's value will be the same value as that of the time parameter, * and y attribute will hold a value between 0 and 1 with the resulting value of applying the * interpolation function for the time parameter. * *

    * For am exponential interpolation, the getPosition function would look like this: * function getPosition(time) { return { x:time, y: Math.pow(time,2) }�}. * meaning that for time=0.5, a value of 0,5*0,5 should use instead. * *

    * For a visual understanding of interpolators see tutorial 4 interpolators, or play with technical * demo 1 where a SpriteActor moves along a path and the way it does can be modified by every * out-of-the-box interpolator. * * @constructor * */ CAAT.Interpolator = function() { this.interpolated= new CAAT.Point(0,0,0); return this; }; CAAT.Interpolator.prototype= { interpolated: null, // a coordinate holder for not building a new CAAT.Point for each interpolation call. paintScale: 90, // the size of the interpolation draw on screen in pixels. /** * Set a linear interpolation function. * * @param bPingPong {boolean} * @param bInverse {boolean} will values will be from 1 to 0 instead of 0 to 1 ?. */ createLinearInterpolator : function(bPingPong, bInverse) { /** * Linear and inverse linear interpolation function. * @param time {number} */ this.getPosition= function getPosition(time) { var orgTime= time; if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } if ( bInverse!==null && bInverse ) { time= 1-time; } return this.interpolated.set(orgTime,time); }; return this; }, createBackOutInterpolator : function(bPingPong) { this.getPosition= function getPosition(time) { var orgTime= time; if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } time = time - 1; var overshoot= 1.70158; return this.interpolated.set( orgTime, time * time * ((overshoot + 1) * time + overshoot) + 1); }; return this; }, /** * Set an exponential interpolator function. The function to apply will be Math.pow(time,exponent). * This function starts with 0 and ends in values of 1. * * @param exponent {number} exponent of the function. * @param bPingPong {boolean} */ createExponentialInInterpolator : function(exponent, bPingPong) { this.getPosition= function getPosition(time) { var orgTime= time; if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } return this.interpolated.set(orgTime,Math.pow(time,exponent)); }; return this; }, /** * Set an exponential interpolator function. The function to apply will be 1-Math.pow(time,exponent). * This function starts with 1 and ends in values of 0. * * @param exponent {number} exponent of the function. * @param bPingPong {boolean} */ createExponentialOutInterpolator : function(exponent, bPingPong) { this.getPosition= function getPosition(time) { var orgTime= time; if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } return this.interpolated.set(orgTime,1-Math.pow(1-time,exponent)); }; return this; }, /** * Set an exponential interpolator function. Two functions will apply: * Math.pow(time*2,exponent)/2 for the first half of the function (t<0.5) and * 1-Math.abs(Math.pow(time*2-2,exponent))/2 for the second half (t>=.5) * This function starts with 0 and goes to values of 1 and ends with values of 0. * * @param exponent {number} exponent of the function. * @param bPingPong {boolean} */ createExponentialInOutInterpolator : function(exponent, bPingPong) { this.getPosition= function getPosition(time) { var orgTime= time; if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } if ( time*2<1 ) { return this.interpolated.set(orgTime,Math.pow(time*2,exponent)/2); } return this.interpolated.set(orgTime,1-Math.abs(Math.pow(time*2-2,exponent))/2); }; return this; }, /** * Creates a Quadric bezier curbe as interpolator. * * @param p0 {CAAT.Point} a CAAT.Point instance. * @param p1 {CAAT.Point} a CAAT.Point instance. * @param p2 {CAAT.Point} a CAAT.Point instance. * @param bPingPong {boolean} a boolean indicating if the interpolator must ping-pong. */ createQuadricBezierInterpolator : function(p0,p1,p2,bPingPong) { this.getPosition= function getPosition(time) { var orgTime= time; if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } time= (1-time)*(1-time)*p0.y + 2*(1-time)*time*p1.y + time*time*p2.y; return this.interpolated.set( orgTime, time ); }; return this; }, /** * Creates a Cubic bezier curbe as interpolator. * * @param p0 {CAAT.Point} a CAAT.Point instance. * @param p1 {CAAT.Point} a CAAT.Point instance. * @param p2 {CAAT.Point} a CAAT.Point instance. * @param p3 {CAAT.Point} a CAAT.Point instance. * @param bPingPong {boolean} a boolean indicating if the interpolator must ping-pong. */ createCubicBezierInterpolator : function(p0,p1,p2,p3,bPingPong) { this.getPosition= function getPosition(time) { var orgTime= time; if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } var t2= time*time; var t3= time*t2; time = (p0.y + time * (-p0.y * 3 + time * (3 * p0.y - p0.y * time))) + time * (3 * p1.y + time * (-6 * p1.y + p1.y * 3 * time)) + t2 * (p2.y * 3 - p2.y * 3 * time) + p3.y * t3; return this.interpolated.set( orgTime, time ); }; return this; }, createElasticOutInterpolator : function(amplitude,p,bPingPong) { this.getPosition= function getPosition(time) { if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } if (time === 0) { return {x:0,y:0}; } if (time === 1) { return {x:1,y:1}; } var s = p/(2*Math.PI) * Math.asin (1/amplitude); return this.interpolated.set( time, (amplitude*Math.pow(2,-10*time) * Math.sin( (time-s)*(2*Math.PI)/p ) + 1 ) ); }; return this; }, createElasticInInterpolator : function(amplitude,p,bPingPong) { this.getPosition= function getPosition(time) { if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } if (time === 0) { return {x:0,y:0}; } if (time === 1) { return {x:1,y:1}; } var s = p/(2*Math.PI) * Math.asin (1/amplitude); return this.interpolated.set( time, -(amplitude*Math.pow(2,10*(time-=1)) * Math.sin( (time-s)*(2*Math.PI)/p ) ) ); }; return this; }, createElasticInOutInterpolator : function(amplitude,p,bPingPong) { this.getPosition= function getPosition(time) { if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } var s = p/(2*Math.PI) * Math.asin (1/amplitude); time*=2; if ( time<=1 ) { return this.interpolated.set( time, -0.5*(amplitude*Math.pow(2,10*(time-=1)) * Math.sin( (time-s)*(2*Math.PI)/p ))); } return this.interpolated.set( time, 1+0.5*(amplitude*Math.pow(2,-10*(time-=1)) * Math.sin( (time-s)*(2*Math.PI)/p ))); }; return this; }, /** * @param time {number} * @private */ bounce : function(time) { if ((time /= 1) < (1 / 2.75)) { return {x:time, y:7.5625 * time * time}; } else if (time < (2 / 2.75)) { return {x:time, y:7.5625 * (time -= (1.5 / 2.75)) * time + 0.75}; } else if (time < (2.5 / 2.75)) { return {x:time, y:7.5625 * (time -= (2.25 / 2.75)) * time + 0.9375}; } else { return {x:time, y:7.5625*(time-=(2.625/2.75))*time+0.984375}; } }, createBounceOutInterpolator : function(bPingPong) { this.getPosition= function getPosition(time) { if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } return this.bounce(time); }; return this; }, createBounceInInterpolator : function(bPingPong) { this.getPosition= function getPosition(time) { if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } var r= this.bounce(1-time); r.y= 1-r.y; return r; }; return this; }, createBounceInOutInterpolator : function(bPingPong) { this.getPosition= function getPosition(time) { if ( bPingPong ) { if ( time<0.5 ) { time*=2; } else { time= 1-(time-0.5)*2; } } var r; if (time < 0.5) { r= this.bounce(1 - time * 2); r.y= (1 - r.y)* 0.5; return r; } r= this.bounce(time * 2 - 1,bPingPong); r.y= r.y* 0.5 + 0.5; return r; }; return this; }, /** * Paints an interpolator on screen. * @param director {CAAT.Director} a CAAT.Director instance. * @param time {number} an integer indicating the scene time the Interpolator will be drawn at. This value is useless. */ paint : function(director,time) { var canvas= director.crc; canvas.save(); canvas.beginPath(); canvas.moveTo( 0, this.getPosition(0).y * this.paintScale ); for( var i=0; i<=this.paintScale; i++ ) { canvas.lineTo( i, this.getPosition(i/this.paintScale).y * this.paintScale ); } canvas.strokeStyle='black'; canvas.stroke(); canvas.restore(); }, /** * Gets an array of coordinates which define the polyline of the intepolator's curve contour. * Values for both coordinates range from 0 to 1. * @param iSize {number} an integer indicating the number of contour segments. * @return array {[CAAT.Point]} of object of the form {x:float, y:float}. */ getContour : function(iSize) { var contour=[]; for( var i=0; i<=iSize; i++ ) { contour.push( {x: i/iSize, y: this.getPosition(i/iSize).y} ); } return contour; }, /** * */ enumerateInterpolators : function() { return [ new CAAT.Interpolator().createLinearInterpolator(false, false), 'Linear pingpong=false, inverse=false', new CAAT.Interpolator().createLinearInterpolator(true, false), 'Linear pingpong=true, inverse=false', new CAAT.Interpolator().createLinearInterpolator(false, true), 'Linear pingpong=false, inverse=true', new CAAT.Interpolator().createLinearInterpolator(true, true), 'Linear pingpong=true, inverse=true', new CAAT.Interpolator().createExponentialInInterpolator( 2, false), 'ExponentialIn pingpong=false, exponent=2', new CAAT.Interpolator().createExponentialOutInterpolator( 2, false), 'ExponentialOut pingpong=false, exponent=2', new CAAT.Interpolator().createExponentialInOutInterpolator( 2, false), 'ExponentialInOut pingpong=false, exponent=2', new CAAT.Interpolator().createExponentialInInterpolator( 2, true), 'ExponentialIn pingpong=true, exponent=2', new CAAT.Interpolator().createExponentialOutInterpolator( 2, true), 'ExponentialOut pingpong=true, exponent=2', new CAAT.Interpolator().createExponentialInOutInterpolator( 2, true), 'ExponentialInOut pingpong=true, exponent=2', new CAAT.Interpolator().createExponentialInInterpolator( 4, false), 'ExponentialIn pingpong=false, exponent=4', new CAAT.Interpolator().createExponentialOutInterpolator( 4, false), 'ExponentialOut pingpong=false, exponent=4', new CAAT.Interpolator().createExponentialInOutInterpolator( 4, false), 'ExponentialInOut pingpong=false, exponent=4', new CAAT.Interpolator().createExponentialInInterpolator( 4, true), 'ExponentialIn pingpong=true, exponent=4', new CAAT.Interpolator().createExponentialOutInterpolator( 4, true), 'ExponentialOut pingpong=true, exponent=4', new CAAT.Interpolator().createExponentialInOutInterpolator( 4, true), 'ExponentialInOut pingpong=true, exponent=4', new CAAT.Interpolator().createExponentialInInterpolator( 6, false), 'ExponentialIn pingpong=false, exponent=6', new CAAT.Interpolator().createExponentialOutInterpolator( 6, false), 'ExponentialOut pingpong=false, exponent=6', new CAAT.Interpolator().createExponentialInOutInterpolator( 6, false), 'ExponentialInOut pingpong=false, exponent=6', new CAAT.Interpolator().createExponentialInInterpolator( 6, true), 'ExponentialIn pingpong=true, exponent=6', new CAAT.Interpolator().createExponentialOutInterpolator( 6, true), 'ExponentialOut pingpong=true, exponent=6', new CAAT.Interpolator().createExponentialInOutInterpolator( 6, true), 'ExponentialInOut pingpong=true, exponent=6', new CAAT.Interpolator().createBounceInInterpolator(false), 'BounceIn pingpong=false', new CAAT.Interpolator().createBounceOutInterpolator(false), 'BounceOut pingpong=false', new CAAT.Interpolator().createBounceInOutInterpolator(false), 'BounceInOut pingpong=false', new CAAT.Interpolator().createBounceInInterpolator(true), 'BounceIn pingpong=true', new CAAT.Interpolator().createBounceOutInterpolator(true), 'BounceOut pingpong=true', new CAAT.Interpolator().createBounceInOutInterpolator(true), 'BounceInOut pingpong=true', new CAAT.Interpolator().createElasticInInterpolator( 1.1, 0.4, false), 'ElasticIn pingpong=false, amp=1.1, d=.4', new CAAT.Interpolator().createElasticOutInterpolator( 1.1, 0.4, false), 'ElasticOut pingpong=false, amp=1.1, d=.4', new CAAT.Interpolator().createElasticInOutInterpolator( 1.1, 0.4, false), 'ElasticInOut pingpong=false, amp=1.1, d=.4', new CAAT.Interpolator().createElasticInInterpolator( 1.1, 0.4, true), 'ElasticIn pingpong=true, amp=1.1, d=.4', new CAAT.Interpolator().createElasticOutInterpolator( 1.1, 0.4, true), 'ElasticOut pingpong=true, amp=1.1, d=.4', new CAAT.Interpolator().createElasticInOutInterpolator( 1.1, 0.4, true), 'ElasticInOut pingpong=true, amp=1.1, d=.4', new CAAT.Interpolator().createElasticInInterpolator( 1.0, 0.2, false), 'ElasticIn pingpong=false, amp=1.0, d=.2', new CAAT.Interpolator().createElasticOutInterpolator( 1.0, 0.2, false), 'ElasticOut pingpong=false, amp=1.0, d=.2', new CAAT.Interpolator().createElasticInOutInterpolator( 1.0, 0.2, false), 'ElasticInOut pingpong=false, amp=1.0, d=.2', new CAAT.Interpolator().createElasticInInterpolator( 1.0, 0.2, true), 'ElasticIn pingpong=true, amp=1.0, d=.2', new CAAT.Interpolator().createElasticOutInterpolator( 1.0, 0.2, true), 'ElasticOut pingpong=true, amp=1.0, d=.2', new CAAT.Interpolator().createElasticInOutInterpolator( 1.0, 0.2, true), 'ElasticInOut pingpong=true, amp=1.0, d=.2' ]; } }; })(); /** * See LICENSE file. * * Behaviors are keyframing elements. * By using a BehaviorContainer, you can specify different actions on any animation Actor. * An undefined number of Behaviors can be defined for each Actor. * * There're the following Behaviors: * + AlphaBehavior: controls container/actor global alpha. * + RotateBehavior: takes control of rotation affine transform. * + ScaleBehavior: takes control of scaling on x/y axis affine transform. * + PathBehavior: takes control of translating an Actor/ActorContainer across a path [ie. pathSegment collection]. * + GenericBehavior: applies a behavior to any given target object's property, or notifies a callback. * * **/ (function() { /** * Behavior base class. * *

    * A behavior is defined by a frame time (behavior duration) and a behavior application function called interpolator. * In its default form, a behaviour is applied linearly, that is, the same amount of behavior is applied every same * time interval. *

    * A concrete Behavior, a rotateBehavior in example, will change a concrete Actor's rotationAngle during the specified * period. *

    * A behavior is guaranteed to notify (if any observer is registered) on behavior expiration. *

    * A behavior can keep an unlimited observers. Observers are objects of the form: *

    * * { * behaviorExpired : function( behavior, time, actor); * behaviorApplied : function( behavior, time, normalizedTime, actor, value); * } * *

    * behaviorExpired: function( behavior, time, actor). This method will be called for any registered observer when * the scene time is greater than behavior's startTime+duration. This method will be called regardless of the time * granurality. *

    * behaviorApplied : function( behavior, time, normalizedTime, actor, value). This method will be called once per * frame while the behavior is not expired and is in frame time (behavior startTime>=scene time). This method can be * called multiple times. *

    * Every behavior is applied to a concrete Actor. * Every actor must at least define an start and end value. The behavior will set start-value at behaviorStartTime and * is guaranteed to apply end-value when scene time= behaviorStartTime+behaviorDuration. *

    * You can set behaviors to apply forever that is cyclically. When a behavior is cycle=true, won't notify * behaviorExpired to its registered observers. *

    * Other Behaviors simply must supply with the method setForTime(time, actor) overriden. * * @constructor */ CAAT.Behavior= function() { this.lifecycleListenerList=[]; this.setDefaultInterpolator(); return this; }; /** * @enum */ CAAT.Behavior.Status= { NOT_STARTED: 0, STARTED: 1, EXPIRED: 2 }; var DefaultInterpolator= new CAAT.Interpolator().createLinearInterpolator(false); var DefaultPPInterpolator= new CAAT.Interpolator().createLinearInterpolator(true); CAAT.Behavior.prototype= { lifecycleListenerList: null, // observer list. behaviorStartTime: -1, // scene time to start applying the behavior behaviorDuration: -1, // behavior duration in ms. cycleBehavior: false, // apply forever ? status: CAAT.Behavior.NOT_STARTED, interpolator: null, // behavior application function. linear by default. actor: null, // actor the Behavior acts on. id: 0, // an integer id suitable to identify this behavior by number. timeOffset: 0, doValueApplication: true, solved : true, setValueApplication : function( apply ) { this.doValueApplication= apply; return this; }, setTimeOffset : function( offset ) { this.timeOffset= offset; return this; }, /** * Sets this behavior id. * @param id an integer. * */ setId : function( id ) { this.id= id; return this; }, /** * Sets the default interpolator to a linear ramp, that is, behavior will be applied linearly. * @return this */ setDefaultInterpolator : function() { this.interpolator= DefaultInterpolator; return this; }, /** * Sets default interpolator to be linear from 0..1 and from 1..0. * @return this */ setPingPong : function() { this.interpolator= DefaultPPInterpolator; return this; }, /** * * @param status {CAAT.Behavior.Status} */ setStatus : function(status) { this.status= status; }, /** * Sets behavior start time and duration. * Scene time will be the time of the scene the behavior actor is bound to. * @param startTime {number} an integer indicating behavior start time in scene time in ms.. * @param duration {number} an integer indicating behavior duration in ms. */ setFrameTime : function( startTime, duration ) { this.behaviorStartTime= startTime; this.behaviorDuration= duration; this.setStatus( CAAT.Behavior.Status.NOT_STARTED ); return this; }, /** * Sets behavior start time and duration but instead as setFrameTime which sets initial time as absolute time * regarding scene's time, it uses a relative time offset from current scene time. * a call to * setFrameTime( scene.time, duration ) is equivalent to * setDelayTime( 0, duration ) * @param delay {number} * @param duration {number} */ setDelayTime : function( delay, duration ) { this.behaviorStartTime= delay; this.behaviorDuration= duration; this.setStatus( CAAT.Behavior.Status.NOT_STARTED ); this.solved= false; return this; }, setOutOfFrameTime : function() { this.setStatus( CAAT.Behavior.Status.EXPIRED ); this.behaviorStartTime= Number.MAX_VALUE; this.behaviorDuration= 0; return this; }, /** * Changes behavior default interpolator to another instance of CAAT.Interpolator. * If the behavior is not defined by CAAT.Interpolator factory methods, the interpolation function must return * its values in the range 0..1. The behavior will only apply for such value range. * @param interpolator a CAAT.Interpolator instance. */ setInterpolator : function(interpolator) { this.interpolator= interpolator; return this; }, /** * This method must no be called directly. * The director loop will call this method in orther to apply actor behaviors. * @param time the scene time the behaviro is being applied at. * @param actor a CAAT.Actor instance the behavior is being applied to. */ apply : function( time, actor ) { if ( !this.solved ) { this.behaviorStartTime+= time; this.solved= true; } time+= this.timeOffset*this.behaviorDuration; var orgTime= time; if ( this.isBehaviorInTime(time,actor) ) { time= this.normalizeTime(time); this.fireBehaviorAppliedEvent( actor, orgTime, time, this.setForTime( time, actor ) ); } }, /** * Sets the behavior to cycle, ie apply forever. * @param bool a boolean indicating whether the behavior is cycle. */ setCycle : function(bool) { this.cycleBehavior= bool; return this; }, /** * Adds an observer to this behavior. * @param behaviorListener an observer instance. */ addListener : function( behaviorListener ) { this.lifecycleListenerList.push(behaviorListener); return this; }, /** * Remove all registered listeners to the behavior. */ emptyListenerList : function() { this.lifecycleListenerList= []; return this; }, /** * @return an integer indicating the behavior start time in ms.. */ getStartTime : function() { return this.behaviorStartTime; }, /** * @return an integer indicating the behavior duration time in ms. */ getDuration : function() { return this.behaviorDuration; }, /** * Chekcs whether the behaviour is in scene time. * In case it gets out of scene time, and has not been tagged as expired, the behavior is expired and observers * are notified about that fact. * @param time the scene time to check the behavior against. * @param actor the actor the behavior is being applied to. * @return a boolean indicating whether the behavior is in scene time. */ isBehaviorInTime : function(time,actor) { var S= CAAT.Behavior.Status; if ( this.status===S.EXPIRED || this.behaviorStartTime<0 ) { return false; } if ( this.cycleBehavior ) { if ( time>=this.behaviorStartTime ) { time= (time-this.behaviorStartTime)%this.behaviorDuration + this.behaviorStartTime; } } if ( time>this.behaviorStartTime+this.behaviorDuration ) { if ( this.status!==S.EXPIRED ) { this.setExpired(actor,time); } return false; } if ( this.status===S.NOT_STARTED ) { this.status=S.STARTED; this.fireBehaviorStartedEvent(actor,time); } return this.behaviorStartTime<=time; // && time * A ContainerBehavior is a holder to sum up different behaviors. *

    * It imposes some constraints to contained Behaviors: *

    * *

    * A ContainerBehavior can contain other ContainerBehaviors at will. *

    * A ContainerBehavior will not apply any CAAT.Actor property change by itself, but will instrument its contained * Behaviors to do so. * * @constructor * @extends CAAT.Behavior */ CAAT.ContainerBehavior= function() { CAAT.ContainerBehavior.superclass.constructor.call(this); this.behaviors= []; return this; }; CAAT.ContainerBehavior.prototype= { behaviors: null, // contained behaviors array /** * Proportionally change this container duration to its children. * @param duration {number} new duration in ms. * @return this; */ conformToDuration : function( duration ) { this.duration= duration; var f= duration/this.duration; var bh; for( var i=0; i=time ) { // 3.- renormalizar tiempo reltivo a comportamiento. time= (time-bh.behaviorStartTime)/bh.behaviorDuration; // 4.- obtener valor de comportamiento para tiempo normalizado relativo a contenedor cssRuleValue= bh.calculateKeyFrameData(time); cssProperty= bh.getPropertyName(prefix); if ( typeof retValue[cssProperty] ==='undefined' ) { retValue[cssProperty]= ""; } // 5.- asignar a objeto, par de propiedad/valor css retValue[cssProperty]+= cssRuleValue+" "; } } } var tr=""; var pv; function xx(pr) { if ( retValue[pr] ) { tr+= retValue[pr]; } else { if ( prevValues ) { pv= prevValues[pr]; if ( pv ) { tr+= pv; retValue[pr]= pv; } } } } xx('translate'); xx('rotate'); xx('scale'); var keyFrameRule= ""; if ( tr ) { keyFrameRule='-'+prefix+'-transform: '+tr+';'; } tr=""; xx('opacity'); if( tr ) { keyFrameRule+= ' opacity: '+tr+';'; } return { rules: keyFrameRule, ret: retValue }; }, /** * * @param prefix * @param name * @param keyframessize */ calculateKeyFramesData : function(prefix, name, keyframessize) { if ( this.duration===Number.MAX_VALUE ) { return ""; } if ( typeof keyframessize==='undefined' ) { keyframessize=100; } var i; var prevValues= null; var kfd= "@-"+prefix+"-keyframes "+name+" {"; var ret; var time; var kfr; for( i=0; i<=keyframessize; i++ ) { time= this.interpolator.getPosition(i/keyframessize).y; ret= this.calculateKeyFrameData(time, prefix, prevValues); kfr= "" + (i/keyframessize*100) + "%" + // percentage "{" + ret.rules + "}\n"; prevValues= ret.ret; kfd+= kfr; } kfd+= "}"; return kfd; } }; extend( CAAT.ContainerBehavior, CAAT.Behavior, null ); })(); (function() { /** * This class applies a rotation to a CAAt.Actor instance. * StartAngle, EndAngle must be supplied. Angles are in radians. * The RotationAnchor, if not supplied, will be ANCHOR_CENTER. * * An example os use will be * * var rb= new CAAT.RotateBehavior(). * setValues(0,2*Math.PI). * setFrameTime(0,2500); * * @see CAAT.Actor. * * @constructor * @extends CAAT.Behavior * */ CAAT.RotateBehavior= function() { CAAT.RotateBehavior.superclass.constructor.call(this); this.anchor= CAAT.Actor.prototype.ANCHOR_CENTER; return this; }; CAAT.RotateBehavior.prototype= { startAngle: 0, // behavior start angle endAngle: 0, // behavior end angle anchorX: .50, // rotation center x. anchorY: .50, // rotation center y. getPropertyName : function() { return "rotate"; }, /** * Behavior application function. * Do not call directly. * @param time an integer indicating the application time. * @param actor a CAAT.Actor the behavior will be applied to. * @return the set angle. */ setForTime : function(time,actor) { var angle= this.startAngle + time*(this.endAngle-this.startAngle); if ( this.doValueApplication ) { actor.setRotationAnchored(angle, this.anchorX, this.anchorY); } return angle; }, /** * Set behavior bound values. * if no anchorx,anchory values are supplied, the behavior will assume * 50% for both values, that is, the actor's center. * * Be aware the anchor values are supplied in RELATIVE PERCENT to * actor's size. * * @param startAngle {float} indicating the starting angle. * @param endAngle {float} indicating the ending angle. * @param anchorx {float} the percent position for anchorX * @param anchory {float} the percent position for anchorY */ setValues : function( startAngle, endAngle, anchorx, anchory ) { this.startAngle= startAngle; this.endAngle= endAngle; if ( typeof anchorx!=='undefined' && typeof anchory!=='undefined' ) { this.anchorX= anchorx; this.anchorY= anchory; } return this; }, /** * @deprecated * Use setValues instead * @param start * @param end */ setAngles : function( start, end ) { return this.setValues(start,end); }, /** * Set the behavior rotation anchor. Use this method when setting an exact percent * by calling setValues is complicated. * @see CAAT.Actor * @param anchor any of CAAT.Actor.prototype.ANCHOR_* constants. * * These parameters are to set a custom rotation anchor point. if anchor==CAAT.Actor.prototype.ANCHOR_CUSTOM * the custom rotation point is set. * @param rx * @param ry * */ setAnchor : function( actor, rx, ry ) { this.anchorX= rx/actor.width; this.anchorY= ry/actor.height; return this; }, calculateKeyFrameData : function( time ) { time= this.interpolator.getPosition(time).y; return "rotate(" + (this.startAngle + time*(this.endAngle-this.startAngle)) +"rad)"; }, /** * @param prefix {string} browser vendor prefix * @param name {string} keyframes animation name * @param keyframessize {integer} number of keyframes to generate * @override */ calculateKeyFramesData : function(prefix, name, keyframessize) { if ( typeof keyframessize==='undefined' ) { keyframessize= 100; } keyframessize>>=0; var i; var kfr; var kfd= "@-"+prefix+"-keyframes "+name+" {"; for( i=0; i<=keyframessize; i++ ) { kfr= "" + (i/keyframessize*100) + "%" + // percentage "{" + "-"+prefix+"-transform:" + this.calculateKeyFrameData(i/keyframessize) + "}\n"; kfd+= kfr; } kfd+="}"; return kfd; } }; extend( CAAT.RotateBehavior, CAAT.Behavior, null); })(); (function() { /** *

    * A generic behavior is supposed to be extended to create new behaviors when the out-of-the-box * ones are not sufficient. It applies the behavior result to a given target object in two ways: * *

      *
    1. defining the property parameter: the toolkit will perform target_object[property]= calculated_value_for_time. *
    2. defining a callback function. Sometimes setting of a property is not enough. In example, * for a give property in a DOM element, it is needed to set object.style['left']= '70px'; * With the property approach, you won't be able to add de 'px' suffix to the value, and hence won't * work correctly. The function callback will allow to take control by receiving as parameters the * target object, and the calculated value to apply by the behavior for the given time. *
    * *

    * For example, this code will move a dom element from 0 to 400 px on x during 1 second: * *

    * var enterBehavior= new CAAT.GenericBehavior().
    *   setFrameTime( scene.time, 1000 ).
    *   setValues(
    *     0,
    *     400,
    *     domElement,
    *     null,
    *     function( currentValue, target ) {
    *       target.style['left']= currentValue+'px';
    *     }
    *   );
    *
    * * @constructor * @extends CAAT.Behavior * */ CAAT.GenericBehavior= function() { CAAT.GenericBehavior.superclass.constructor.call(this); return this; }; CAAT.GenericBehavior.prototype= { start: 0, end: 0, target: null, property: null, callback: null, /** * Sets the target objects property to the corresponding value for the given time. * If a callback function is defined, it is called as well. * * @param time {number} the scene time to apply the behavior at. * @param actor {CAAT.Actor} a CAAT.Actor object instance. */ setForTime : function(time, actor) { var value= this.start+ time*(this.end-this.start); if ( this.callback ) { this.callback( value, this.target, actor ); } if ( this.property ) { this.target[this.property]= value; } }, /** * Defines the values to apply this behavior. * * @param start {number} initial behavior value. * @param end {number} final behavior value. * @param target {object} an object. Usually a CAAT.Actor. * @param property {string} target object's property to set value to. * @param callback {function} a function of the form function( target, value ). */ setValues : function( start, end, target, property, callback ) { this.start= start; this.end= end; this.target= target; this.property= property; this.callback= callback; return this; } }; extend( CAAT.GenericBehavior, CAAT.Behavior, null); })(); (function() { /** * ScaleBehavior applies scale affine transforms in both axis. * StartScale and EndScale must be supplied for each axis. This method takes care of a FF bug in which if a Scale is * set to 0, the animation will fail playing. * * This behavior specifies anchors in values ranges 0..1 * * @constructor * @extends CAAT.Behavior * */ CAAT.ScaleBehavior= function() { CAAT.ScaleBehavior.superclass.constructor.call(this); this.anchor= CAAT.Actor.prototype.ANCHOR_CENTER; return this; }; CAAT.ScaleBehavior.prototype= { startScaleX: 1, endScaleX: 1, startScaleY: 1, endScaleY: 1, anchorX: .50, anchorY: .50, getPropertyName : function() { return "scale"; }, /** * Applies corresponding scale values for a given time. * * @param time the time to apply the scale for. * @param actor the target actor to Scale. * @return {object} an object of the form { scaleX: {float}, scaleY: {float}�} */ setForTime : function(time,actor) { var scaleX= this.startScaleX + time*(this.endScaleX-this.startScaleX); var scaleY= this.startScaleY + time*(this.endScaleY-this.startScaleY); // Firefox 3.x & 4, will crash animation if either scaleX or scaleY equals 0. if (0===scaleX ) { scaleX=0.01; } if (0===scaleY ) { scaleY=0.01; } if ( this.doValueApplication ) { actor.setScaleAnchored( scaleX, scaleY, this.anchorX, this.anchorY ); } return { scaleX: scaleX, scaleY: scaleY }; }, /** * Define this scale behaviors values. * * Be aware the anchor values are supplied in RELATIVE PERCENT to * actor's size. * * @param startX {number} initial X axis scale value. * @param endX {number} final X axis scale value. * @param startY {number} initial Y axis scale value. * @param endY {number} final Y axis scale value. * @param anchorx {float} the percent position for anchorX * @param anchory {float} the percent position for anchorY * * @return this. */ setValues : function( startX, endX, startY, endY, anchorx, anchory ) { this.startScaleX= startX; this.endScaleX= endX; this.startScaleY= startY; this.endScaleY= endY; if ( typeof anchorx!=='undefined' && typeof anchory!=='undefined' ) { this.anchorX= anchorx; this.anchorY= anchory; } return this; }, /** * Set an exact position scale anchor. Use this method when it is hard to * set a thorough anchor position expressed in percentage. * @param actor * @param x * @param y */ setAnchor : function( actor, x, y ) { this.anchorX= x/actor.width; this.anchorY= y/actor.height; return this; }, calculateKeyFrameData : function( time ) { var scaleX; var scaleY; time= this.interpolator.getPosition(time).y; scaleX= this.startScaleX + time*(this.endScaleX-this.startScaleX); scaleY= this.startScaleY + time*(this.endScaleY-this.startScaleY); return "scaleX("+scaleX+") scaleY("+scaleY+")"; }, calculateKeyFramesData : function(prefix, name, keyframessize) { if ( typeof keyframessize==='undefined' ) { keyframessize= 100; } keyframessize>>=0; var i; var kfr; var kfd= "@-"+prefix+"-keyframes "+name+" {"; for( i=0; i<=keyframessize; i++ ) { kfr= "" + (i/keyframessize*100) + "%" + // percentage "{" + "-"+prefix+"-transform:" + this.calculateKeyFrameData(i/keyframessize) + "}"; kfd+= kfr; } kfd+="}"; return kfd; } }; extend( CAAT.ScaleBehavior, CAAT.Behavior, null); })(); (function() { /** * AlphaBehavior modifies alpha composition property for an actor. * * @constructor * @extends CAAT.Behavior */ CAAT.AlphaBehavior= function() { CAAT.AlphaBehavior.superclass.constructor.call(this); return this; }; CAAT.AlphaBehavior.prototype= { startAlpha: 0, endAlpha: 0, getPropertyName : function() { return "opacity"; }, /** * Applies corresponding alpha transparency value for a given time. * * @param time the time to apply the scale for. * @param actor the target actor to set transparency for. * @return {number} the alpha value set. Normalized from 0 (total transparency) to 1 (total opacity) */ setForTime : function(time,actor) { var alpha= (this.startAlpha+time*(this.endAlpha-this.startAlpha)); if ( this.doValueApplication ) { actor.setAlpha( alpha ); } return alpha; }, /** * Set alpha transparency minimum and maximum value. * This value can be coerced by Actor's property isGloblAlpha. * * @param start {number} a float indicating the starting alpha value. * @param end {number} a float indicating the ending alpha value. */ setValues : function( start, end ) { this.startAlpha= start; this.endAlpha= end; return this; }, calculateKeyFrameData : function( time ) { time= this.interpolator.getPosition(time).y; return (this.startAlpha+time*(this.endAlpha-this.startAlpha)); }, /** * @param prefix {string} browser vendor prefix * @param name {string} keyframes animation name * @param keyframessize {integer} number of keyframes to generate * @override */ calculateKeyFramesData : function(prefix, name, keyframessize) { if ( typeof keyframessize==='undefined' ) { keyframessize= 100; } keyframessize>>=0; var i; var kfr; var kfd= "@-"+prefix+"-keyframes "+name+" {"; for( i=0; i<=keyframessize; i++ ) { kfr= "" + (i/keyframessize*100) + "%" + // percentage "{" + "opacity: " + this.calculateKeyFrameData( i / keyframessize ) + "}"; kfd+= kfr; } kfd+="}"; return kfd; } }; extend( CAAT.AlphaBehavior, CAAT.Behavior, null); })(); (function() { /** * CAAT.PathBehavior modifies the position of a CAAT.Actor along the path represented by an * instance of CAAT.Path. * * @constructor * @extends CAAT.Behavior * */ CAAT.PathBehavior= function() { CAAT.PathBehavior.superclass.constructor.call(this); return this; }; /** * @enum */ CAAT.PathBehavior.autorotate = { LEFT_TO_RIGHT: 0, // fix left_to_right direction RIGHT_TO_LEFT: 1, // fix right_to_left FREE: 2 // do not apply correction }; CAAT.PathBehavior.prototype= { path: null, // the path to traverse autoRotate : false, // set whether the actor must be rotated tangentially to the path. prevX: -1, // private, do not use. prevY: -1, // private, do not use. autoRotateOp: CAAT.PathBehavior.autorotate.FREE, getPropertyName : function() { return "translate"; }, /** * Sets an actor rotation to be heading from past to current path's point. * Take into account that this will be incompatible with rotation Behaviors * since they will set their own rotation configuration. * @param autorotate {boolean} * @param autorotateOp {CAAT.PathBehavior.autorotate} whether the sprite is drawn heading to the right. * @return this. */ setAutoRotate : function( autorotate, autorotateOp ) { this.autoRotate= autorotate; if (autorotateOp!==undefined) { this.autoRotateOp= autorotateOp; } return this; }, /** * Set the behavior path. * The path can be any length, and will take behaviorDuration time to be traversed. * @param {CAAT.Path} * * @deprecated */ setPath : function(path) { this.path= path; return this; }, /** * Set the behavior path. * The path can be any length, and will take behaviorDuration time to be traversed. * @param {CAAT.Path} * @return this */ setValues : function(path) { return this.setPath(path); }, /** * @see Acotr.setPositionAcchor * @deprecated * @param tx a float with xoffset. * @param ty a float with yoffset. */ setTranslation : function( tx, ty ) { return this; }, calculateKeyFrameData : function( time ) { time= this.interpolator.getPosition(time).y; var point= this.path.getPosition(time); return "translateX("+point.x+"px) translateY("+point.y+"px)" ; }, calculateKeyFramesData : function(prefix, name, keyframessize) { if ( typeof keyframessize==='undefined' ) { keyframessize= 100; } keyframessize>>=0; var i; var kfr; var time; var kfd= "@-"+prefix+"-keyframes "+name+" {"; for( i=0; i<=keyframessize; i++ ) { kfr= "" + (i/keyframessize*100) + "%" + // percentage "{" + "-"+prefix+"-transform:" + this.calculateKeyFrameData(i/keyframessize) + "}"; kfd+= kfr; } kfd+="}"; return kfd; }, /** * Translates the Actor to the corresponding time path position. * If autoRotate=true, the actor is rotated as well. The rotation anchor will (if set) always be ANCHOR_CENTER. * @param time an integer indicating the time the behavior is being applied at. * @param actor a CAAT.Actor instance to be translated. * @return {object} an object of the form { x: {float}, y: {float}�}. */ setForTime : function(time,actor) { if ( !this.path ) { return { x: actor.x, y: actor.y }; } var point= this.path.getPosition(time); if ( this.autoRotate ) { if ( -1===this.prevX && -1===this.prevY ) { this.prevX= point.x; this.prevY= point.y; } var ax= point.x-this.prevX; var ay= point.y-this.prevY; if ( ax===0 && ay===0 ) { actor.setLocation( point.x, point.y ); return { x: actor.x, y: actor.y }; } var angle= Math.atan2( ay, ax ); var si= CAAT.SpriteImage.prototype; var pba= CAAT.PathBehavior.autorotate; // actor is heading left to right if ( this.autoRotateOp===pba.LEFT_TO_RIGHT ) { if ( this.prevX<=point.x ) { actor.setImageTransformation( si.TR_NONE ); } else { actor.setImageTransformation( si.TR_FLIP_HORIZONTAL ); angle+=Math.PI; } } else if ( this.autoRotateOp===pba.RIGHT_TO_LEFT ) { if ( this.prevX<=point.x ) { actor.setImageTransformation( si.TR_FLIP_HORIZONTAL ); } else { actor.setImageTransformation( si.TR_NONE ); angle-=Math.PI; } } actor.setRotation(angle); this.prevX= point.x; this.prevY= point.y; var modulo= Math.sqrt(ax*ax+ay*ay); ax/=modulo; ay/=modulo; } if ( this.doValueApplication ) { actor.setLocation( point.x, point.y ); return { x: actor.x, y: actor.y }; } else { return { x: point.x, y: point.y }; } }, /** * Get a point on the path. * If the time to get the point at is in behaviors frame time, a point on the path will be returned, otherwise * a default {x:-1, y:-1} point will be returned. * * @param time {number} the time at which the point will be taken from the path. * @return {object} an object of the form {x:float y:float} */ positionOnTime : function(time) { if ( this.isBehaviorInTime(time,null) ) { time= this.normalizeTime(time); return this.path.getPosition( time ); } return {x:-1, y:-1}; } }; extend( CAAT.PathBehavior, CAAT.Behavior ); })(); (function() { /** * ColorBehavior interpolates between two given colors. * @constructor */ CAAT.ColorBehavior= function() { return this; }; CAAT.ColorBehavior.prototype= { }; extend( CAAT.ColorBehavior, CAAT.Behavior ); })(); (function() { /** * * Scale only X or Y axis, instead both at the same time as ScaleBehavior. * * @constructor */ CAAT.Scale1Behavior= function() { CAAT.Scale1Behavior.superclass.constructor.call(this); this.anchor= CAAT.Actor.prototype.ANCHOR_CENTER; return this; }; CAAT.Scale1Behavior.prototype= { startScale: 1, endScale: 1, anchorX: .50, anchorY: .50, sx : 1, sy : 1, applyOnX : true, getPropertyName : function() { return "scale"; }, /** * Applies corresponding scale values for a given time. * * @param time the time to apply the scale for. * @param actor the target actor to Scale. * @return {object} an object of the form { scaleX: {float}, scaleY: {float}�} */ setForTime : function(time,actor) { var scale= this.startScale + time*(this.endScale-this.startScale); // Firefox 3.x & 4, will crash animation if either scaleX or scaleY equals 0. if (0===scale ) { scale=0.01; } if ( this.doValueApplication ) { if ( this.applyOnX ) { actor.setScaleAnchored( scale, actor.scaleY, this.anchorX, this.anchorY ); } else { actor.setScaleAnchored( actor.scaleX, scale, this.anchorX, this.anchorY ); } } return scale; }, /** * Define this scale behaviors values. * * Be aware the anchor values are supplied in RELATIVE PERCENT to * actor's size. * * @param start {number} initial X axis scale value. * @param end {number} final X axis scale value. * @param anchorx {float} the percent position for anchorX * @param anchory {float} the percent position for anchorY * * @return this. */ setValues : function( start, end, applyOnX, anchorx, anchory ) { this.startScale= start; this.endScale= end; this.applyOnX= !!applyOnX; if ( typeof anchorx!=='undefined' && typeof anchory!=='undefined' ) { this.anchorX= anchorx; this.anchorY= anchory; } return this; }, /** * Set an exact position scale anchor. Use this method when it is hard to * set a thorough anchor position expressed in percentage. * @param actor * @param x * @param y */ setAnchor : function( actor, x, y ) { this.anchorX= x/actor.width; this.anchorY= y/actor.height; return this; }, calculateKeyFrameData : function( time ) { var scale; time= this.interpolator.getPosition(time).y; scale= this.startScale + time*(this.endScale-this.startScale); return this.applyOnX ? "scaleX("+scale+")" : "scaleY("+scale+")"; }, calculateKeyFramesData : function(prefix, name, keyframessize) { if ( typeof keyframessize==='undefined' ) { keyframessize= 100; } keyframessize>>=0; var i; var kfr; var kfd= "@-"+prefix+"-keyframes "+name+" {"; for( i=0; i<=keyframessize; i++ ) { kfr= "" + (i/keyframessize*100) + "%" + // percentage "{" + "-"+prefix+"-transform:" + this.calculateKeyFrameData(i/keyframessize) + "}"; kfd+= kfr; } kfd+="}"; return kfd; } }; extend( CAAT.Scale1Behavior, CAAT.Behavior ); })();/** * See LICENSE file. * * This object manages CSS3 transitions reflecting applying behaviors. * **/ (function() { CAAT.CSS= {}; CAAT.CSS.PREFIX= (function() { var prefix = ""; var prefixes = ['WebKit', 'Moz', 'O']; var keyframes= ""; // guess this browser vendor prefix. for (var i = 0; i < prefixes.length; i++) { if (window[prefixes[i] + 'CSSKeyframeRule']) { prefix = prefixes[i].toLowerCase(); break; } } CAAT.CSS.PROP_ANIMATION= '-'+prefix+'-animation'; return prefix; })(); CAAT.CSS.applyKeyframe= function( domElement, name, secs, forever ) { domElement.style[CAAT.CSS.PROP_ANIMATION]= name+' '+(secs/1000)+'s linear both '+(forever ? 'infinite' : '') ; }; CAAT.CSS.unregisterKeyframes= function( name ) { var index= CAAT.CSS.getCSSKeyframesIndex(name); if ( -1!==index ) { document.styleSheets[0].deleteRule( index ); } }; /** * * @param kfDescriptor {object{ name{string}, behavior{CAAT.Behavior}, size{!number}, overwrite{boolean}} */ CAAT.CSS.registerKeyframes= function( kfDescriptor ) { var name= kfDescriptor.name; var behavior= kfDescriptor.behavior; var size= kfDescriptor.size; var overwrite= kfDescriptor.overwrite; if ( typeof name==='undefined' || typeof behavior==='undefined' ) { throw 'Keyframes must be defined by a name and a CAAT.Behavior instance.'; } if ( typeof size==='undefined' ) { size= 100; } if ( typeof overwrite==='undefined' ) { overwrite= false; } // find if keyframes has already a name set. var cssRulesIndex= CAAT.CSS.getCSSKeyframesIndex(name); if (-1!==cssRulesIndex && !overwrite) { return; } var keyframesRule= behavior.calculateKeyframesData(CAAT.CSS.PREFIX, name, size ); if (document.styleSheets) { if ( !document.styleSheets.length) { var s = document.createElement('style'); s.type="text/css"; document.getElementsByTagName('head')[ 0 ].appendChild(s); } if ( -1!==cssRulesIndex ) { document.styleSheets[0].deleteRule( cssRulesIndex ); } document.styleSheets[0].insertRule( keyframesRule, 0 ); } }; CAAT.CSS.getCSSKeyframesIndex= function(name) { var ss = document.styleSheets; for (var i = ss.length - 1; i >= 0; i--) { try { var s = ss[i], rs = s.cssRules ? s.cssRules : s.rules ? s.rules : []; for (var j = rs.length - 1; j >= 0; j--) { if ( ( rs[j].type === window.CSSRule.WEBKIT_KEYFRAMES_RULE || rs[j].type === window.CSSRule.MOZ_KEYFRAMES_RULE ) && rs[j].name === name) { return j; } } } catch(e) { } } return -1; }; CAAT.CSS.getCSSKeyframes= function(name) { var ss = document.styleSheets; for (var i = ss.length - 1; i >= 0; i--) { try { var s = ss[i], rs = s.cssRules ? s.cssRules : s.rules ? s.rules : []; for (var j = rs.length - 1; j >= 0; j--) { if ( ( rs[j].type === window.CSSRule.WEBKIT_KEYFRAMES_RULE || rs[j].type === window.CSSRule.MOZ_KEYFRAMES_RULE ) && rs[j].name === name) { return rs[j]; } } } catch(e) { } } return null; }; })();/** * * taken from: http://www.quirksmode.org/js/detect.html * * 20101008 Hyperandroid. IE9 seems to identify himself as Explorer and stopped calling himself MSIE. * Added Explorer description to browser list. Thanks @alteredq for this tip. * */ (function() { CAAT.BrowserDetect = function() { this.init(); return this; }; CAAT.BrowserDetect.prototype = { browser: '', version: 0, OS: '', init: function() { this.browser = this.searchString(this.dataBrowser) || "An unknown browser"; this.version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || "an unknown version"; this.OS = this.searchString(this.dataOS) || "an unknown OS"; }, searchString: function (data) { for (var i=0;i"+ " #caat-debug {"+ " z-index: 10000;"+ " position:fixed;"+ " bottom:0;"+ " left:0;"+ " width:100%;"+ " background-color: rgba(0,0,0,0.8);"+ " }"+ " #caat-debug.caat_debug_max {"+ " margin-bottom: 0px;"+ " }"+ " .caat_debug_bullet {"+ " display:inline-block;"+ " background-color:#f00;"+ " width:8px;"+ " height:8px;"+ " border-radius: 4px;"+ " margin-left:10px;"+ " margin-right:2px;"+ " }"+ " .caat_debug_description {"+ " font-size:11px;"+ " font-family: helvetica, arial;"+ " color: #aaa;"+ " display: inline-block;"+ " }"+ " .caat_debug_value {"+ " font-size:11px;"+ " font-family: helvetica, arial;"+ " color: #fff;"+ " width:25px;"+ " text-align: right;"+ " display: inline-block;"+ " margin-right: .3em;"+ " }"+ " .caat_debug_indicator {"+ " float: right;"+ " }"+ " #debug_tabs {"+ " border-top: 1px solid #888;"+ " height:25px;"+ " }"+ " .tab_max_min {"+ " font-family: helvetica, arial;"+ " font-size: 12px;"+ " font-weight: bold;"+ " color: #888;"+ " border-right: 1px solid #888;"+ " float: left;"+ " cursor: pointer;"+ " padding-left: 5px;"+ " padding-right: 5px;"+ " padding-top: 5px;"+ " height: 20px;"+ " }"+ " .debug_tabs_content_hidden {"+ " display: none;"+ " width: 100%;"+ " }"+ " .debug_tabs_content_visible {"+ " display: block;"+ " width: 100%;"+ " }"+ " .checkbox_enabled {"+ " display:inline-block;"+ " background-color:#eee;"+ " border: 1px solid #eee;"+ " width:6px;"+ " height:8px;"+ " margin-left:12px;"+ " margin-right:2px;"+ " cursor: pointer;"+ " }"+ " .checkbox_disabled {"+ " display:inline-block;"+ " width:6px;"+ " height:8px;"+ " background-color: #333;"+ " border: 1px solid #eee;"+ " margin-left:12px;"+ " margin-right:2px;"+ " cursor: pointer;"+ " }"+ " .checkbox_description {"+ " font-size:11px;"+ " font-family: helvetica, arial;"+ " color: #fff;"+ " }"+ " .debug_tab {"+ " font-family: helvetica, arial;"+ " font-size: 12px;"+ " color: #fff;"+ " border-right: 1px solid #888;"+ " float: left;"+ " padding-left: 5px;"+ " padding-right: 5px;"+ " height: 20px;"+ " padding-top: 5px;"+ " cursor: default;"+ " }"+ " .debug_tab_selected {"+ " background-color: #444;"+ " cursor: default;"+ " }"+ " .debug_tab_not_selected {"+ " background-color: #000;"+ " cursor: pointer;"+ " }"+ " "+ "

    "+ "
    "+ " CAAT Debug panel "+ " Performance"+ " Controls"+ " "+ " "+ " Draw Time: "+ " 5.46"+ " ms."+ " "+ " "+ " "+ " FPS: "+ " 48"+ " "+ "
    "+ "
    "+ "
    "+ " "+ "
    "+ " "+ " "+ " RAF Time:"+ " 20.76"+ " ms."+ " "+ " "+ " "+ " Entities Total: "+ " 41"+ " "+ " "+ " "+ " Entities Active: "+ " 37"+ " "+ " "+ " "+ " Draws: "+ " 0"+ " "+ " "+ " "+ " DirtyRects: "+ " 0"+ " "+ "
    "+ "
    "+ "
    "+ "
    "+ "
    "+ " "+ " Sound"+ "
    "+ "
    "+ " "+ " Music"+ "
    "+ "
    "+ " "+ " AA Bounding Boxes"+ "
    "+ "
    "+ " "+ " Bounding Boxes"+ "
    "+ "
    "+ " "+ " Dirty Rects"+ "
    "+ "
    "+ "
    "+ "
    "+ "
    ", setScale : function(s) { this.scale= s; return this; }, initialize: function(w,h) { w= window.innerWidth; this.width= w; this.height= h; this.framerate = { refreshInterval: CAAT.FPS_REFRESH || 500, // refresh every ? ms, updating too quickly gives too large rounding errors frames: 0, // number offrames since last refresh timeLastRefresh: 0, // When was the framerate counter refreshed last fps: 0, // current framerate prevFps: -1, // previously drawn FPS fpsMin: 1000, // minimum measured framerate fpsMax: 0 // maximum measured framerate }; var debugContainer= document.getElementById('caat-debug'); if (!debugContainer) { var wrap = document.createElement('div'); wrap.innerHTML=this.debugTpl; document.body.appendChild(wrap); eval( ""+ " function initCheck( name, bool, callback ) {"+ " var elem= document.getElementById(name);"+ " if ( elem ) {"+ " elem.className= (bool) ? \"checkbox_enabled\" : \"checkbox_disabled\";"+ " if ( callback ) {"+ " elem.addEventListener( \"click\", (function(elem, callback) {"+ " return function(e) {"+ " elem.__value= !elem.__value;"+ " elem.className= (elem.__value) ? \"checkbox_enabled\" : \"checkbox_disabled\";"+ " callback(e,elem.__value);"+ " }"+ " })(elem, callback), false );"+ " }"+ " elem.__value= bool;"+ " }"+ " }"+ " function setupTabs() {"+ " var numTabs=0;"+ " var elem;"+ " var elemContent;"+ " do {"+ " elem= document.getElementById(\"caat-debug-tab\"+numTabs);"+ " if ( elem ) {"+ " elemContent= document.getElementById(\"caat-debug-tab\"+numTabs+\"-content\");"+ " if ( elemContent ) {"+ " elemContent.style.display= numTabs===0 ? 'block' : 'none';"+ " elem.className= numTabs===0 ? \"debug_tab debug_tab_selected\" : \"debug_tab debug_tab_not_selected\";"+ " elem.addEventListener( \"click\", (function(tabIndex) {"+ " return function(e) {"+ " for( var i=0; i this.framerate.timeLastRefresh + this.framerate.refreshInterval ) { this.framerate.fps = ( ( this.framerate.frames * 1000 ) / ( CAAT.RAF - this.framerate.timeLastRefresh ) ) | 0; this.framerate.fpsMin = this.framerate.frames > 0 ? Math.min( this.framerate.fpsMin, this.framerate.fps ) : this.framerate.fpsMin; this.framerate.fpsMax = Math.max( this.framerate.fpsMax, this.framerate.fps ); this.textFPS.innerHTML= this.framerate.fps; var value= ((this.frameTimeAcc*100/this.framerate.frames)|0)/100; this.frameTimeAcc=0; this.textDrawTime.innerHTML= value; var value2= ((this.frameRAFAcc*100/this.framerate.frames)|0)/100; this.frameRAFAcc=0; this.textRAFTime.innerHTML= value2; this.framerate.timeLastRefresh = CAAT.RAF; this.framerate.frames = 0; this.paint(value2); } this.textEntitiesTotal.innerHTML= this.statistics.size_total; this.textEntitiesActive.innerHTML= this.statistics.size_active; this.textDirtyRects.innerHTML= this.statistics.size_dirtyRects; this.textDraws.innerHTML= this.statistics.draws; }, paint : function( rafValue ) { var ctx= this.ctx; var t=0; ctx.drawImage( this.canvas, 1, 0, this.width-1, this.height, 0, 0, this.width-1, this.height ); ctx.strokeStyle= 'black'; ctx.beginPath(); ctx.moveTo( this.width-.5, 0 ); ctx.lineTo( this.width-.5, this.height ); ctx.stroke(); ctx.strokeStyle= '#a22'; ctx.beginPath(); t= this.height-((20/this.SCALE*this.height)>>0)-.5; ctx.moveTo( .5, t ); ctx.lineTo( this.width+.5, t ); ctx.stroke(); ctx.strokeStyle= '#aa2'; ctx.beginPath(); t= this.height-((30/this.SCALE*this.height)>>0)-.5; ctx.moveTo( .5, t ); ctx.lineTo( this.width+.5, t ); ctx.stroke(); var fps = Math.min( this.height-(this.framerate.fps/this.SCALE*this.height), 59 ); if (-1===this.framerate.prevFps) { this.framerate.prevFps= fps|0; } ctx.strokeStyle= '#0ff';//this.framerate.fps<15 ? 'red' : this.framerate.fps<30 ? 'yellow' : 'green'; ctx.beginPath(); ctx.moveTo( this.width, (fps|0)-.5 ); ctx.lineTo( this.width, this.framerate.prevFps-.5 ); ctx.stroke(); this.framerate.prevFps= fps; var t1= ((this.height-(rafValue/this.SCALE*this.height))>>0)-.5; ctx.strokeStyle= '#ff0'; ctx.beginPath(); ctx.moveTo( this.width, t1 ); ctx.lineTo( this.width, t1 ); ctx.stroke(); } }; })();/** * See LICENSE file. * * Classes to define animable elements. * Actor is the superclass of every animable element in the scene graph. It handles the whole * affine transformation MatrixStack, rotation, translation, globalAlpha and Behaviours. It also * defines input methods. * TODO: add text presentation/animation effects. **/ (function() { /** * This class is the base for all animable entities in CAAT. * It defines an entity able to: * *
      *
    • Position itself on screen. *
    • Able to modify its presentation aspect via affine transforms. *
    • Take control of parent/child relationship. *
    • Take track of behaviors (@see CAAT.Behavior). *
    • Define a region on screen. *
    • Define alpha composition scope. *
    • Expose lifecycle. *
    • Manage itself in/out scene time. *
    • etc. *
    * * @constructor */ CAAT.Actor = function() { this.behaviorList= []; // this.keyframesList= []; this.lifecycleListenerList= []; this.AABB= new CAAT.Rectangle(); this.viewVertices= [ new CAAT.Point(0,0,0), new CAAT.Point(0,0,0), new CAAT.Point(0,0,0), new CAAT.Point(0,0,0) ]; this.scaleAnchor= this.ANCHOR_CENTER; this.modelViewMatrix= new CAAT.Matrix(); this.worldModelViewMatrix= new CAAT.Matrix(); this.resetTransform(); this.setScale(1,1); this.setRotation(0); return this; }; /** * Reflection information needed to use the inspector. * Each key defined identifies an object field. For each field, it could be specified: * + get : accessor function or field name. if ended with () a function will be assumed. * + set : mutator function or field name. if ended with () a function will be assumed. * + type : field or accessor function return type. * * If not get or set method is defined, the inspector will assume either the field can't be read and/or set. * If neither get and set are defined, the property will be avoided. * * The key can contain a set of comma separated values. This means these properties must be set/modified * at once in the inspector editor field (if any). The way these functions will be set will be by calling * the set method (must be a method) as previously defined. */ CAAT.Actor.__reflectionInfo= { "x" : "set:setX(), get:x, type:number", "cached" : "get:isCached(), type:boolean", "scaleX,scaleY" : "set:setScale(), type:number" /* "y" : "setY,w", "width" : "setWidth,w", "height" : "setHeight,w", "start_time" : "setStartTime,w", "duration" : "setDuration,w", "clip" : "setClip,w", "rotationAngle" : "setRotation,w", "alpha" : "setAlpha,w", "isGlobalAlpha" : "isGlobalAlpha,w", "visible" : "isVisible", "id" : "getId", "backgroundImage" : ""*/ }; CAAT.Actor.ANCHOR_CENTER= 0; // constant values to determine different affine transform CAAT.Actor.ANCHOR_TOP= 1; // anchors. CAAT.Actor.ANCHOR_BOTTOM= 2; CAAT.Actor.ANCHOR_LEFT= 3; CAAT.Actor.ANCHOR_RIGHT= 4; CAAT.Actor.ANCHOR_TOP_LEFT= 5; CAAT.Actor.ANCHOR_TOP_RIGHT= 6; CAAT.Actor.ANCHOR_BOTTOM_LEFT= 7; CAAT.Actor.ANCHOR_BOTTOM_RIGHT= 8; CAAT.Actor.ANCHOR_CUSTOM= 9; CAAT.Actor.CACHE_SIMPLE= 1; CAAT.Actor.CACHE_DEEP= 2; CAAT.Actor.prototype= { lifecycleListenerList: null, // Array of life cycle listener behaviorList: null, // Array of behaviors to apply to the Actor parent: null, // Parent of this Actor. May be Scene. x: 0, // x position on parent. In parent's local coord. system. y: 0, // y position on parent. In parent's local coord. system. width: 0, // Actor's width. In parent's local coord. system. height: 0, // Actor's height. In parent's local coord. system. start_time: 0, // Start time in Scene time. duration: Number.MAX_VALUE, // Actor duration in Scene time clip: false, // should clip the Actor's content against its contour. clipPath: null, tAnchorX : 0, tAnchorY : 0, scaleX: 0, // transformation. width scale parameter scaleY: 0, // transformation. height scale parameter scaleTX: .50, // transformation. scale anchor x position scaleTY: .50, // transformation. scale anchor y position scaleAnchor: 0, // transformation. scale anchor rotationAngle: 0, // transformation. rotation angle in radians rotationY: .50, // transformation. rotation center y alpha: 1, // alpha transparency value rotationX: .50, // transformation. rotation center x isGlobalAlpha: false, // is this a global alpha frameAlpha: 1, // hierarchically calculated alpha for this Actor. expired: false, // set when the actor has been expired discardable: false, // set when you want this actor to be removed if expired pointed: false, // is the mouse pointer inside this actor mouseEnabled: true, // events enabled ? visible: true, ANCHOR_CENTER: 0, // constant values to determine different affine transform ANCHOR_TOP: 1, // anchors. ANCHOR_BOTTOM: 2, ANCHOR_LEFT: 3, ANCHOR_RIGHT: 4, ANCHOR_TOP_LEFT: 5, ANCHOR_TOP_RIGHT: 6, ANCHOR_BOTTOM_LEFT: 7, ANCHOR_BOTTOM_RIGHT: 8, ANCHOR_CUSTOM: 9, fillStyle: null, // any canvas rendering valid fill style. strokeStyle: null, // any canvas rendering valid stroke style. time: 0, // Cache Scene time. AABB: null, // CAAT.Rectangle viewVertices: null, // model to view transformed vertices. inFrame: false, // boolean indicating whether this Actor was present on last frame. dirty: true, // model view is dirty ? wdirty: true, // world model view is dirty ? oldX: -1, oldY: -1, modelViewMatrix: null, // model view matrix. worldModelViewMatrix: null, // world model view matrix. modelViewMatrixI: null, // model view matrix. worldModelViewMatrixI: null, // world model view matrix. glEnabled: false, backgroundImage: null, id: null, size_active: 1, // number of animated children size_total: 1, __next: null, __d_ax: -1, // for drag-enabled actors. __d_ay: -1, gestureEnabled: false, invalid : true, cached : 0, // 0 no, CACHE_SIMPLE | CACHE_DEEP collides : false, collidesAsRect : true, isAA : true, // is this actor/container Axis aligned ? if so, much faster inverse matrices // can be calculated. isVisible : function() { return this.isVisible; }, setupCollission : function( collides, isCircular ) { this.collides= collides; this.collidesAsRect= !isCircular; }, invalidate : function() { this.invalid= true; }, setGestureEnabled : function( enable ) { this.gestureEnabled= !!enable; }, isGestureEnabled : function() { return this.gestureEnabled; }, getId : function() { return this.id; }, setId : function(id) { this.id= id; return this; }, /** * Set this actor's parent. * @param parent {CAAT.ActorContainer} * @return this */ setParent : function(parent) { this.parent= parent; return this; }, /** * Set this actor's background image. * The need of a background image is to kept compatibility with the new CSSDirector class. * The image parameter can be either an Image/Canvas or a CAAT.SpriteImage instance. If an image * is supplied, it will be wrapped into a CAAT.SriteImage instance of 1 row by 1 column. * If the actor has set an image in the background, the paint method will draw the image, otherwise * and if set, will fill its background with a solid color. * If adjust_size_to_image is true, the host actor will be redimensioned to the size of one * single image from the SpriteImage (either supplied or generated because of passing an Image or * Canvas to the function). That means the size will be set to [width:SpriteImage.singleWidth, * height:singleHeight]. * * WARN: if using a CSS renderer, the image supplied MUST be a HTMLImageElement instance. * * @see CAAT.SpriteImage * * @param image {Image|HTMLCanvasElement|CAAT.SpriteImage} * @param adjust_size_to_image {boolean} whether to set this actor's size based on image parameter. * * @return this */ setBackgroundImage : function(image, adjust_size_to_image ) { if ( image ) { if ( !(image instanceof CAAT.SpriteImage) ) { image= new CAAT.SpriteImage().initialize(image,1,1); } image.setOwner(this); this.backgroundImage= image; if ( typeof adjust_size_to_image==='undefined' || adjust_size_to_image ) { this.width= image.getWidth(); this.height= image.getHeight(); } this.glEnabled= true; } else { this.backgroundImage= null; } return this; }, /** * Set the actor's SpriteImage index from animation sheet. * @see CAAT.SpriteImage * @param index {number} * * @return this */ setSpriteIndex: function(index) { if ( this.backgroundImage ) { this.backgroundImage.setSpriteIndex(index); this.invalidate(); } return this; }, /** * Set this actor's background SpriteImage offset displacement. * The values can be either positive or negative meaning the texture space of this background * image does not start at (0,0) but at the desired position. * @see CAAT.SpriteImage * @param ox {number} horizontal offset * @param oy {number} vertical offset * * @return this */ setBackgroundImageOffset : function( ox, oy ) { if ( this.backgroundImage ) { this.backgroundImage.setOffset(ox,oy); } return this; }, /** * Set this actor's background SpriteImage its animation sequence. * In its simplet's form a SpriteImage treats a given image as an array of rows by columns * subimages. If you define d Sprite Image of 2x2, you'll be able to draw any of the 4 subimages. * This method defines the animation sequence so that it could be set [0,2,1,3,2,1] as the * animation sequence * @param ii {Array} an array of integers. */ setAnimationImageIndex : function( ii ) { if ( this.backgroundImage ) { this.backgroundImage.setAnimationImageIndex(ii); } return this; }, resetAnimationTime : function() { if ( this.backgroundImage ) { this.backgroundImage.resetAnimationTime(); } return this; }, setChangeFPS : function(time) { if ( this.backgroundImage ) { this.backgroundImage.setChangeFPS(time); } return this; }, /** * Set this background image transformation. * If GL is enabled, this parameter has no effect. * @param it any value from CAAT.SpriteImage.TR_* * @return this */ setImageTransformation : function( it ) { if ( this.backgroundImage ) { this.backgroundImage.setSpriteTransformation(it); } return this; }, /** * Center this actor at position (x,y). * @param x {number} x position * @param y {number} y position * * @return this * @deprecated */ centerOn : function( x,y ) { this.setLocation( x-this.width/2, y-this.height/2 ); return this; }, /** * Center this actor at position (x,y). * @param x {number} x position * @param y {number} y position * * @return this */ centerAt : function(x,y) { return this.centerOn(x,y); }, /** * If GL is enables, get this background image's texture page, otherwise it will fail. * @return {CAAT.GLTexturePage} */ getTextureGLPage : function() { return this.backgroundImage.image.__texturePage; }, /** * Set this actor invisible. * The actor is animated but not visible. * A container won't show any of its children if set visible to false. * * @param visible {boolean} set this actor visible or not. * @return this */ setVisible : function(visible) { this.visible= visible; return this; }, /** * Puts an Actor out of time line, that is, won't be transformed nor rendered. * @return this */ setOutOfFrameTime : function() { this.setFrameTime(-1,0); return this; }, /** * Adds an Actor's life cycle listener. * The developer must ensure the actorListener is not already a listener, otherwise * it will notified more than once. * @param actorListener {object} an object with at least a method of the form: * actorLyfeCycleEvent( actor, string_event_type, long_time ) */ addListener : function( actorListener ) { this.lifecycleListenerList.push(actorListener); return this; }, /** * Removes an Actor's life cycle listener. * It will only remove the first occurrence of the given actorListener. * @param actorListener {object} an Actor's life cycle listener. */ removeListener : function( actorListener ) { var n= this.lifecycleListenerList.length; while(n--) { if ( this.lifecycleListenerList[n]===actorListener ) { // remove the nth element. this.lifecycleListenerList.splice(n,1); return; } } }, /** * Set alpha composition scope. global will mean this alpha value will be its children maximum. * If set to false, only this actor will have this alpha value. * @param global {boolean} whether the alpha value should be propagated to children. */ setGlobalAlpha : function( global ) { this.isGlobalAlpha= global; return this; }, /** * Notifies the registered Actor's life cycle listener about some event. * @param sEventType an string indicating the type of event being notified. * @param time an integer indicating the time related to Scene's timeline when the event * is being notified. */ fireEvent : function(sEventType, time) { for( var i=0; i=this.start_time+this.duration ) { if ( !this.expired ) { this.setExpired(time); } return false; } return this.start_time<=time && time=0 && y>=0 && x * WARNING: every call to this method calculates * actor's world model view matrix. * * @param point {CAAT.Point} a point in screen space to be transformed to model space. * * @return the source point object * * */ viewToModel : function(point) { if ( this.dirty ) { this.setModelViewMatrix(); } this.worldModelViewMatrixI= this.worldModelViewMatrix.getInverse(); this.worldModelViewMatrixI.transformCoord(point); return point; }, /** * Private * This method does the needed point transformations across an Actor hierarchy to devise * whether the parameter point coordinate lies inside the Actor. * @param point {CAAT.Point} * * @return null if the point is not inside the Actor. The Actor otherwise. */ findActorAtPosition : function(point) { if ( !this.visible || !this.mouseEnabled || !this.isInAnimationFrame(this.time) ) { return null; } this.modelViewMatrixI= this.modelViewMatrix.getInverse(); this.modelViewMatrixI.transformCoord(point); return this.contains(point.x, point.y) ? this :null; }, /** * Enables a default dragging routine for the Actor. * This default dragging routine allows to: *
  • scale the Actor by pressing shift+drag *
  • rotate the Actor by pressing control+drag *
  • scale non uniformly by pressing alt+shift+drag * * @return this */ enableDrag : function() { var me= this; this.ax= 0; this.ay= 0; this.mx= 0; this.my= 0; this.asx=1; this.asy=1; this.ara=0; this.screenx=0; this.screeny=0; /** * Mouse enter handler for default drag behavior. * @param mouseEvent {CAAT.MouseEvent} * * @ignore */ this.mouseEnter= function(mouseEvent) { this.__d_ax= -1; this.__d_ay= -1; this.pointed= true; CAAT.setCursor('move'); }; /** * Mouse exit handler for default drag behavior. * @param mouseEvent {CAAT.MouseEvent} * * @ignore */ this.mouseExit = function(mouseEvent) { this.__d_ax = -1; this.__d_ay = -1; this.pointed = false; CAAT.setCursor('default'); }; /** * Mouse move handler for default drag behavior. * @param mouseEvent {CAAT.MouseEvent} * * @ignore */ this.mouseMove = function(mouseEvent) { }; /** * Mouse up handler for default drag behavior. * @param mouseEvent {CAAT.MouseEvent} * * @ignore */ this.mouseUp = function(mouseEvent) { this.__d_ax = -1; this.__d_ay = -1; }; /** * Mouse drag handler for default drag behavior. * @param mouseEvent {CAAT.MouseEvent} * * @ignore */ this.mouseDrag = function(mouseEvent) { var pt; pt= this.modelToView( new CAAT.Point(mouseEvent.x, mouseEvent.y ) ); this.parent.viewToModel( pt ); if (this.__d_ax === -1 || this.__d_ay === -1) { this.__d_ax = pt.x; this.__d_ay = pt.y; this.__d_asx = this.scaleX; this.__d_asy = this.scaleY; this.__d_ara = this.rotationAngle; this.__d_screenx = mouseEvent.screenPoint.x; this.__d_screeny = mouseEvent.screenPoint.y; } if (mouseEvent.isShiftDown()) { var scx = (mouseEvent.screenPoint.x - this.__d_screenx) / 100; var scy = (mouseEvent.screenPoint.y - this.__d_screeny) / 100; if (!mouseEvent.isAltDown()) { var sc = Math.max(scx, scy); scx = sc; scy = sc; } this.setScale(scx + this.__d_asx, scy + this.__d_asy); } else if (mouseEvent.isControlDown()) { var vx = mouseEvent.screenPoint.x - this.__d_screenx; var vy = mouseEvent.screenPoint.y - this.__d_screeny; this.setRotation(-Math.atan2(vx, vy) + this.__d_ara); } else { this.x += pt.x-this.__d_ax; this.y += pt.y-this.__d_ay; } this.__d_ax= pt.x; this.__d_ay= pt.y; }; return this; }, /** * Default mouseClick handler. * Mouse click events are received after a call to mouseUp method if no dragging was in progress. * * @param mouseEvent {CAAT.MouseEvent} */ mouseClick : function(mouseEvent) { }, /** * Default double click handler * * @param mouseEvent {CAAT.MouseEvent} */ mouseDblClick : function(mouseEvent) { }, /** * Default mouse enter on Actor handler. * @param mouseEvent {CAAT.MouseEvent} */ mouseEnter : function(mouseEvent) { this.pointed= true; }, /** * Default mouse exit on Actor handler. * * @param mouseEvent {CAAT.MouseEvent} */ mouseExit : function(mouseEvent) { this.pointed= false; }, /** * Default mouse move inside Actor handler. * * @param mouseEvent {CAAT.MouseEvent} */ mouseMove : function(mouseEvent) { }, /** * default mouse press in Actor handler. * * @param mouseEvent {CAAT.MouseEvent} */ mouseDown : function(mouseEvent) { }, /** * default mouse release in Actor handler. * * @param mouseEvent {CAAT.MouseEvent} */ mouseUp : function(mouseEvent) { }, mouseOut : function(mouseEvent) { }, mouseOver : function(mouseEvent) { }, /** * default Actor mouse drag handler. * * @param mouseEvent {CAAT.MouseEvent} */ mouseDrag : function(mouseEvent) { }, /** * Draw a bounding box with on-screen coordinates regardless of the transformations * applied to the Actor. * * @param director {CAAT.Director} object instance that contains the Scene the Actor is in. * @param time {number} integer indicating the Scene time when the bounding box is to be drawn. */ drawScreenBoundingBox : function( director, time ) { if ( null!==this.AABB && this.inFrame ) { var s= this.AABB; var ctx= director.ctx; ctx.strokeStyle= CAAT.DEBUGAABBCOLOR; ctx.strokeRect( .5+(s.x|0), .5+(s.y|0), s.width|0, s.height|0 ); if ( CAAT.DEBUGBB ) { var vv= this.viewVertices; ctx.beginPath( ); ctx.lineTo( vv[0].x, vv[0].y ); ctx.lineTo( vv[1].x, vv[1].y ); ctx.lineTo( vv[2].x, vv[2].y ); ctx.lineTo( vv[3].x, vv[3].y ); ctx.closePath(); ctx.strokeStyle= CAAT.DEBUGBBCOLOR; ctx.stroke(); } } }, /** * Private * This method is called by the Director instance. * It applies the list of behaviors the Actor has registered. * * @param director the CAAT.Director object instance that contains the Scene the Actor is in. * @param time an integer indicating the Scene time when the bounding box is to be drawn. */ animate : function(director, time) { var i; if ( !this.isInAnimationFrame(time) ) { this.inFrame= false; this.dirty= true; return false; } if ( this.x!==this.oldX || this.y!==this.oldY ) { this.dirty= true; this.oldX= this.x; this.oldY= this.y; } for( i=0; i xmax ) { xmax=vvv.x; } if ( vvv.y < ymin ) { ymin=vvv.y; } if ( vvv.y > ymax ) { ymax=vvv.y; } var vvv= vv[1]; if ( vvv.x < xmin ) { xmin=vvv.x; } if ( vvv.x > xmax ) { xmax=vvv.x; } if ( vvv.y < ymin ) { ymin=vvv.y; } if ( vvv.y > ymax ) { ymax=vvv.y; } var vvv= vv[2]; if ( vvv.x < xmin ) { xmin=vvv.x; } if ( vvv.x > xmax ) { xmax=vvv.x; } if ( vvv.y < ymin ) { ymin=vvv.y; } if ( vvv.y > ymax ) { ymax=vvv.y; } var vvv= vv[3]; if ( vvv.x < xmin ) { xmin=vvv.x; } if ( vvv.x > xmax ) { xmax=vvv.x; } if ( vvv.y < ymin ) { ymin=vvv.y; } if ( vvv.y > ymax ) { ymax=vvv.y; } AABB.x= xmin; AABB.y= ymin; AABB.x1= xmax; AABB.y1= ymax; AABB.width= (xmax-xmin); AABB.height= (ymax-ymin); return this; }, /** * @private. * This method will be called by the Director to set the whole Actor pre-render process. * * @param director the CAAT.Director object instance that contains the Scene the Actor is in. * @param time an integer indicating the Scene time when the bounding box is to be drawn. * * @return boolean indicating whether the Actor isInFrameTime */ paintActor : function(director, time) { if (!this.visible) { return true; } var ctx= director.ctx; this.frameAlpha= this.parent ? this.parent.frameAlpha*this.alpha : 1; ctx.globalAlpha= this.frameAlpha; director.modelViewMatrix.transformRenderingContextSet( ctx ); this.worldModelViewMatrix.transformRenderingContext(ctx); if ( this.clip ) { ctx.beginPath(); if (!this.clipPath ) { ctx.rect(0,0,this.width,this.height); } else { this.clipPath.applyAsPath(director); } ctx.clip(); } this.paint(director, time); return true; }, /** * for js2native * @param director * @param time */ __paintActor : function(director, time) { if (!this.visible) { return true; } var ctx= director.ctx; // global opt: set alpha as owns alpha, not take globalAlpha procedure. this.frameAlpha= this.alpha; var m= this.worldModelViewMatrix.matrix; ctx.setTransform( m[0], m[3], m[1], m[4], m[2], m[5], this.frameAlpha ); this.paint(director, time); return true; }, /** * Set coordinates and uv values for this actor. * This function uses Director's coords and indexCoords values. * @param director * @param time */ paintActorGL : function(director,time) { this.frameAlpha= this.parent.frameAlpha*this.alpha; if ( !this.glEnabled || !this.visible) { return; } if ( this.glNeedsFlush(director) ) { director.glFlush(); this.glSetShader(director); if ( !this.__uv ) { this.__uv= new Float32Array(8); } if ( !this.__vv ) { this.__vv= new Float32Array(12); } this.setGLCoords( this.__vv, 0 ); this.setUV( this.__uv, 0 ); director.glRender(this.__vv, 12, this.__uv); return; } var glCoords= director.coords; var glCoordsIndex= director.coordsIndex; ////////////////// XYZ this.setGLCoords(glCoords, glCoordsIndex); director.coordsIndex= glCoordsIndex+12; ////////////////// UV this.setUV( director.uv, director.uvIndex ); director.uvIndex+= 8; }, /** * TODO: set GLcoords for different image transformations. * @param glCoords * @param glCoordsIndex * @param z */ setGLCoords : function( glCoords, glCoordsIndex ) { var vv= this.viewVertices; glCoords[glCoordsIndex++]= vv[0].x; glCoords[glCoordsIndex++]= vv[0].y; glCoords[glCoordsIndex++]= 0; glCoords[glCoordsIndex++]= vv[1].x; glCoords[glCoordsIndex++]= vv[1].y; glCoords[glCoordsIndex++]= 0; glCoords[glCoordsIndex++]= vv[2].x; glCoords[glCoordsIndex++]= vv[2].y; glCoords[glCoordsIndex++]= 0; glCoords[glCoordsIndex++]= vv[3].x; glCoords[glCoordsIndex++]= vv[3].y; glCoords[glCoordsIndex++]= 0; }, /** * Set UV for this actor's quad. * * @param uvBuffer {Float32Array} * @param uvIndex {number} */ setUV : function( uvBuffer, uvIndex ) { this.backgroundImage.setUV(uvBuffer, uvIndex); }, /** * Test for compulsory gl flushing: * 1.- opacity has changed. * 2.- texture page has changed. * */ glNeedsFlush : function(director) { if ( this.getTextureGLPage()!==director.currentTexturePage ) { return true; } if ( this.frameAlpha!==director.currentOpacity ) { return true; } return false; }, /** * Change texture shader program parameters. * @param director */ glSetShader : function(director) { var tp= this.getTextureGLPage(); if ( tp!==director.currentTexturePage ) { director.setGLTexturePage(tp); } if ( this.frameAlpha!==director.currentOpacity ) { director.setGLCurrentOpacity(this.frameAlpha); } }, /** * @private. * This method is called after the Director has transformed and drawn a whole frame. * * @param director the CAAT.Director object instance that contains the Scene the Actor is in. * @param time an integer indicating the Scene time when the bounding box is to be drawn. * @return this * * @deprecated */ endAnimate : function(director,time) { return this; }, initialize : function(overrides) { if (overrides) { for (var i in overrides) { this[i] = overrides[i]; } } return this; }, /** * Set this Actor's clipping area. * @param enable {boolean} enable clip area. * @param clipPath {CAAT.Path=} An optional path to apply clip with. If enabled and clipPath is not set, * a rectangle will be used. */ setClip : function( enable, clipPath ) { this.clip= enable; this.clipPath= clipPath; return this; }, /** * * @param time {Number=} * @param stragegy {CAAT.Actor.CACHE_SIMPLE | CAAT.Actor.CACHE_DEEP} * @return this */ cacheAsBitmap : function(time, strategy) { time= time||0; var canvas= document.createElement('canvas'); canvas.width= this.width; canvas.height= this.height; var ctx= canvas.getContext('2d'); var director= { ctx: ctx, crc: ctx, modelViewMatrix: new CAAT.Matrix() }; this.cached= false; this.paintActor(director,time); this.setBackgroundImage(canvas); this.cached= strategy ? strategy : CAAT.Actor.CACHE_SIMPLE; return this; }, /** * Set this actor behavior as if it were a Button. The actor size will be set as SpriteImage's * single size. * * @param buttonImage {CAAT.SpriteImage} sprite image with button's state images. * @param _iNormal {number} button's normal state image index * @param _iOver {number} button's mouse over state image index * @param _iPress {number} button's pressed state image index * @param _iDisabled {number} button's disabled state image index * @param fn {function(button{CAAT.Actor})} callback function */ setAsButton : function( buttonImage, iNormal, iOver, iPress, iDisabled, fn ) { var me= this; this.setBackgroundImage(buttonImage, true); this.iNormal= iNormal || 0; this.iOver= iOver || iNormal; this.iPress= iPress || iNormal; this.iDisabled= iDisabled || iNormal; this.fnOnClick= fn; this.enabled= true; this.setSpriteIndex( iNormal ); /** * Enable or disable the button. * @param enabled {boolean} * @ignore */ this.setEnabled= function( enabled ) { this.enabled= enabled; this.setSpriteIndex( this.enabled ? this.iNormal : this.iDisabled ); return this; }; /** * This method will be called by CAAT *before* the mouseUp event is fired. * @param event {CAAT.MouseEvent} * @ignore */ this.actionPerformed= function(event) { if ( this.enabled && null!==this.fnOnClick ) { this.fnOnClick(this); } }; /** * Button's mouse enter handler. It makes the button provide visual feedback * @param mouseEvent {CAAT.MouseEvent} * @ignore */ this.mouseEnter= function(mouseEvent) { if ( !this.enabled ) { return; } if ( this.dragging ) { this.setSpriteIndex( this.iPress ); } else { this.setSpriteIndex( this.iOver ); } CAAT.setCursor('pointer'); }; /** * Button's mouse exit handler. Release visual apperance. * @param mouseEvent {CAAT.MouseEvent} * @ignore */ this.mouseExit= function(mouseEvent) { if ( !this.enabled ) { return; } this.setSpriteIndex( this.iNormal ); CAAT.setCursor('default'); }; /** * Button's mouse down handler. * @param mouseEvent {CAAT.MouseEvent} * @ignore */ this.mouseDown= function(mouseEvent) { if ( !this.enabled ) { return; } this.setSpriteIndex( this.iPress ); }; /** * Button's mouse up handler. * @param mouseEvent {CAAT.MouseEvent} * @ignore */ this.mouseUp= function(mouseEvent) { if ( !this.enabled ) { return; } this.setSpriteIndex( this.iNormal ); this.dragging= false; }; /** * Button's mouse click handler. Do nothing by default. This event handler will be * called ONLY if it has not been drag on the button. * @param mouseEvent {CAAT.MouseEvent} * @ignore */ this.mouseClick= function(mouseEvent) { }; /** * Button's mouse drag handler. * @param mouseEvent {CAAT.MouseEvent} * @ignore */ this.mouseDrag= function(mouseEvent) { if ( !this.enabled ) { return; } this.dragging= true; }; this.setButtonImageIndex= function(_normal, _over, _press, _disabled ) { this.iNormal= _normal; this.iOver= _over; this.iPress= _press; this.iDisabled= _disabled; this.setSpriteIndex( this.iNormal ); return this; }; return this; } }; /* if ( CAAT.NO_PERF ) { CAAT.Actor.prototype.paintActor= CAAT.Actor.prototype.__paintActor; } */ })(); (function() { var __CD= 2; /** * This class is a general container of CAAT.Actor instances. It extends the concept of an Actor * from a single entity on screen to a set of entities with a parent/children relationship among * them. *

    * This mainly overrides default behavior of a single entity and exposes methods to manage its children * collection. * * @constructor * @extends CAAT.Actor */ CAAT.ActorContainer= function(hint) { CAAT.ActorContainer.superclass.constructor.call(this); this.childrenList= []; this.pendingChildrenList= []; if ( typeof hint!=='undefined' ) { this.addHint= hint; this.boundingBox= new CAAT.Rectangle(); } return this; }; CAAT.ActorContainer.AddHint= { CONFORM : 1 }; CAAT.ActorContainer.prototype= { childrenList : null, // the list of children contained. activeChildren : null, pendingChildrenList : null, addHint : 0, boundingBox : null, runion : new CAAT.Rectangle(), // Watch out. one for every container. /** * Draws this ActorContainer and all of its children screen bounding box. * * @param director the CAAT.Director object instance that contains the Scene the Actor is in. * @param time an integer indicating the Scene time when the bounding box is to be drawn. */ drawScreenBoundingBox : function( director, time ) { if (!this.inFrame) { return; } var cl= this.childrenList; for( var i=0; i=this.childrenList.length ) { index= this.childrenList.length; } } child.parent= this; child.dirty= true; this.childrenList.splice(index, 0, child); return this; }, /** * Find the first actor with the supplied ID. * This method is not recommended to be used since executes a linear search. * @param id */ findActorById : function(id) { var cl= this.childrenList; for( var i=0, l=cl.length; i=0; i-- ) { var child= this.childrenList[i]; var np= new CAAT.Point( point.x, point.y, 0 ); var contained= child.findActorAtPosition( np ); if ( null!==contained ) { return contained; } } return this; }, /** * Destroys this ActorContainer. * The process falls down recursively for each contained Actor into this ActorContainer. * * @return this */ destroy : function() { var cl= this.childrenList; for( var i=cl.length-1; i>=0; i-- ) { cl[i].destroy(); } CAAT.ActorContainer.superclass.destroy.call(this); return this; }, /** * Get number of Actors into this container. * @return integer indicating the number of children. */ getNumChildren : function() { return this.childrenList.length; }, getNumActiveChildren : function() { return this.activeChildren.length; }, /** * Returns the Actor at the iPosition(th) position. * @param iPosition an integer indicating the position array. * @return the CAAT.Actor object at position. */ getChildAt : function( iPosition ) { return this.childrenList[ iPosition ]; }, /** * Changes an actor's ZOrder. * @param actor the actor to change ZOrder for * @param index an integer indicating the new ZOrder. a value greater than children list size means to be the * last ZOrder Actor. */ setZOrder : function( actor, index ) { var actorPos= this.findChild(actor); // the actor is present if ( -1!==actorPos ) { var cl= this.childrenList; // trivial reject. if ( index===actorPos ) { return; } if ( index>=cl.length ) { cl.splice(actorPos,1); cl.push(actor); } else { var nActor= cl.splice(actorPos,1); if ( index<0 ) { index=0; } else if ( index>cl.length ) { index= cl.length; } cl.splice( index, 0, nActor[0] ); } } } }; /* if ( CAAT.NO_PERF ) { CAAT.ActorContainer.prototype.paintActor= CAAT.ActorContainer.prototype.__paintActor; } */ extend( CAAT.ActorContainer, CAAT.Actor, null); })(); (function() { /** * TextActor draws text on screen. The text can be drawn directly on screen or make if follow a * path defined by an instance of CAAT.Path. * * @constructor * @extends CAAT.ActorContainer * */ CAAT.TextActor = function() { CAAT.TextActor.superclass.constructor.call(this); this.font= "10px sans-serif"; this.textAlign= "left"; this.textBaseline= "top"; this.outlineColor= "black"; this.clip= false; return this; }; CAAT.TextActor.TRAVERSE_PATH_FORWARD= 1; CAAT.TextActor.TRAVERSE_PATH_BACKWARD= -1; CAAT.TextActor.prototype= { font: null, // a valid canvas rendering context font description. Default font // will be "10px sans-serif". textAlign: null, // a valid canvas rendering context textAlign string. Any of: // start, end, left, right, center. // defaults to "left". textBaseline: null, // a valid canvas rendering context textBaseLine string. Any of: // top, hanging, middle, alphabetic, ideographic, bottom. // defaults to "top". fill: true, // a boolean indicating whether the text should be filled. textFillStyle : '#eee', // text fill color text: null, // a string with the text to draw. textWidth: 0, // an integer indicating text width in pixels. textHeight: 0, // an integer indicating text height in pixels. outline: false, // a boolean indicating whether the text should be outlined. // not all browsers support it. outlineColor: null, // a valid color description string. path: null, // a CAAT.Path which will be traversed by the text. [Optional] pathInterpolator: null, // a CAAT.Interpolator to apply to the path traversing. pathDuration: 10000, // an integer indicating the time to be taken to traverse the path. ms. sign: 1, // traverse the path forward or backwards. /** * Set the text to be filled. The default Filling style will be set by calling setFillStyle method. * Default value is true. * @param fill {boolean} a boolean indicating whether the text will be filled. * @return this; */ setFill : function( fill ) { this.fill= fill; return this; }, setTextFillStyle : function( style ) { this.textFillStyle= style; return this; }, /** * Sets whether the text will be outlined. * @param outline {boolean} a boolean indicating whether the text will be outlined. * @return this; */ setOutline : function( outline ) { this.outline= outline; return this; }, setPathTraverseDirection : function(direction) { this.sign= direction; return this; }, /** * Defines text's outline color. * * @param color {string} sets a valid canvas context color. * @return this. */ setOutlineColor : function( color ) { this.outlineColor= color; return this; }, /** * Set the text to be shown by the actor. * @param sText a string with the text to be shwon. * @return this */ setText : function( sText ) { this.text= sText; if ( null===this.text || this.text==="" ) { this.width= this.height= 0; } this.calcTextSize( CAAT.director[0] ); return this; }, setTextAlign : function( align ) { this.textAlign= align; return this; }, /** * Sets text alignment * @param align * @deprecated use setTextAlign */ setAlign : function( align ) { return this.setTextAlign(align); }, /** * Set text baseline. * @param baseline */ setTextBaseline : function( baseline ) { this.textBaseline= baseline; return this; }, setBaseline : function( baseline ) { return this.setTextBaseline(baseline); }, /** * Sets the font to be applied for the text. * @param font a string with a valid canvas rendering context font description. * @return this */ setFont : function(font) { if ( !font ) { font= "10px sans-serif"; } this.font= font; this.calcTextSize( CAAT.director[0] ); return this; }, /** * Calculates the text dimension in pixels and stores the values in textWidth and textHeight * attributes. * If Actor's width and height were not set, the Actor's dimension will be set to these values. * @param director a CAAT.Director instance. * @return this */ calcTextSize : function(director) { if ( typeof this.text==='undefined' || null===this.text || ""===this.text ) { this.textWidth= 0; this.textHeight= 0; return this; } if ( director.glEnabled ) { return this; } if ( this.font instanceof CAAT.SpriteImage ) { this.textWidth= this.font.stringWidth( this.text ); this.textHeight=this.font.stringHeight(); this.width= this.textWidth; this.height= this.textHeight; return this; } var ctx= director.ctx; ctx.save(); ctx.font= this.font; this.textWidth= ctx.measureText( this.text ).width; if (this.width===0) { this.width= this.textWidth; } try { var pos= this.font.indexOf("px"); var s = this.font.substring(0, pos ); this.textHeight= parseInt(s,10); // needed to calculate the descent. // no context.getDescent(font) WTF !!! this.textHeight+= (this.textHeight/4)>>0; } catch(e) { this.textHeight=20; // default height; } if ( this.height===0 ) { this.height= this.textHeight; } ctx.restore(); return this; }, /** * Custom paint method for TextActor instances. * If the path attribute is set, the text will be drawn traversing the path. * * @param director a valid CAAT.Director instance. * @param time an integer with the Scene time the Actor is being drawn. */ paint : function(director, time) { CAAT.TextActor.superclass.paint.call(this, director, time ); if ( this.cached ) { // cacheAsBitmap sets this actor's background image as a representation of itself. // So if after drawing the background it was cached, we're done. return; } if ( null===this.text) { return; } if ( this.textWidth===0 || this.textHeight===0 ) { this.calcTextSize(director); } var ctx= director.ctx; if ( this.font instanceof CAAT.SpriteImage ) { return this.drawSpriteText(director,time); } if( null!==this.font ) { ctx.font= this.font; } if ( null!==this.textAlign ) { ctx.textAlign= this.textAlign; } if ( null!==this.textBaseline ) { ctx.textBaseline= this.textBaseline; } if ( this.fill && null!==this.textFillStyle ) { ctx.fillStyle= this.textFillStyle; } if ( this.outline && null!==this.outlineColor ) { ctx.strokeStyle= this.outlineColor; } if (null===this.path) { var tx=0; if ( this.textAlign==='center') { tx= (this.width/2)|0; } else if ( this.textAlign==='right' ) { tx= this.width; } if ( this.fill ) { ctx.fillText( this.text, tx, 0 ); if ( this.outline ) { // firefox necesita beginPath, si no, dibujara ademas el cuadrado del // contenedor de los textos. // if ( null!==this.outlineColor ) { // ctx.strokeStyle= this.outlineColor; // } ctx.beginPath(); ctx.strokeText( this.text, tx, 0 ); } } else { if ( null!==this.outlineColor ) { ctx.strokeStyle= this.outlineColor; } ctx.beginPath(); ctx.strokeText( this.text, tx, 0 ); } } else { this.drawOnPath(director,time); } }, /** * Private. * Draw the text traversing a path. * @param director a valid CAAT.Director instance. * @param time an integer with the Scene time the Actor is being drawn. */ drawOnPath : function(director, time) { var ctx= director.ctx; var textWidth=this.sign * this.pathInterpolator.getPosition( (time%this.pathDuration)/this.pathDuration ).y * this.path.getLength() ; var p0= new CAAT.Point(0,0,0); var p1= new CAAT.Point(0,0,0); for( var i=0; i>0, p0.y>>0 ); ctx.rotate( angle ); if ( this.fill ) { ctx.fillText(caracter,0,0); } if ( this.outline ) { // ctx.strokeStyle= this.outlineColor; ctx.strokeText(caracter,0,0); } ctx.restore(); textWidth+= charWidth; } }, /** * Private. * Draw the text using a sprited font instead of a canvas font. * @param director a valid CAAT.Director instance. * @param time an integer with the Scene time the Actor is being drawn. */ drawSpriteText: function(director, time) { if (null===this.path) { this.font.drawString( director.ctx, this.text, 0, 0); } else { this.drawSpriteTextOnPath(director, time); } }, /** * Private. * Draw the text traversing a path using a sprited font. * @param director a valid CAAT.Director instance. * @param time an integer with the Scene time the Actor is being drawn. */ drawSpriteTextOnPath: function(director, time) { var context= director.ctx; var textWidth=this.sign * this.pathInterpolator.getPosition( (time%this.pathDuration)/this.pathDuration ).y * this.path.getLength() ; var p0= new CAAT.Point(0,0,0); var p1= new CAAT.Point(0,0,0); for( var i=0; i0} */ setLineWidth : function(l) { this.lineWidth= l; return this; }, /** * * @param lc {string{butt|round|square}} */ setLineCap : function(lc) { this.lineCap= lc; return this; }, /** * * @param lj {string{bevel|round|miter}} */ setLineJoin : function(lj) { this.lineJoin= lj; return this; }, /** * * @param ml {integer>0} */ setMiterLimit : function(ml) { this.miterLimit= ml; return this; }, getLineCap : function() { return this.lineCap; }, getLineJoin : function() { return this.lineJoin; }, getMiterLimit : function() { return this.miterLimit; }, getLineWidth : function() { return this.lineWidth; }, /** * Sets shape type. * No check for parameter validity is performed. * Set paint method according to the shape. * @param iShape an integer with any of the SHAPE_* constants. * @return this */ setShape : function(iShape) { this.shape= iShape; this.paint= this.shape===this.SHAPE_CIRCLE ? this.paintCircle : this.paintRectangle; return this; }, /** * Sets the composite operation to apply on shape drawing. * @param compositeOp an string with a valid canvas rendering context string describing compositeOps. * @return this */ setCompositeOp : function(compositeOp){ this.compositeOp= compositeOp; return this; }, /** * Draws the shape. * Applies the values of fillStype, strokeStyle, compositeOp, etc. * * @param director a valid CAAT.Director instance. * @param time an integer with the Scene time the Actor is being drawn. */ paint : function(director,time) { }, /** * @private * Draws a circle. * @param director a valid CAAT.Director instance. * @param time an integer with the Scene time the Actor is being drawn. */ paintCircle : function(director,time) { var ctx= director.crc; ctx.lineWidth= this.lineWidth; ctx.globalCompositeOperation= this.compositeOp; if ( null!==this.fillStyle ) { ctx.fillStyle= this.fillStyle; ctx.beginPath(); ctx.arc( this.width/2, this.height/2, Math.min(this.width,this.height)/2, 0, 2*Math.PI, false ); ctx.fill(); } if ( null!==this.strokeStyle ) { ctx.strokeStyle= this.strokeStyle; ctx.beginPath(); ctx.arc( this.width/2, this.height/2, Math.min(this.width,this.height)/2, 0, 2*Math.PI, false ); ctx.stroke(); } }, /** * * Private * Draws a Rectangle. * * @param director a valid CAAT.Director instance. * @param time an integer with the Scene time the Actor is being drawn. */ paintRectangle : function(director,time) { var ctx= director.crc; ctx.lineWidth= this.lineWidth; if ( this.lineCap ) { ctx.lineCap= this.lineCap; } if ( this.lineJoin ) { ctx.lineJoin= this.lineJoin; } if ( this.miterLimit ) { ctx.miterLimit= this.miterLimit; } ctx.globalCompositeOperation= this.compositeOp; if ( null!==this.fillStyle ) { ctx.fillStyle= this.fillStyle; ctx.beginPath(); ctx.fillRect(0,0,this.width,this.height); ctx.fill(); } if ( null!==this.strokeStyle ) { ctx.strokeStyle= this.strokeStyle; ctx.beginPath(); ctx.strokeRect(0,0,this.width,this.height); ctx.stroke(); } } }; extend( CAAT.ShapeActor, CAAT.ActorContainer, null); })(); (function() { /** * This actor draws stars. * * @constructor * @extends CAAT.ActorContainer */ CAAT.StarActor= function() { CAAT.StarActor.superclass.constructor.call(this); this.compositeOp= 'source-over'; return this; }; CAAT.StarActor.prototype= { nPeaks: 0, maxRadius: 0, minRadius: 0, initialAngle: 0, compositeOp: null, lineWidth: 1, lineCap: null, lineJoin: null, miterLimit: null, /** * * @param l {number>0} */ setLineWidth : function(l) { this.lineWidth= l; return this; }, /** * * @param lc {string{butt|round|square}} */ setLineCap : function(lc) { this.lineCap= lc; return this; }, /** * * @param lj {string{bevel|round|miter}} */ setLineJoin : function(lj) { this.lineJoin= lj; return this; }, /** * * @param ml {integer>0} */ setMiterLimit : function(ml) { this.miterLimit= ml; return this; }, getLineCap : function() { return this.lineCap; }, getLineJoin : function() { return this.lineJoin; }, getMiterLimit : function() { return this.miterLimit; }, getLineWidth : function() { return this.lineWidth; }, /** * Sets whether the star will be color filled. * @param filled {boolean} * @deprecated */ setFilled : function( filled ) { return this; }, /** * Sets whether the star will be outlined. * @param outlined {boolean} * @deprecated */ setOutlined : function( outlined ) { return this; }, /** * Sets the composite operation to apply on shape drawing. * @param compositeOp an string with a valid canvas rendering context string describing compositeOps. * @return this */ setCompositeOp : function(compositeOp){ this.compositeOp= compositeOp; return this; }, /** * * @param angle {number} number in radians. */ setInitialAngle : function(angle) { this.initialAngle= angle; return this; }, /** * Initialize the star values. *

    * The star actor will be of size 2*maxRadius. * * @param nPeaks {number} number of star points. * @param maxRadius {number} maximum star radius * @param minRadius {number} minimum star radius * * @return this */ initialize : function(nPeaks, maxRadius, minRadius) { this.setSize( 2*maxRadius, 2*maxRadius ); this.nPeaks= nPeaks; this.maxRadius= maxRadius; this.minRadius= minRadius; return this; }, /** * Paint the star. * * @param director {CAAT.Director} * @param timer {number} */ paint : function(director, timer) { var ctx= director.ctx; var centerX= this.width/2; var centerY= this.height/2; var r1= this.maxRadius; var r2= this.minRadius; var ix= centerX + r1*Math.cos(this.initialAngle); var iy= centerY + r1*Math.sin(this.initialAngle); ctx.lineWidth= this.lineWidth; if ( this.lineCap ) { ctx.lineCap= this.lineCap; } if ( this.lineJoin ) { ctx.lineJoin= this.lineJoin; } if ( this.miterLimit ) { ctx.miterLimit= this.miterLimit; } ctx.globalCompositeOperation= this.compositeOp; ctx.beginPath(); ctx.moveTo(ix,iy); for( var i=1; ithis.changeTime ) { this.imageProcessor.apply( director, time ); this.lastApplicationTime= time; } var ctx= director.ctx; this.imageProcessor.paint( director, time ); } }; extend( CAAT.IMActor, CAAT.ActorContainer, null); })();/** * See LICENSE file. * * Sound implementation. */ (function() { /** * This class is a sound manager implementation which can play at least 'numChannels' sounds at the same time. * By default, CAAT.Director instances will set eight channels to play sound. *

    * If more than 'numChannels' sounds want to be played at the same time the requests will be dropped, * so no more than 'numChannels' sounds can be concurrently played. *

    * Available sounds to be played must be supplied to every CAAT.Director instance by calling addSound * method. The default implementation will accept a URL/URI or a HTMLAudioElement as source. *

    * The cached elements can be played, or looped. The loop method will return a handler to * give the opportunity of cancelling the sound. *

    * Be aware of Audio.canPlay, is able to return 'yes', 'no', 'maybe', ..., so anything different from * '' and 'no' will do. * * @constructor * */ CAAT.AudioManager= function() { this.browserInfo= new CAAT.BrowserDetect(); return this; }; CAAT.AudioManager.prototype= { browserInfo: null, musicEnabled: true, fxEnabled: true, audioCache: null, // audio elements. channels: null, // available playing channels. workingChannels: null, // currently playing channels. loopingChannels: [], audioTypes: { // supported audio formats. Don't remember where i took them from :S 'mp3': 'audio/mpeg;', 'ogg': 'audio/ogg; codecs="vorbis"', 'wav': 'audio/wav; codecs="1"', 'mp4': 'audio/mp4; codecs="mp4a.40.2"' }, /** * Initializes the sound subsystem by creating a fixed number of Audio channels. * Every channel registers a handler for sound playing finalization. If a callback is set, the * callback function will be called with the associated sound id in the cache. * * @param numChannels {number} number of channels to pre-create. 8 by default. * * @return this. */ initialize : function(numChannels) { this.audioCache= []; this.channels= []; this.workingChannels= []; for( var i=0; i * The audio element can be one of the two forms: * *

      *
    1. Either an HTMLAudioElement/Audio object or a string url. *
    2. An array of elements of the previous form. *
    * *

    * When the audio attribute is an array, this function will iterate throught the array elements * until a suitable audio element to be played is found. When this is the case, the other array * elements won't be taken into account. The valid form of using this addAudio method will be: * *

    * 1.
    * addAudio( id, url } ). In this case, if the resource pointed by url is * not suitable to be played (i.e. a call to the Audio element's canPlayType method return 'no') * no resource will be added under such id, so no sound will be played when invoking the play(id) * method. *

    * 2.
    * addAudio( id, dom_audio_tag ). In this case, the same logic than previous case is applied, but * this time, the parameter url is expected to be an audio tag present in the html file. *

    * 3.
    * addAudio( id, [array_of_url_or_domaudiotag] ). In this case, the function tries to locate a valid * resource to be played in any of the elements contained in the array. The array element's can * be any type of case 1 and 2. As soon as a valid resource is found, it will be associated to the * id in the valid audio resources to be played list. * * @return this */ addAudio : function( id, array_of_url_or_domnodes, endplaying_callback ) { if ( array_of_url_or_domnodes instanceof Array ) { /* iterate throught array elements until we can safely add an audio element. */ for( var i=0; i0 ) { var channel= this.channels.shift(); channel.src= audio.src; channel.load(); channel.volume= audio.volume; channel.play(); this.workingChannels.push(channel); } return this; }, /** * This method creates a new AudioChannel to loop the sound with. * It returns an Audio object so that the developer can cancel the sound loop at will. * The user must call pause() method to stop playing a loop. *

    * Firefox does not honor the loop property, so looping is performed by attending end playing * event on audio elements. * * @return {HTMLElement} an Audio instance if a valid sound id is supplied. Null otherwise */ loop : function( id ) { if (!this.musicEnabled) { return this; } var audio_in_cache= this.getAudio(id); // existe el audio, y ademas hay un canal de audio disponible. if ( null!==audio_in_cache ) { var audio= document.createElement('audio'); if ( null!==audio ) { audio.src= audio_in_cache.src; audio.preload = "auto"; if ( this.browserInfo.browser==='Firefox') { audio.addEventListener( 'ended', // on sound end, set channel to available channels list. function(audioEvent) { var target= audioEvent.target; target.currentTime=0; }, false ); } else { audio.loop= true; } audio.load(); audio.play(); this.loopingChannels.push(audio); return audio; } } return null; }, /** * Cancel all playing audio channels * Get back the playing channels to available channel list. * * @return this */ endSound : function() { var i; for( i=0; i *

  • CAAT.Dock.prototype.OP_LAYOUT_BOTTOM *
  • CAAT.Dock.prototype.OP_LAYOUT_TOP *
  • CAAT.Dock.prototype.OP_LAYOUT_BOTTOM *
  • CAAT.Dock.prototype.OP_LAYOUT_RIGHT * * By default, the layou operation is OP_LAYOUT_BOTTOM, that is, elements zoom bottom anchored. * * @param lo {number} one of CAAT.Dock.OP_LAYOUT_BOTTOM, CAAT.Dock.OP_LAYOUT_TOP, * CAAT.Dock.OP_LAYOUT_BOTTOM, CAAT.Dock.OP_LAYOUT_RIGHT. * * @return this */ setLayoutOp : function( lo ) { this.layoutOp= lo; return this; }, /** * * Set maximum and minimum size of docked elements. By default, every contained actor will be * of 'min' size, and will be scaled up to 'max' size. * * @param min {number} * @param max {number} * @return this */ setSizes : function( min, max ) { this.minSize= min; this.maxSize= max; for( var i=0; isetLayoutOp. * * @private */ layout : function() { var i,actor; if ( this.layoutOp===this.OP_LAYOUT_BOTTOM || this.layoutOp===this.OP_LAYOUT_TOP ) { var currentWidth=0, currentX=0; for( i=0; i index + this.range) { wwidth = this.minSize; } else if (i === index) { wwidth = this.maxSize; } else if (i < index) { wwidth= this.minSize + (this.maxSize-this.minSize) * (Math.cos((i - index - across + 1) / this.range * Math.PI) + 1) / 2; } else { wwidth= this.minSize + (this.maxSize-this.minSize)* (Math.cos( (i - index - across) / this.range * Math.PI) + 1) / 2; } actor.height= wwidth; actor.width= wwidth; } this.layout(); }, /** * Perform the process of exiting the docking element, that is, animate elements to the minimum * size. * * @param mouseEvent {CAAT.MouseEvent} a CAAT.MouseEvent object. * * @private */ actorMouseExit : function(mouseEvent) { if ( null!==this.ttask ) { this.ttask.cancel(); } var me= this; this.ttask= this.scene.createTimer( this.scene.time, 100, function timeout(sceneTime, time, timerTask) { me.actorNotPointed(); }, null, null); }, /** * Perform the beginning of docking elements. * @param mouseEvent {CAAT.MouseEvent} a CAAT.MouseEvent object. * * @private */ actorMouseEnter : function(mouseEvent) { if ( null!==this.ttask ) { this.ttask.cancel(); this.ttask= null; } }, /** * Adds an actor to Dock. *

    * Be aware that actor mouse functions must be set prior to calling this method. The Dock actor * needs set his own actor input events functions for mouseEnter, mouseExit and mouseMove and * will then chain to the original methods set by the developer. * * @param actor {CAAT.Actor} a CAAT.Actor instance. * * @return this */ addChild : function(actor) { var me= this; actor.__Dock_mouseEnter= actor.mouseEnter; actor.__Dock_mouseExit= actor.mouseExit; actor.__Dock_mouseMove= actor.mouseMove; /** * @ignore * @param mouseEvent */ actor.mouseEnter= function(mouseEvent) { me.actorMouseEnter(mouseEvent); this.__Dock_mouseEnter(mouseEvent); }; /** * @ignore * @param mouseEvent */ actor.mouseExit= function(mouseEvent) { me.actorMouseExit(mouseEvent); this.__Dock_mouseExit(mouseEvent); }; /** * @ignore * @param mouseEvent */ actor.mouseMove= function(mouseEvent) { me.actorPointed( mouseEvent.point.x, mouseEvent.point.y, mouseEvent.source ); this.__Dock_mouseMove(mouseEvent); }; actor.width= this.minSize; actor.height= this.minSize; return CAAT.Dock.superclass.addChild.call(this,actor); } }; extend( CAAT.Dock, CAAT.ActorContainer, null); })(); /** * See LICENSE file. * **/ (function() { /** * Director is the animator scene graph manager. *

    * The director elements is an ActorContainer itself with the main responsibility of managing * different Scenes. *

    * It is responsible for: *

      *
    • scene changes. *
    • route input to the appropriate scene graph actor. *
    • be the central point for resource caching. *
    • manage the timeline. *
    • manage frame rate. *
    • etc. *
    * *

    * One document can contain different CAAT.Director instances which will be kept together in CAAT * function. * * @constructor * @extends CAAT.ActorContainer */ CAAT.Director = function() { CAAT.Director.superclass.constructor.call(this); this.browserInfo = new CAAT.BrowserDetect(); this.audioManager = new CAAT.AudioManager().initialize(8); this.scenes = []; // input related variables initialization this.mousePoint = new CAAT.Point(0, 0, 0); this.prevMousePoint = new CAAT.Point(0, 0, 0); this.screenMousePoint = new CAAT.Point(0, 0, 0); this.isMouseDown = false; this.lastSelectedActor = null; this.dragging = false; this.cDirtyRects= []; this.dirtyRects= []; for( var i=0; i<64; i++ ) { this.dirtyRects.push( new CAAT.Rectangle() ); } this.dirtyRectsIndex= 0; return this; }; CAAT.Director.CLEAR_DIRTY_RECTS= 1; CAAT.Director.CLEAR_ALL= true; CAAT.Director.CLEAR_NONE= false; CAAT.Director.prototype = { debug: false, // flag indicating debug mode. It will draw affedted screen areas. onRenderStart: null, onRenderEnd: null, // input related attributes mousePoint: null, // mouse coordinate related to canvas 0,0 coord. prevMousePoint: null, // previous mouse position cache. Needed for drag events. screenMousePoint: null, // screen mouse coordinates. isMouseDown: false, // is the left mouse button pressed ? lastSelectedActor: null, // director's last actor receiving input. dragging: false, // is in drag mode ? // other attributes scenes: null, // Scenes collection. An array. currentScene: null, // The current Scene. This and only this will receive events. canvas: null, // The canvas the Director draws on. crc: null, // @deprecated. canvas rendering context ctx: null, // refactoring crc for a more convenient name time: 0, // virtual actor time. timeline: 0, // global director timeline. imagesCache: null, // An array of JSON elements of the form { id:string, image:Image } audioManager: null, clear: true, // clear background before drawing scenes ?? transitionScene: null, browserInfo: null, gl: null, glEnabled: false, glTextureManager: null, glTtextureProgram: null, glColorProgram: null, pMatrix: null, // projection matrix coords: null, // Float32Array coordsIndex: 0, uv: null, uvIndex: 0, front_to_back: false, statistics: { size_total: 0, size_active: 0, size_dirtyRects: 0, draws: 0 }, currentTexturePage: 0, currentOpacity: 1, intervalId: null, frameCounter: 0, RESIZE_NONE: 1, RESIZE_WIDTH: 2, RESIZE_HEIGHT: 4, RESIZE_BOTH: 8, RESIZE_PROPORTIONAL:16, resize: 1, onResizeCallback : null, __gestureScale : 0, __gestureRotation : 0, dirtyRects : null, cDirtyRects : null, dirtyRectsIndex : 0, dirtyRectsEnabled : false, nDirtyRects : 0, stopped : false, // is stopped, this director will do nothing. checkDebug : function() { if ( CAAT.DEBUG ) { var dd= new CAAT.Debug().initialize( this.width, 60 ); this.debugInfo= dd.debugInfo.bind(dd); } }, getRenderType : function() { return this.glEnabled ? 'WEBGL' : 'CANVAS'; }, windowResized : function(w, h) { switch (this.resize) { case this.RESIZE_WIDTH: this.setBounds(0, 0, w, this.height); break; case this.RESIZE_HEIGHT: this.setBounds(0, 0, this.width, h); break; case this.RESIZE_BOTH: this.setScaleBoth(w,h); break; case this.RESIZE_PROPORTIONAL: this.setScaleProportional(w,h); break; } if ( this.glEnabled ) { this.glReset(); } if ( this.onResizeCallback ) { this.onResizeCallback( this, w, h ); } },setScaleBoth : function(w,h) { // var factor= Math.min(w/this.referenceWidth, h/this.referenceHeight); var wf = w/this.referenceWidth; var hf = h/this.referenceHeight this.setScaleAnchored( wf, hf, 0, 0 ); this.canvas.width = this.referenceWidth*wf; this.canvas.height = this.referenceHeight*hf; this.ctx = this.canvas.getContext(this.glEnabled ? 'experimental-webgl' : '2d' ); this.crc = this.ctx; if ( this.glEnabled ) { this.glReset(); } }, setScaleProportional : function(w,h) { var factor= Math.min(w/this.referenceWidth, h/this.referenceHeight); this.setScaleAnchored( factor, factor, 0, 0 ); this.canvas.width = this.referenceWidth*factor; this.canvas.height = this.referenceHeight*factor; this.ctx = this.canvas.getContext(this.glEnabled ? 'experimental-webgl' : '2d' ); this.crc = this.ctx; if ( this.glEnabled ) { this.glReset(); } }, /** * Enable window resize events and set redimension policy. A callback functio could be supplied * to be notified on a Director redimension event. This is necessary in the case you set a redim * policy not equal to RESIZE_PROPORTIONAL. In those redimension modes, director's area and their * children scenes are resized to fit the new area. But scenes content is not resized, and have * no option of knowing so uless an onResizeCallback function is supplied. * * @param mode {number} RESIZE_BOTH, RESIZE_WIDTH, RESIZE_HEIGHT, RESIZE_NONE. * @param onResizeCallback {function(director{CAAT.Director}, width{integer}, height{integer})} a callback * to notify on canvas resize. */ enableResizeEvents : function(mode, onResizeCallback) { if (mode === this.RESIZE_BOTH || mode === this.RESIZE_WIDTH || mode === this.RESIZE_HEIGHT || mode===this.RESIZE_PROPORTIONAL) { this.referenceWidth= this.width; this.referenceHeight=this.height; this.resize = mode; CAAT.registerResizeListener(this); this.onResizeCallback= onResizeCallback; this.windowResized( window.innerWidth, window.innerHeight ); } else { CAAT.unregisterResizeListener(this); this.onResizeCallback= null; } }, /** * Set this director's bounds as well as its contained scenes. * @param x {number} ignored, will be 0. * @param y {number} ignored, will be 0. * @param w {number} director width. * @param h {number} director height. * * @return this */ setBounds : function(x, y, w, h) { CAAT.Director.superclass.setBounds.call(this, x, y, w, h); this.canvas.width = w; this.canvas.height = h; this.ctx = this.canvas.getContext(this.glEnabled ? 'experimental-webgl' : '2d'); this.crc = this.ctx; for (var i = 0; i < this.scenes.length; i++) { this.scenes[i].setBounds(0, 0, w, h); } if ( this.glEnabled ) { this.glReset(); } return this; }, /** * This method performs Director initialization. Must be called once. * If the canvas parameter is not set, it will create a Canvas itself, * and the developer must explicitly add the canvas to the desired DOM position. * This method will also set the Canvas dimension to the specified values * by width and height parameters. * * @param width {number} a canvas width * @param height {number} a canvas height * @param canvas {HTMLCanvasElement=} An optional Canvas object. * @param proxy {HTMLElement} this object can be an event proxy in case you'd like to layer different elements * and want events delivered to the correct element. * * @return this */ initialize : function(width, height, canvas, proxy) { if ( !canvas ) { canvas= document.createElement('canvas'); document.body.appendChild(canvas); } this.canvas = canvas; if ( typeof proxy==='undefined' ) { proxy= canvas; } this.setBounds(0, 0, width, height); this.create(); this.enableEvents(proxy); this.timeline = new Date().getTime(); // transition scene this.transitionScene = new CAAT.Scene().setBounds(0, 0, width, height); var transitionCanvas = document.createElement('canvas'); transitionCanvas.width = width; transitionCanvas.height = height; var transitionImageActor = new CAAT.Actor().setBackgroundImage(transitionCanvas); this.transitionScene.ctx = transitionCanvas.getContext('2d'); this.transitionScene.addChildImmediately(transitionImageActor); this.transitionScene.setEaseListener(this); this.checkDebug(); return this; }, glReset : function() { this.pMatrix= makeOrtho( 0, this.referenceWidth, this.referenceHeight, 0, -1, 1 ); this.gl.viewport(0,0,this.canvas.width,this.canvas.height); this.glColorProgram.setMatrixUniform(this.pMatrix); this.glTextureProgram.setMatrixUniform(this.pMatrix); this.gl.viewportWidth = this.canvas.width; this.gl.viewportHeight = this.canvas.height; }, /** * Experimental. * Initialize a gl enabled director. * @param width * @param height * @param canvas */ initializeGL : function(width, height, canvas, proxy) { if ( !canvas ) { canvas= document.createElement('canvas'); document.body.appendChild(canvas); } canvas.width = width; canvas.height = height; if ( typeof proxy==='undefined' ) { proxy= canvas; } this.referenceWidth= width; this.referenceHeight=height; var i; try { this.gl = canvas.getContext("experimental-webgl"/*, {antialias: false}*/); this.gl.viewportWidth = width; this.gl.viewportHeight = height; CAAT.GLRENDER= true; } catch(e) { } if (this.gl) { this.canvas = canvas; this.create(); this.setBounds(0, 0, width, height); this.crc = this.ctx; this.enableEvents(canvas); this.timeline = new Date().getTime(); this.glColorProgram = new CAAT.ColorProgram(this.gl).create().initialize(); this.glTextureProgram = new CAAT.TextureProgram(this.gl).create().initialize(); this.glTextureProgram.useProgram(); this.glReset(); var maxTris = 512; this.coords = new Float32Array(maxTris * 12); this.uv = new Float32Array(maxTris * 8); this.gl.clearColor(0.0, 0.0, 0.0, 255); if (this.front_to_back) { this.gl.clearDepth(1.0); this.gl.enable(this.gl.DEPTH_TEST); this.gl.depthFunc(this.gl.LESS); } else { this.gl.disable(this.gl.DEPTH_TEST); } this.gl.enable(this.gl.BLEND); // Fix FF this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA); this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA); this.glEnabled = true; this.checkDebug(); } else { // fallback to non gl enabled canvas. return this.initialize(width, height, canvas); } return this; }, /** * Creates an initializes a Scene object. * @return {CAAT.Scene} */ createScene : function() { var scene = new CAAT.Scene().create(); this.addScene(scene); return scene; }, setImagesCache : function(imagesCache, tpW, tpH) { var i; if (null !== this.glTextureManager) { this.glTextureManager.deletePages(); this.glTextureManager = null; } // delete previous image identifiers if ( this.imagesCache ) { var ids= []; for ( i = 0; i < this.imagesCache.length; i++) { ids.push( this.imagesCache[i].id ); } for( i=0; idirector.addImage(id,image,false) many times and a final call with * director.addImage(id,image,true) to finally command the director to create texture pages. * * @param id {string|object} an identitifier to retrieve the image with * @param image {Image|HTMLCanvasElement} image to add to cache * @param noUpdateGL {!boolean} unless otherwise stated, the director will * try to recreate the texture pages. */ addImage : function( id, image, noUpdateGL ) { if ( this.getImage(id) ) { for (var i = 0; i < this.imagesCache.length; i++) { if (this.imagesCache[i].id === id) { this.imagesCache[i].image = image; break; } } this.imagesCache[ id ] = image; } else { this.imagesCache.push( { id: id, image: image } ); this.imagesCache[id]= image; } if ( !!!noUpdateGL ) { this.updateGLPages( ); } }, deleteImage : function( id, noUpdateGL ) { for (var i = 0; i < this.imagesCache.length; i++) { if (this.imagesCache[i].id === id) { delete this.imagesCache[id]; this.imagesCache.splice(i,1); break; } } if ( !!!noUpdateGL ) { this.updateGLPages(); } }, setGLCurrentOpacity : function(opacity) { this.currentOpacity = opacity; this.glTextureProgram.setAlpha(opacity); }, /** * Render buffered elements. * @param vertex * @param coordsIndex * @param uv */ glRender : function(vertex, coordsIndex, uv) { vertex = vertex || this.coords; uv = uv || this.uv; coordsIndex = coordsIndex || this.coordsIndex; var gl = this.gl; var numTris = coordsIndex / 12 * 2; var numVertices = coordsIndex / 3; this.glTextureProgram.updateVertexBuffer(vertex); this.glTextureProgram.updateUVBuffer(uv); gl.drawElements(gl.TRIANGLES, 3 * numTris, gl.UNSIGNED_SHORT, 0); }, glFlush : function() { if (this.coordsIndex !== 0) { this.glRender(this.coords, this.coordsIndex, this.uv); } this.coordsIndex = 0; this.uvIndex = 0; this.statistics.draws++; }, findActorAtPosition : function(point) { // z-order var cl= this.childrenList; for( var i=cl.length-1; i>=0; i-- ) { var child= this.childrenList[i]; var np= new CAAT.Point( point.x, point.y, 0 ); var contained= child.findActorAtPosition( np ); if ( null!==contained ) { return contained; } } return this; }, /** * * Reset statistics information. * * @private */ resetStats : function() { this.statistics.size_total= 0; this.statistics.size_active=0; this.statistics.draws= 0; }, /** * This is the entry point for the animation system of the Director. * The director is fed with the elapsed time value to maintain a virtual timeline. * This virtual timeline will provide each Scene with its own virtual timeline, and will only * feed time when the Scene is the current Scene, or is being switched. * * If dirty rectangles are enabled and canvas is used for rendering, the dirty rectangles will be * set up as a single clip area. * * @param time {number} integer indicating the elapsed time between two consecutive frames of the * Director. */ render : function(time) { this.time += time; this.animate(this,time); if ( CAAT.DEBUG ) { this.resetStats(); } /** * draw director active scenes. */ var ne = this.childrenList.length; var i, tt, c; var ctx= this.ctx; if (this.glEnabled) { this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); this.coordsIndex = 0; this.uvIndex = 0; for (i = 0; i < ne; i++) { c = this.childrenList[i]; if (c.isInAnimationFrame(this.time)) { tt = c.time - c.start_time; if ( c.onRenderStart ) { c.onRenderStart(tt); } c.paintActorGL(this, tt); if ( c.onRenderEnd ) { c.onRenderEnd(tt); } if ( !c.isPaused() ) { c.time += time; } if ( CAAT.DEBUG ) { this.statistics.size_total+= c.size_total; this.statistics.size_active+= c.size_active; } } } this.glFlush(); } else { ctx.globalAlpha = 1; ctx.globalCompositeOperation = 'source-over'; ctx.save(); if ( this.dirtyRectsEnabled ) { this.modelViewMatrix.transformRenderingContext( ctx ); if ( !CAAT.DEBUG_DIRTYRECTS ) { ctx.beginPath(); this.nDirtyRects=0; var dr= this.cDirtyRects; for( i=0; i0 && CAAT.DEBUG && CAAT.DEBUG_DIRTYRECTS ) { ctx.beginPath(); this.nDirtyRects=0; var dr= this.cDirtyRects; for( i=0; i=this.dirtyRects.length ) { for( i=0; i<32; i++ ) { this.dirtyRects.push( new CAAT.Rectangle() ); } } var r= this.dirtyRects[ this.dirtyRectsIndex ]; r.x= rectangle.x; r.y= rectangle.y; r.x1= rectangle.x1; r.y1= rectangle.y1; r.width= rectangle.width; r.height= rectangle.height; this.cDirtyRects.push( r ); }, /** * This method draws an Scene to an offscreen canvas. This offscreen canvas is also a child of * another Scene (transitionScene). So instead of drawing two scenes while transitioning from * one to another, first of all an scene is drawn to offscreen, and that image is translated. *

    * Until the creation of this method, both scenes where drawn while transitioning with * its performance penalty since drawing two scenes could be twice as expensive than drawing * only one. *

    * Though a high performance increase, we should keep an eye on memory consumption. * * @param ctx a canvas.getContext('2d') instnce. * @param scene {CAAT.Scene} the scene to draw offscreen. */ renderToContext : function(ctx, scene) { /** * draw actors on scene. */ if (scene.isInAnimationFrame(this.time)) { ctx.setTransform(1,0,0,1, 0,0); ctx.globalAlpha = 1; ctx.globalCompositeOperation = 'source-over'; ctx.clearRect(0, 0, this.width, this.height); var octx = this.ctx; var ocrc = this.crc; this.ctx = ctx; this.crc = ctx; ctx.save(); /** * to draw an scene to an offscreen canvas, we have to: * 1.- save diector's world model view matrix * 2.- set no transformation on director since we want the offscreen to * be drawn 1:1. * 3.- set world dirty flag, so that the scene will recalculate its matrices * 4.- animate the scene * 5.- paint the scene * 6.- restore world model view matrix. */ var matmv= this.modelViewMatrix; var matwmv= this.worldModelViewMatrix; this.worldModelViewMatrix= new CAAT.Matrix(); this.modelViewMatrix= this.worldModelViewMatrix; this.wdirty= true; scene.animate(this, scene.time); if ( scene.onRenderStart ) { scene.onRenderStart(scene.time); } scene.paintActor(this, scene.time); if ( scene.onRenderEnd ) { scene.onRenderEnd(scene.time); } this.worldModelViewMatrix = matwmv; this.modelViewMatrix= matmv; ctx.restore(); this.ctx = octx; this.crc = ocrc; } }, /** * Add a new Scene to Director's Scene list. By adding a Scene to the Director * does not mean it will be immediately visible, you should explicitly call either *

      *
    • easeIn *
    • easeInOut *
    • easeInOutRandom *
    • setScene *
    • or any of the scene switching methods *
    * * @param scene {CAAT.Scene} an CAAT.Scene object. */ addScene : function(scene) { scene.setBounds(0, 0, this.width, this.height); this.scenes.push(scene); scene.setEaseListener(this); if (null === this.currentScene) { this.setScene(0); } }, /** * Get the number of scenes contained in the Director. * @return {number} the number of scenes contained in the Director. */ getNumScenes : function() { return this.scenes.length; }, /** * This method offers full control over the process of switching between any given two Scenes. * To apply this method, you must specify the type of transition to apply for each Scene and * the anchor to keep the Scene pinned at. *

    * The type of transition will be one of the following values defined in CAAT.Scene.prototype: *

      *
    • EASE_ROTATION *
    • EASE_SCALE *
    • EASE_TRANSLATION *
    * *

    * The anchor will be any of these values defined in CAAT.Actor.prototype: *

      *
    • ANCHOR_CENTER *
    • ANCHOR_TOP *
    • ANCHOR_BOTTOM *
    • ANCHOR_LEFT *
    • ANCHOR_RIGHT *
    • ANCHOR_TOP_LEFT *
    • ANCHOR_TOP_RIGHT *
    • ANCHOR_BOTTOM_LEFT *
    • ANCHOR_BOTTOM_RIGHT *
    * *

    * In example, for an entering scene performing a EASE_SCALE transition, the anchor is the * point by which the scene will scaled. * * @param inSceneIndex integer indicating the Scene index to bring in to the Director. * @param typein integer indicating the type of transition to apply to the bringing in Scene. * @param anchorin integer indicating the anchor of the bringing in Scene. * @param outSceneIndex integer indicating the Scene index to take away from the Director. * @param typeout integer indicating the type of transition to apply to the taking away in Scene. * @param anchorout integer indicating the anchor of the taking away Scene. * @param time inteter indicating the time to perform the process of switchihg between Scene object * in milliseconds. * @param alpha boolean boolean indicating whether alpha transparency fading will be applied to * the scenes. * @param interpolatorIn CAAT.Interpolator object to apply to entering scene. * @param interpolatorOut CAAT.Interpolator object to apply to exiting scene. */ easeInOut : function(inSceneIndex, typein, anchorin, outSceneIndex, typeout, anchorout, time, alpha, interpolatorIn, interpolatorOut) { if (inSceneIndex === this.getCurrentSceneIndex()) { return; } var ssin = this.scenes[ inSceneIndex ]; var sout = this.scenes[ outSceneIndex ]; if ( !CAAT.__CSS__ && !this.glEnabled ) { this.renderToContext(this.transitionScene.ctx, sout); sout = this.transitionScene; } ssin.setExpired(false); sout.setExpired(false); ssin.mouseEnabled = false; sout.mouseEnabled = false; ssin.resetTransform(); sout.resetTransform(); ssin.setLocation(0, 0); sout.setLocation(0, 0); ssin.alpha = 1; sout.alpha = 1; if (typein === CAAT.Scene.prototype.EASE_ROTATION) { ssin.easeRotationIn(time, alpha, anchorin, interpolatorIn); } else if (typein === CAAT.Scene.prototype.EASE_SCALE) { ssin.easeScaleIn(0, time, alpha, anchorin, interpolatorIn); } else { ssin.easeTranslationIn(time, alpha, anchorin, interpolatorIn); } if (typeout === CAAT.Scene.prototype.EASE_ROTATION) { sout.easeRotationOut(time, alpha, anchorout, interpolatorOut); } else if (typeout === CAAT.Scene.prototype.EASE_SCALE) { sout.easeScaleOut(0, time, alpha, anchorout, interpolatorOut); } else { sout.easeTranslationOut(time, alpha, anchorout, interpolatorOut); } this.childrenList = []; this.addChild(sout); this.addChild(ssin); }, /** * This method will switch between two given Scene indexes (ie, take away scene number 2, * and bring in scene number 5). *

    * It will randomly choose for each Scene the type of transition to apply and the anchor * point of each transition type. *

    * It will also set for different kind of transitions the following interpolators: *

      *
    • EASE_ROTATION -> ExponentialInOutInterpolator, exponent 4. *
    • EASE_SCALE -> ElasticOutInterpolator, 1.1 and .4 *
    • EASE_TRANSLATION -> BounceOutInterpolator *
    * *

    * These are the default values, and could not be changed by now. * This method in final instance delegates the process to easeInOutMethod. * * @see easeInOutMethod. * * @param inIndex integer indicating the entering scene index. * @param outIndex integer indicating the exiting scene index. * @param time integer indicating the time to take for the process of Scene in/out in milliseconds. * @param alpha boolean indicating whether alpha transparency fading should be applied to transitions. */ easeInOutRandom : function(inIndex, outIndex, time, alpha) { var pin = Math.random(); var pout = Math.random(); var typeIn; var interpolatorIn; if (pin < 0.33) { typeIn = CAAT.Scene.prototype.EASE_ROTATION; interpolatorIn = new CAAT.Interpolator().createExponentialInOutInterpolator(4); } else if (pin < 0.66) { typeIn = CAAT.Scene.prototype.EASE_SCALE; interpolatorIn = new CAAT.Interpolator().createElasticOutInterpolator(1.1, 0.4); } else { typeIn = CAAT.Scene.prototype.EASE_TRANSLATE; interpolatorIn = new CAAT.Interpolator().createBounceOutInterpolator(); } var typeOut; var interpolatorOut; if (pout < 0.33) { typeOut = CAAT.Scene.prototype.EASE_ROTATION; interpolatorOut = new CAAT.Interpolator().createExponentialInOutInterpolator(4); } else if (pout < 0.66) { typeOut = CAAT.Scene.prototype.EASE_SCALE; interpolatorOut = new CAAT.Interpolator().createExponentialOutInterpolator(4); } else { typeOut = CAAT.Scene.prototype.EASE_TRANSLATE; interpolatorOut = new CAAT.Interpolator().createBounceOutInterpolator(); } this.easeInOut( inIndex, typeIn, (Math.random() * 8.99) >> 0, outIndex, typeOut, (Math.random() * 8.99) >> 0, time, alpha, interpolatorIn, interpolatorOut); }, /** * This method changes Director's current Scene to the scene index indicated by * inSceneIndex parameter. The Scene running in the director won't be eased out. * * @see {CAAT.Interpolator} * @see {CAAT.Actor} * @see {CAAT.Scene} * * @param inSceneIndex integer indicating the new Scene to set as current. * @param type integer indicating the type of transition to apply to bring the new current * Scene to the Director. The values will be one of: CAAT.Scene.prototype.EASE_ROTATION, * CAAT.Scene.prototype.EASE_SCALE, CAAT.Scene.prototype.EASE_TRANSLATION. * @param time integer indicating how much time in milliseconds the Scene entrance will take. * @param alpha boolean indicating whether alpha transparency fading will be applied to the * entereing Scene. * @param anchor integer indicating the anchor to fix for Scene transition. It will be any of * CAAT.Actor.prototype.ANCHOR_* values. * @param interpolator an CAAT.Interpolator object indicating the interpolation function to * apply. */ easeIn : function(inSceneIndex, type, time, alpha, anchor, interpolator) { var sin = this.scenes[ inSceneIndex ]; if (type === CAAT.Scene.prototype.EASE_ROTATION) { sin.easeRotationIn(time, alpha, anchor, interpolator); } else if (type === CAAT.Scene.prototype.EASE_SCALE) { sin.easeScaleIn(0, time, alpha, anchor, interpolator); } else { sin.easeTranslationIn(time, alpha, anchor, interpolator); } this.childrenList = []; this.addChild(sin); sin.resetTransform(); sin.setLocation(0, 0); sin.alpha = 1; sin.mouseEnabled = false; sin.setExpired(false); }, /** * Changes (or sets) the current Director scene to the index * parameter. There will be no transition on scene change. * @param sceneIndex {number} an integer indicating the index of the target Scene * to be shown. */ setScene : function(sceneIndex) { var sin = this.scenes[ sceneIndex ]; this.childrenList = []; this.addChild(sin); this.currentScene = sin; sin.setExpired(false); sin.mouseEnabled = true; sin.resetTransform(); sin.setLocation(0, 0); sin.alpha = 1; sin.activated(); }, /** * This method will change the current Scene by the Scene indicated as parameter. * It will apply random values for anchor and transition type. * @see easeInOutRandom * * @param iNewSceneIndex {number} an integer indicating the index of the new scene to run on the Director. * @param time {number} an integer indicating the time the Scene transition will take. * @param alpha {boolean} a boolean indicating whether Scene transition should be fading. * @param transition {boolean} a boolean indicating whether the scene change must smoothly animated. */ switchToScene : function(iNewSceneIndex, time, alpha, transition) { var currentSceneIndex = this.getSceneIndex(this.currentScene); if (!transition) { this.setScene(iNewSceneIndex); } else { this.easeInOutRandom(iNewSceneIndex, currentSceneIndex, time, alpha); } }, /** * Sets the previous Scene in sequence as the current Scene. * @see switchToScene. * * @param time {number} integer indicating the time the Scene transition will take. * @param alpha {boolean} a boolean indicating whether Scene transition should be fading. * @param transition {boolean} a boolean indicating whether the scene change must smoothly animated. */ switchToPrevScene : function(time, alpha, transition) { var currentSceneIndex = this.getSceneIndex(this.currentScene); if (this.getNumScenes() <= 1 || currentSceneIndex === 0) { return; } if (!transition) { this.setScene(currentSceneIndex - 1); } else { this.easeInOutRandom(currentSceneIndex - 1, currentSceneIndex, time, alpha); } }, /** * Sets the previous Scene in sequence as the current Scene. * @see switchToScene. * * @param time {number} integer indicating the time the Scene transition will take. * @param alpha {boolean} a boolean indicating whether Scene transition should be fading. * @param transition {boolean} a boolean indicating whether the scene change must smoothly animated. */ switchToNextScene: function(time, alpha, transition) { var currentSceneIndex = this.getSceneIndex(this.currentScene); if (this.getNumScenes() <= 1 || currentSceneIndex === this.getNumScenes() - 1) { return; } if (!transition) { this.setScene(currentSceneIndex + 1); } else { this.easeInOutRandom(currentSceneIndex + 1, currentSceneIndex, time, alpha); } }, mouseEnter : function(mouseEvent) { }, mouseExit : function(mouseEvent) { }, mouseMove : function(mouseEvent) { }, mouseDown : function(mouseEvent) { }, mouseUp : function(mouseEvent) { }, mouseDrag : function(mouseEvent) { }, /** * Scene easing listener. Notifies scenes when they're about to be activated (set as current * director's scene). * * @param scene {CAAT.Scene} the scene that has just been brought in or taken out of the director. * @param b_easeIn {boolean} scene enters or exits ? */ easeEnd : function(scene, b_easeIn) { // scene is going out if (!b_easeIn) { scene.setExpired(true); } else { this.currentScene = scene; this.currentScene.activated(); } scene.mouseEnabled = true; scene.emptyBehaviorList(); }, /** * Return the index for a given Scene object contained in the Director. * @param scene {CAAT.Scene} */ getSceneIndex : function(scene) { for (var i = 0; i < this.scenes.length; i++) { if (this.scenes[i] === scene) { return i; } } return -1; }, /** * Get a concrete director's scene. * @param index {number} an integer indicating the scene index. * @return {CAAT.Scene} a CAAT.Scene object instance or null if the index is oob. */ getScene : function(index) { return this.scenes[index]; }, /** * Return the index of the current scene in the Director's scene list. * @return {number} the current scene's index. */ getCurrentSceneIndex : function() { return this.getSceneIndex(this.currentScene); }, /** * Return the running browser name. * @return {string} the browser name. */ getBrowserName : function() { return this.browserInfo.browser; }, /** * Return the running browser version. * @return {string} the browser version. */ getBrowserVersion : function() { return this.browserInfo.version; }, /** * Return the operating system name. * @return {string} the os name. */ getOSName : function() { return this.browserInfo.OS; }, /** * Gets the resource with the specified resource name. * The Director holds a collection called imagesCache * where you can store a JSON of the form * [ { id: imageId, image: imageObject } ]. * This structure will be used as a resources cache. * There's a CAAT.ImagePreloader class to preload resources and * generate this structure on loading finalization. * * @param sId {object} an String identifying a resource. */ getImage : function(sId) { var ret = this.imagesCache[sId]; if (ret) { return ret; } for (var i = 0; i < this.imagesCache.length; i++) { if (this.imagesCache[i].id === sId) { return this.imagesCache[i].image; } } return null; }, /** * Adds an audio to the cache. * * @see CAAT.AudioManager.addAudio * @return this */ addAudio : function(id, url) { this.audioManager.addAudio(id, url); return this; }, /** * Plays the audio instance identified by the id. * @param id {object} the object used to store a sound in the audioCache. */ audioPlay : function(id) { this.audioManager.play(id); }, /** * Loops an audio instance identified by the id. * @param id {object} the object used to store a sound in the audioCache. * * @return {HTMLElement|null} the value from audioManager.loop */ audioLoop : function(id) { return this.audioManager.loop(id); }, endSound : function() { return this.audioManager.endSound(); }, setSoundEffectsEnabled : function(enabled) { return this.audioManager.setSoundEffectsEnabled(enabled); }, setMusicEnabled : function(enabled) { return this.audioManager.setMusicEnabled(enabled); }, isMusicEnabled : function() { return this.audioManager.isMusicEnabled(); }, isSoundEffectsEnabled : function() { return this.audioManager.isSoundEffectsEnabled(); }, setVolume : function( id, volume ) { return this.audioManager.setVolume( id, volume ); }, /** * Removes Director's scenes. */ emptyScenes : function() { this.scenes = []; }, /** * Adds an scene to this Director. * @param scene {CAAT.Scene} a scene object. */ addChild : function(scene) { scene.parent = this; this.childrenList.push(scene); }, /** * @Deprecated use CAAT.loop instead. * @param fps * @param callback * @param callback2 */ loop : function(fps,callback,callback2) { if ( callback2 ) { this.onRenderStart= callback; this.onRenderEnd= callback2; } else if (callback) { this.onRenderEnd= callback; } CAAT.loop(); }, /** * Starts the director animation.If no scene is explicitly selected, the current Scene will * be the first scene added to the Director. *

    * The fps parameter will set the animation quality. Higher values, * means CAAT will try to render more frames in the same second (at the * expense of cpu power at least until hardware accelerated canvas rendering * context are available). A value of 60 is a high frame rate and should not be exceeded. * * @param fps {number} integer value indicating the target frames per second to run * the animation at. */ renderFrame : function(fps, callback) { if (this.stopped) { return; } var t = new Date().getTime(), delta = t - this.timeline; /* check for massive frame time. if for example the current browser tab is minified or taken out of foreground, the system will account for a bit time interval. minify that impact by lowering down the elapsed time (virtual timelines FTW) */ if ( delta > 500 ) { delta= 500; } if ( this.onRenderStart ) { this.onRenderStart(delta); } this.render(delta); if ( this.debugInfo ) { this.debugInfo(this.statistics); } this.timeline = t; if (this.onRenderEnd) { this.onRenderEnd(delta); } }, endLoop : function () { }, /** * This method states whether the director must clear background before rendering * each frame. * * The clearing method could be: * + CAAT.Director.CLEAR_ALL. previous to draw anything on screen the canvas will have clearRect called on it. * + CAAT.Director.CLEAR_DIRTY_RECTS. Actors marked as invalid, or which have been moved, rotated or scaled * will have their areas redrawn. * + CAAT.Director.CLEAR_NONE. clears nothing. * * @param clear {CAAT.Director.CLEAR_ALL |�CAAT.Director.CLEAR_NONE | CAAT.Director.CLEAR_DIRTY_RECTS} * @return this. */ setClear : function(clear) { this.clear = clear; if ( this.clear===CAAT.Director.CLEAR_DIRTY_RECTS ) { this.dirtyRectsEnabled= true; } return this; }, /** * Get this Director's AudioManager instance. * @return {CAAT.AudioManager} the AudioManager instance. */ getAudioManager : function() { return this.audioManager; }, /** * Acculumate dom elements position to properly offset on-screen mouse/touch events. * @param node */ cumulateOffset : function(node, parent, prop) { var left= prop+'Left'; var top= prop+'Top'; var x=0, y=0, style; while( navigator.browser!=='iOS' && node && node.style ) { if ( node.currentStyle ) { style= node.currentStyle['position']; } else { style= (node.ownerDocument.defaultView || node.ownerDocument.parentWindow).getComputedStyle(node, null); style= style ? style.getPropertyValue('position') : null; } // if (!/^(relative|absolute|fixed)$/.test(style)) { if (!/^(fixed)$/.test(style)) { x += node[left]; y+= node[top]; node = node[parent]; } else { break; } } return { x: x, y: y, style: style }; }, getOffset : function( node ) { var res= this.cumulateOffset(node, 'offsetParent', 'offset'); if ( res.style==='fixed' ) { var res2= this.cumulateOffset(node, node.parentNode ? 'parentNode' : 'parentElement', 'scroll'); return { x: res.x + res2.x, y: res.y + res2.y }; } return { x: res.x, y: res.y }; }, /** * Normalize input event coordinates to be related to (0,0) canvas position. * @param point {CAAT.Point} a CAAT.Point instance to hold the canvas coordinate. * @param e {MouseEvent} a mouse event from an input event. */ getCanvasCoord : function(point, e) { var pt= new CAAT.Point( ); var posx = 0; var posy = 0; if (!e) e = window.event; if (e.pageX || e.pageY) { posx = e.pageX; posy = e.pageY; } else if (e.clientX || e.clientY) { posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; } var offset= this.getOffset(this.canvas); posx-= offset.x; posy-= offset.y; ////////////// // transformar coordenada inversamente con affine transform de director. pt.x= posx; pt.y= posy; if ( !this.modelViewMatrixI ) { this.modelViewMatrixI= this.modelViewMatrix.getInverse(); } this.modelViewMatrixI.transformCoord(pt); posx= pt.x; posy= pt.y point.set(posx, posy); this.screenMousePoint.set(posx, posy); }, __mouseDownHandler : function(e) { /* was dragging and mousedown detected, can only mean a mouseOut's been performed and on mouseOver, no button was presses. Then, send a mouseUp for the previos actor, and return; */ if ( this.dragging && this.lastSelectedActor ) { this.__mouseUpHandler(e); return; } this.getCanvasCoord(this.mousePoint, e); this.isMouseDown = true; var lactor = this.findActorAtPosition(this.mousePoint); if (null !== lactor) { var pos = lactor.viewToModel( new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0)); lactor.mouseDown( new CAAT.MouseEvent().init( pos.x, pos.y, e, lactor, new CAAT.Point( this.screenMousePoint.x, this.screenMousePoint.y ))); } this.lastSelectedActor= lactor; }, __mouseUpHandler : function(e) { this.isMouseDown = false; this.getCanvasCoord(this.mousePoint, e); var pos= null; var lactor= this.lastSelectedActor; if (null !== lactor) { pos = lactor.viewToModel( new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0)); if ( lactor.actionPerformed && lactor.contains(pos.x, pos.y) ) { lactor.actionPerformed(e) } lactor.mouseUp( new CAAT.MouseEvent().init( pos.x, pos.y, e, lactor, this.screenMousePoint, this.currentScene.time)); } if (!this.dragging && null !== lactor) { if (lactor.contains(pos.x, pos.y)) { lactor.mouseClick( new CAAT.MouseEvent().init( pos.x, pos.y, e, lactor, this.screenMousePoint, this.currentScene.time)); } } this.dragging = false; this.in_= false; // CAAT.setCursor('default'); }, __mouseMoveHandler : function(e) { //this.getCanvasCoord(this.mousePoint, e); var lactor; var pos; var ct= this.currentScene ? this.currentScene.time : 0; // drag if (this.isMouseDown && null !== this.lastSelectedActor) { lactor = this.lastSelectedActor; pos = lactor.viewToModel( new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0)); // check for mouse move threshold. if (!this.dragging) { if (Math.abs(this.prevMousePoint.x - pos.x) < CAAT.DRAG_THRESHOLD_X && Math.abs(this.prevMousePoint.y - pos.y) < CAAT.DRAG_THRESHOLD_Y) { return; } } this.dragging = true; var px= lactor.x; var py= lactor.y; lactor.mouseDrag( new CAAT.MouseEvent().init( pos.x, pos.y, e, lactor, new CAAT.Point( this.screenMousePoint.x, this.screenMousePoint.y), ct)); this.prevMousePoint.x= pos.x; this.prevMousePoint.y= pos.y; /** * Element has not moved after drag, so treat it as a button. */ if ( px===lactor.x && py===lactor.y ) { var contains= lactor.contains(pos.x, pos.y); if (this.in_ && !contains) { lactor.mouseExit( new CAAT.MouseEvent().init( pos.x, pos.y, e, lactor, this.screenMousePoint, ct)); this.in_ = false; } if (!this.in_ && contains ) { lactor.mouseEnter( new CAAT.MouseEvent().init( pos.x, pos.y, e, lactor, this.screenMousePoint, ct)); this.in_ = true; } } return; } // mouse move. this.in_= true; lactor = this.findActorAtPosition(this.mousePoint); // cambiamos de actor. if (lactor !== this.lastSelectedActor) { if (null !== this.lastSelectedActor) { pos = this.lastSelectedActor.viewToModel( new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0)); this.lastSelectedActor.mouseExit( new CAAT.MouseEvent().init( pos.x, pos.y, e, this.lastSelectedActor, this.screenMousePoint, ct)); } if (null !== lactor) { pos = lactor.viewToModel( new CAAT.Point( this.screenMousePoint.x, this.screenMousePoint.y, 0)); lactor.mouseEnter( new CAAT.MouseEvent().init( pos.x, pos.y, e, lactor, this.screenMousePoint, ct)); } } pos = lactor.viewToModel( new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0)); if (null !== lactor) { lactor.mouseMove( new CAAT.MouseEvent().init( pos.x, pos.y, e, lactor, this.screenMousePoint, ct)); } this.prevMousePoint.x= pos.x; this.prevMousePoint.y= pos.y; this.lastSelectedActor = lactor; }, __mouseOutHandler : function(e) { if ( this.dragging ) { return; } if (null !== this.lastSelectedActor ) { this.getCanvasCoord(this.mousePoint, e); var pos = new CAAT.Point(this.mousePoint.x, this.mousePoint.y, 0); this.lastSelectedActor.viewToModel(pos); var ev= new CAAT.MouseEvent().init( pos.x, pos.y, e, this.lastSelectedActor, this.screenMousePoint, this.currentScene.time); this.lastSelectedActor.mouseExit(ev); this.lastSelectedActor.mouseOut(ev); if ( !this.dragging ) { this.lastSelectedActor = null; } } else { this.isMouseDown = false; this.in_ = false; } }, __mouseOverHandler : function(e) { if (this.dragging ) { return; } var lactor; var pos, ev; if ( null==this.lastSelectedActor ) { lactor= this.findActorAtPosition( this.mousePoint ); if (null !== lactor) { pos = lactor.viewToModel( new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0)); ev= new CAAT.MouseEvent().init( pos.x, pos.y, e, lactor, this.screenMousePoint, this.currentScene ? this.currentScene.time : 0); lactor.mouseOver(ev); lactor.mouseEnter(ev); } this.lastSelectedActor= lactor; } else { lactor= this.lastSelectedActor; pos = lactor.viewToModel( new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0)); ev= new CAAT.MouseEvent().init( pos.x, pos.y, e, lactor, this.screenMousePoint, this.currentScene.time); lactor.mouseOver(ev); lactor.mouseEnter(ev); } }, __mouseDBLClickHandler : function(e) { this.getCanvasCoord(this.mousePoint, e); if (null !== this.lastSelectedActor) { /* var pos = this.lastSelectedActor.viewToModel( new CAAT.Point(this.screenMousePoint.x, this.screenMousePoint.y, 0)); */ this.lastSelectedActor.mouseDblClick( new CAAT.MouseEvent().init( this.mousePoint.x, this.mousePoint.y, e, this.lastSelectedActor, this.screenMousePoint, this.currentScene.time)); } }, /** * Same as mouseDown but not preventing event. * Will only take care of first touch. * @param e */ __touchStartHandler : function(e) { if ( e.target===this.canvas ) { e.preventDefault(); e= e.targetTouches[0]; var mp= this.mousePoint; this.getCanvasCoord(mp, e); if ( mp.x<0 || mp.y<0 || mp.x>=this.width || mp.y>=this.height ) { return; } this.touching= true; this.__mouseDownHandler(e); } }, __touchEndHandler : function(e) { if ( this.touching ) { e.preventDefault(); e= e.changedTouches[0]; var mp= this.mousePoint; this.getCanvasCoord(mp, e); this.touching= false; this.__mouseUpHandler(e); } }, __touchMoveHandler : function(e) { if ( this.touching ) { e.preventDefault(); if ( this.gesturing ) { return; } for( var i=0; i=me.width || mp.y>=me.height ) { return; } me.touching= true; me.__mouseDownHandler(e); } }, false ); window.addEventListener('mouseover',function(e) { if ( e.target===canvas && !me.dragging ) { e.preventDefault(); e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); var mp= me.mousePoint; me.getCanvasCoord(mp, e); if ( mp.x<0 || mp.y<0 || mp.x>=me.width || mp.y>=me.height ) { return; } me.__mouseOverHandler(e); } }, false); window.addEventListener('mouseout',function(e) { if ( e.target===canvas && !me.dragging ) { e.preventDefault(); e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); var mp= me.mousePoint; me.getCanvasCoord(mp, e); me.__mouseOutHandler(e); } }, false); window.addEventListener('mousemove', function(e) { e.preventDefault(); e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); var mp= me.mousePoint; me.getCanvasCoord(mp, e); if ( !me.dragging && ( mp.x<0 || mp.y<0 || mp.x>=me.width || mp.y>=me.height ) ) { return; } me.__mouseMoveHandler(e); }, false); window.addEventListener("dblclick", function(e) { if ( e.target===canvas ) { e.preventDefault(); e.cancelBubble = true; if (e.stopPropagation) e.stopPropagation(); var mp= me.mousePoint; me.getCanvasCoord(mp, e); if ( mp.x<0 || mp.y<0 || mp.x>=me.width || mp.y>=me.height ) { return; } me.__mouseDBLClickHandler(e); } }, false); window.addEventListener("touchstart", this.__touchStartHandler.bind(this), false); window.addEventListener("touchmove", this.__touchMoveHandler.bind(this), false); window.addEventListener("touchend", this.__touchEndHandler.bind(this), false); window.addEventListener("gesturestart", function(e) { if ( e.target===canvas ) { e.preventDefault(); me.__gestureStart( e.scale, e.rotation ); } }, false ); window.addEventListener("gestureend", function(e) { if ( e.target===canvas ) { e.preventDefault(); me.__gestureEnd( e.scale, e.rotation ); } }, false ); window.addEventListener("gesturechange", function(e) { if ( e.target===canvas ) { e.preventDefault(); me.__gestureChange( e.scale, e.rotation ); } }, false ); }, enableEvents : function( onElement ) { CAAT.RegisterDirector(this); this.in_ = false; this.createEventHandler( onElement ); }, createEventHandler : function( onElement ) { //var canvas= this.canvas; this.in_ = false; //this.addHandlers(canvas); this.addHandlers( onElement ); } }; if (CAAT.__CSS__) { CAAT.Director.prototype.clip= true; CAAT.Director.prototype.glEnabled= false; CAAT.Director.prototype.getRenderType= function() { return 'CSS'; }; CAAT.Director.prototype.setScaleProportional= function(w,h) { var factor= Math.min(w/this.referenceWidth, h/this.referenceHeight); this.setScaleAnchored( factor, factor, 0, 0 ); this.eventHandler.style.width= ''+this.referenceWidth +'px'; this.eventHandler.style.height= ''+this.referenceHeight+'px'; }; CAAT.Director.prototype.setBounds= function(x, y, w, h) { CAAT.Director.superclass.setBounds.call(this, x, y, w, h); for (var i = 0; i < this.scenes.length; i++) { this.scenes[i].setBounds(0, 0, w, h); } this.eventHandler.style.width= w+'px'; this.eventHandler.style.height= h+'px'; return this; }; /** * In this DOM/CSS implementation, proxy is not taken into account since the event router is a top most * div in the document hierarchy (z-index 999999). * @param width * @param height * @param domElement * @param proxy */ CAAT.Director.prototype.initialize= function(width, height, domElement, proxy) { this.timeline = new Date().getTime(); this.domElement= domElement; this.style('position','absolute'); this.style('width',''+width+'px'); this.style('height',''+height+'px'); this.style('overflow', 'hidden' ); this.enableEvents(domElement); this.setBounds(0, 0, width, height); this.checkDebug(); return this; }; CAAT.Director.prototype.render= function(time) { this.time += time; this.animate(this,time); /** * draw director active scenes. */ var i, l, tt; if ( CAAT.DEBUG ) { this.resetStats(); } for (i = 0, l=this.childrenList.length; i < l; i++) { var c= this.childrenList[i]; if (c.isInAnimationFrame(this.time)) { tt = c.time - c.start_time; if ( c.onRenderStart ) { c.onRenderStart(tt); } c.paintActor(this, tt); if ( c.onRenderEnd ) { c.onRenderEnd(tt); } if (!c.isPaused()) { c.time += time; } if ( CAAT.DEBUG ) { this.statistics.size_total+= c.size_total; this.statistics.size_active+= c.size_active; this.statistics.size_dirtyRects= this.nDirtyRects; } } } this.frameCounter++; }; CAAT.Director.prototype.addScene= function(scene) { scene.setVisible(true); scene.setBounds(0, 0, this.width, this.height); this.scenes.push(scene); scene.setEaseListener(this); if (null === this.currentScene) { this.setScene(0); } this.domElement.appendChild( scene.domElement ); }; CAAT.Director.prototype.emptyScenes= function() { this.scenes = []; this.domElement.innerHTML=''; this.createEventHandler(); }; CAAT.Director.prototype.setClear= function(clear) { return this; }; CAAT.Director.prototype.createEventHandler= function() { this.eventHandler= document.createElement('div'); this.domElement.appendChild(this.eventHandler); this.eventHandler.style.position= 'absolute'; this.eventHandler.style.left= '0'; this.eventHandler.style.top= '0'; this.eventHandler.style.zIndex= 999999; this.eventHandler.style.width= ''+this.width+'px'; this.eventHandler.style.height= ''+this.height+'px'; var canvas= this.eventHandler; this.in_ = false; this.addHandlers(canvas); }; } extend(CAAT.Director, CAAT.ActorContainer, null); })(); /** * See LICENSE file. * * MouseEvent is a class to hold necessary information of every mouse event related to concrete * scene graph Actors. * * Here it is also the logic to on mouse events, pump the correct event to the appropiate scene * graph Actor. * * TODO: add events for event pumping: * + cancelBubling * **/ (function() { /** * This function creates a mouse event that represents a touch or mouse event. * @constructor */ CAAT.MouseEvent = function() { this.point= new CAAT.Point(0,0,0); this.screenPoint= new CAAT.Point(0,0,0); return this; }; CAAT.MouseEvent.prototype= { screenPoint: null, point: null, time: 0, source: null, shift: false, control: false, alt: false, meta: false, sourceEvent: null, init : function( x,y,sourceEvent,source,screenPoint,time ) { this.point.set(x,y); this.source= source; this.screenPoint= screenPoint; this.alt = sourceEvent.altKey; this.control = sourceEvent.ctrlKey; this.shift = sourceEvent.shiftKey; this.meta = sourceEvent.metaKey; this.sourceEvent= sourceEvent; this.x= x; this.y= y; this.time= time; return this; }, isAltDown : function() { return this.alt; }, isControlDown : function() { return this.control; }, isShiftDown : function() { return this.shift; }, isMetaDown: function() { return this.meta; }, getSourceEvent : function() { return this.sourceEvent; } }; })(); CAAT.setCoordinateClamping= function( clamp ) { if ( clamp ) { CAAT.Matrix.prototype.transformRenderingContext= CAAT.Matrix.prototype.transformRenderingContext_Clamp; CAAT.Matrix.prototype.transformRenderingContextSet= CAAT.Matrix.prototype.transformRenderingContextSet_Clamp; } else { CAAT.Matrix.prototype.transformRenderingContext= CAAT.Matrix.prototype.transformRenderingContext_NoClamp; CAAT.Matrix.prototype.transformRenderingContextSet= CAAT.Matrix.prototype.transformRenderingContextSet_NoClamp; } }; CAAT.RENDER_MODE_CONTINUOUS= 1; // redraw every frame CAAT.RENDER_MODE_DIRTY= 2; // suitable for evented CAAT. CAAT.RENDER_MODE= CAAT.RENDER_MODE_CONTINUOUS; /** * Box2D point meter conversion ratio. */ CAAT.PMR= 64; CAAT.GLRENDER= false; /** * Allow visual debugging artifacts. */ CAAT.DEBUG= false; CAAT.DEBUGBB= false; CAAT.DEBUGBBBCOLOR='#00f'; CAAT.DEBUGAABB= false; // debug bounding boxes. CAAT.DEBUGAABBCOLOR='#f00'; CAAT.DEBUG_DIRTYRECTS=false; /** * Log function which deals with window's Console object. */ CAAT.log= function() { if(window.console){ window.console.log( Array.prototype.slice.call(arguments) ); } }; CAAT.FRAME_TIME= 0; /** * Flag to signal whether events are enabled for CAAT. */ CAAT.GlobalEventsEnabled= false; /** * Accelerometer related data. */ CAAT.prevOnDeviceMotion= null; // previous accelerometer callback function. CAAT.onDeviceMotion= null; // current accelerometer callback set for CAAT. CAAT.accelerationIncludingGravity= { x:0, y:0, z:0 }; // acceleration data. CAAT.rotationRate= { alpha: 0, beta:0, gamma: 0 }; // angles data. /** * Do not consider mouse drag gesture at least until you have dragged * 5 pixels in any direction. */ CAAT.DRAG_THRESHOLD_X= 5; CAAT.DRAG_THRESHOLD_Y= 5; // has the animation loop began ? CAAT.renderEnabled= false; CAAT.FPS= 60; /** * On resize event listener */ CAAT.windowResizeListeners= []; /** * Register an object as resize callback. * @param f { function( windowResized(width{number},height{number})} ) } */ CAAT.registerResizeListener= function(f) { CAAT.windowResizeListeners.push(f); }; /** * Unregister a resize listener. * @param director {CAAT.Director} */ CAAT.unregisterResizeListener= function(director) { for( var i=0; i * var si= new CAAT.SpriteImage(). * initialize( an_image_instance, rows, columns ). * setAnimationImageIndex( [2,1,0,1] ). // cycle throwout image with these indexes * setChangeFPS( 200 ). // change sprite every 200 ms. * setSpriteTransformation( CAAT.SpriteImage.TR_xx); // optionally draw images inverted, ... * * * A SpriteImage is an sprite sheet. It encapsulates an Image and treates and references it as a two * dimensional array of row by columns sub-images. The access form will be sequential so if defined a * CompoundImage of more than one row, the subimages will be referenced by an index ranging from 0 to * rows*columns-1. Each sumimage will be of size (image.width/columns) by (image.height/rows). * *

    * It is able to draw its sub-images in the following ways: *

      *
    • no transformed (default) *
    • flipped horizontally *
    • flipped vertically *
    • flipped both vertical and horizontally *
    * *

    * It is supposed to be used in conjunction with CAAT.SpriteActor instances. * * @constructor * */ CAAT.SpriteImage = function() { this.paint= this.paintN; this.setAnimationImageIndex([0]); this.mapInfo= {}; return this; }; CAAT.SpriteImage.prototype = { animationImageIndex: null, // an Array defining the sprite frame sequence prevAnimationTime: -1, changeFPS: 1000, // how much Scene time to take before changing an Sprite frame. transformation: 0, // any of the TR_* constants. spriteIndex: 0, // the current sprite frame TR_NONE: 0, // constants used to determine how to draw the sprite image, TR_FLIP_HORIZONTAL: 1, TR_FLIP_VERTICAL: 2, TR_FLIP_ALL: 3, TR_FIXED_TO_SIZE: 4, TR_TILE: 5, image: null, rows: 1, columns: 1, width: 0, height: 0, singleWidth: 0, singleHeight: 0, scaleX: 1, scaleY: 1, offsetX: 0, offsetY: 0, ownerActor: null, mapInfo : null, map : null, setOwner : function(actor) { this.ownerActor= actor; return this; }, getRows: function() { return this.rows; }, getColumns : function() { return this.columns; }, getWidth : function() { var el= this.mapInfo[this.spriteIndex]; return el.width; }, getHeight : function() { var el= this.mapInfo[this.spriteIndex]; return el.height; }, /** * Get a reference to the same image information (rows, columns, image and uv cache) of this * SpriteImage. This means that re-initializing this objects image info (that is, calling initialize * method) will change all reference's image information at the same time. */ getRef : function() { var ret= new CAAT.SpriteImage(); ret.image= this.image; ret.rows= this.rows; ret.columns= this.columns; ret.width= this.width; ret.height= this.height; ret.singleWidth= this.singleWidth; ret.singleHeight= this.singleHeight; ret.mapInfo= this.mapInfo; ret.offsetX= this.offsetX; ret.offsetY= this.offsetY; ret.scaleX= this.scaleX; ret.scaleY= this.scaleY; return ret; }, /** * Set horizontal displacement to draw image. Positive values means drawing the image more to the * right. * @param x {number} * @return this */ setOffsetX : function(x) { this.offsetX= x; return this; }, /** * Set vertical displacement to draw image. Positive values means drawing the image more to the * bottom. * @param y {number} * @return this */ setOffsetY : function(y) { this.offsetY= y; return this; }, setOffset : function( x,y ) { this.offsetX= x; this.offsetY= y; return this; }, /** * Initialize a grid of subimages out of a given image. * @param image {HTMLImageElement|Image} an image object. * @param rows {number} number of rows. * @param columns {number} number of columns * * @return this */ initialize : function(image, rows, columns) { this.image = image; this.rows = rows; this.columns = columns; this.width = image.width; this.height = image.height; this.singleWidth = Math.floor(this.width / columns); this.singleHeight = Math.floor(this.height / rows); this.mapInfo= {}; var i,sx0,sy0; var helper; if (image.__texturePage) { image.__du = this.singleWidth / image.__texturePage.width; image.__dv = this.singleHeight / image.__texturePage.height; var w = this.singleWidth; var h = this.singleHeight; var mod = this.columns; if (image.inverted) { var t = w; w = h; h = t; mod = this.rows; } var xt = this.image.__tx; var yt = this.image.__ty; var tp = this.image.__texturePage; for (i = 0; i < rows * columns; i++) { var c = ((i % mod) >> 0); var r = ((i / mod) >> 0); var u = xt + c * w; // esquina izq x var v = yt + r * h; var u1 = u + w; var v1 = v + h; helper= new CAAT.SpriteImageHelper(u,v,(u1-u),(v1-v),tp.width,tp.height).setGL( u / tp.width, v / tp.height, u1 / tp.width, v1 / tp.height ); this.mapInfo[i]= helper; } } else { for (i = 0; i < rows * columns; i++) { sx0 = ((i % this.columns) | 0) * this.singleWidth; sy0 = ((i / this.columns) | 0) * this.singleHeight; helper= new CAAT.SpriteImageHelper( sx0, sy0, this.singleWidth, this.singleHeight, image.width, image.height ); this.mapInfo[i]= helper; } } return this; }, /** * Must be used to draw actor background and the actor should have setClip(true) so that the image tiles * properly. * @param director * @param time * @param x * @param y */ paintTiled : function( director, time, x, y ) { this.setSpriteIndexAtTime(time); var el= this.mapInfo[this.spriteIndex]; var r= new CAAT.Rectangle(); this.ownerActor.AABB.intersect( director.AABB, r ); var w= this.getWidth(); var h= this.getHeight(); var xoff= (this.offsetX-this.ownerActor.x) % w; if ( xoff> 0 ) { xoff= xoff-w; } var yoff= (this.offsetY-this.ownerActor.y) % h; if ( yoff> 0 ) { yoff= yoff-h; } var nw= (((r.width-xoff)/w)>>0)+1; var nh= (((r.height-yoff)/h)>>0)+1; var i,j; var ctx= director.ctx; for( i=0; i>0, (r.y-this.ownerActor.y+yoff+i*el.height)>>0, el.width, el.height); } } }, /** * Draws the subimage pointed by imageIndex horizontally inverted. * @param canvas a canvas context. * @param imageIndex {number} a subimage index. * @param x {number} x position in canvas to draw the image. * @param y {number} y position in canvas to draw the image. * * @return this */ paintInvertedH : function(director, time, x, y) { this.setSpriteIndexAtTime(time); var el= this.mapInfo[this.spriteIndex]; var ctx= director.ctx; ctx.save(); //ctx.translate(((0.5 + x) | 0) + el.width, (0.5 + y) | 0); ctx.translate( (x|0) + el.width, y|0 ); ctx.scale(-1, 1); ctx.drawImage( this.image, el.x, el.y, el.width, el.height, this.offsetX>>0, this.offsetY>>0, el.width, el.height ); ctx.restore(); return this; }, /** * Draws the subimage pointed by imageIndex vertically inverted. * @param canvas a canvas context. * @param imageIndex {number} a subimage index. * @param x {number} x position in canvas to draw the image. * @param y {number} y position in canvas to draw the image. * * @return this */ paintInvertedV : function(director, time, x, y) { this.setSpriteIndexAtTime(time); var el= this.mapInfo[this.spriteIndex]; var ctx= director.ctx; ctx.save(); //ctx.translate((x + 0.5) | 0, (0.5 + y + el.height) | 0); ctx.translate( x|0, (y + el.height) | 0); ctx.scale(1, -1); ctx.drawImage( this.image, el.x, el.y, el.width, el.height, this.offsetX>>0,this.offsetY>>0, el.width, el.height); ctx.restore(); return this; }, /** * Draws the subimage pointed by imageIndex both horizontal and vertically inverted. * @param canvas a canvas context. * @param imageIndex {number} a subimage index. * @param x {number} x position in canvas to draw the image. * @param y {number} y position in canvas to draw the image. * * @return this */ paintInvertedHV : function(director, time, x, y) { this.setSpriteIndexAtTime(time); var el= this.mapInfo[this.spriteIndex]; var ctx= director.ctx; ctx.save(); //ctx.translate((x + 0.5) | 0, (0.5 + y + el.height) | 0); ctx.translate( x | 0, (y + el.height) | 0); ctx.scale(1, -1); ctx.translate(el.width, 0); ctx.scale(-1, 1); ctx.drawImage( this.image, el.x, el.y, el.width, el.height, this.offsetX>>0, this.offsetY>>0, el.width, el.height); ctx.restore(); return this; }, /** * Draws the subimage pointed by imageIndex. * @param canvas a canvas context. * @param imageIndex {number} a subimage index. * @param x {number} x position in canvas to draw the image. * @param y {number} y position in canvas to draw the image. * * @return this */ paintN : function(director, time, x, y) { this.setSpriteIndexAtTime(time); var el= this.mapInfo[this.spriteIndex]; director.ctx.drawImage( this.image, el.x, el.y, el.width, el.height, (this.offsetX+x)>>0, (this.offsetY+y)>>0, el.width, el.height); return this; }, paintChunk : function( ctx, dx,dy, x, y, w, h ) { ctx.drawImage( this.image, x,y,w,h, dx,dy,w,h ); }, paintTile : function(ctx, index, x, y) { var el= this.mapInfo[index]; ctx.drawImage( this.image, el.x, el.y, el.width, el.height, (this.offsetX+x)>>0, (this.offsetY+y)>>0, el.width, el.height); return this; }, /** * Draws the subimage pointed by imageIndex scaled to the size of w and h. * @param canvas a canvas context. * @param imageIndex {number} a subimage index. * @param x {number} x position in canvas to draw the image. * @param y {number} y position in canvas to draw the image. * @param w {number} new width of the subimage. * @param h {number} new height of the subimage. * * @return this */ paintScaled : function(director, time, x, y) { this.setSpriteIndexAtTime(time); var el= this.mapInfo[this.spriteIndex]; director.ctx.drawImage( this.image, el.x, el.y, el.width, el.height, (this.offsetX+x)>>0, (this.offsetY+y)>>0, this.ownerActor.width, this.ownerActor.height ); return this; }, getCurrentSpriteImageCSSPosition : function() { var el= this.mapInfo[this.spriteIndex]; var x= -(el.x-this.offsetX); var y= -(el.y-this.offsetY); return ''+x+'px '+ y+'px '+ (this.ownerActor.transformation===this.TR_TILE ? 'repeat' : 'no-repeat'); }, /** * Get the number of subimages in this compoundImage * @return {number} */ getNumImages : function() { return this.rows * this.columns; }, /** * TODO: set mapping coordinates for different transformations. * @param imageIndex * @param uvBuffer * @param uvIndex */ setUV : function(uvBuffer, uvIndex) { var im = this.image; if (!im.__texturePage) { return; } var index = uvIndex; var sIndex= this.spriteIndex; var el= this.mapInfo[this.spriteIndex]; var u= el.u; var v= el.v; var u1= el.u1; var v1= el.v1; if ( this.offsetX || this.offsetY ) { var w= this.ownerActor.width; var h= this.ownerActor.height; var tp= im.__texturePage; var _u= -this.offsetX / tp.width; var _v= -this.offsetY / tp.height; var _u1=(w-this.offsetX) / tp.width; var _v1=(h-this.offsetY) / tp.height; u= _u + im.__u; v= _v + im.__v; u1= _u1 + im.__u; v1= _v1 + im.__v; } if (im.inverted) { uvBuffer[index++] = u1; uvBuffer[index++] = v; uvBuffer[index++] = u1; uvBuffer[index++] = v1; uvBuffer[index++] = u; uvBuffer[index++] = v1; uvBuffer[index++] = u; uvBuffer[index++] = v; } else { uvBuffer[index++] = u; uvBuffer[index++] = v; uvBuffer[index++] = u1; uvBuffer[index++] = v; uvBuffer[index++] = u1; uvBuffer[index++] = v1; uvBuffer[index++] = u; uvBuffer[index++] = v1; } }, /** * Set the elapsed time needed to change the image index. * @param fps an integer indicating the time in milliseconds to change. * @return this */ setChangeFPS : function(fps) { this.changeFPS= fps; return this; }, /** * Set the transformation to apply to the Sprite image. * Any value of *

  • TR_NONE *
  • TR_FLIP_HORIZONTAL *
  • TR_FLIP_VERTICAL *
  • TR_FLIP_ALL * * @param transformation an integer indicating one of the previous values. * @return this */ setSpriteTransformation : function( transformation ) { this.transformation= transformation; switch(transformation) { case this.TR_FLIP_HORIZONTAL: this.paint= this.paintInvertedH; break; case this.TR_FLIP_VERTICAL: this.paint= this.paintInvertedV; break; case this.TR_FLIP_ALL: this.paint= this.paintInvertedHV; break; case this.TR_FIXED_TO_SIZE: this.paint= this.paintScaled; break; case this.TR_TILE: this.paint= this.paintTiled; break; default: this.paint= this.paintN; } return this; }, resetAnimationTime : function() { this.prevAnimationTime= -1; return this; }, /** * Set the sprite animation images index. This method accepts an array of objects which define indexes to * subimages inside this sprite image. * If the SpriteImage is instantiated by calling the method initialize( image, rows, cols ), the value of * aAnimationImageIndex should be an array of numbers, which define the indexes into an array of subimages * with size rows*columns. * If the method InitializeFromMap( image, map ) is called, the value for aAnimationImageIndex is expected * to be an array of strings which are the names of the subobjects contained in the map object. * * @param aAnimationImageIndex an array indicating the Sprite's frames. */ setAnimationImageIndex : function( aAnimationImageIndex ) { this.animationImageIndex= aAnimationImageIndex; this.spriteIndex= aAnimationImageIndex[0]; this.prevAnimationTime= -1; return this; }, setSpriteIndex : function(index) { this.spriteIndex= index; return this; }, /** * Draws the sprite image calculated and stored in spriteIndex. * * @param director the CAAT.Director object instance that contains the Scene the Actor is in. * @param time an integer indicating the Scene time when the bounding box is to be drawn. */ setSpriteIndexAtTime : function(time) { if ( this.animationImageIndex.length>1 ) { if ( this.prevAnimationTime===-1 ) { this.prevAnimationTime= time; this.spriteIndex=0; } else { var ttime= time; ttime-= this.prevAnimationTime; ttime/= this.changeFPS; ttime%= this.animationImageIndex.length; this.spriteIndex= this.animationImageIndex[Math.floor(ttime)]; } } }, getMapInfo : function( index ) { return this.mapInfo[ index ]; }, /** * This method takes the output generated from the tool at http://labs.hyperandroid.com/static/texture/spriter.html * and creates a map into that image. * @param image {Image|HTMLImageElement|Canvas} an image * @param map {object} the map into the image to define subimages. */ initializeFromMap : function( image, map ) { this.initialize( image, 1, 1 ); var key; var helper; var count=0; for( key in map ) { var value= map[key]; helper= new CAAT.SpriteImageHelper( value.x, value.y, value.width, value.height, image.width, image.height ); this.mapInfo[key]= helper; // set a default spriteIndex if ( !count ) { this.setAnimationImageIndex( [key] ); } count++; } return this; }, /** * * @param image {Image|HTMLImageElement|Canvas} * @param map object with pairs "" : { * id : {number}, * height : {number}, * xoffset : {number}, * letter : {string}, * yoffset : {number}, * width : {number}, * xadvance: {number}, * y : {number}, * x : {number} * } */ initializeAsGlyphDesigner : function( image, map ) { this.initialize( image, 1, 1 ); var key; var helper; var count=0; for( key in map ) { var value= map[key]; helper= new CAAT.SpriteImageHelper( value.x, value.y, value.width, value.height, image.width, image.height ); helper.xoffset= typeof value.xoffset==='undefined' ? 0 : value.xoffset; helper.yoffset= typeof value.yoffset==='undefined' ? 0 : value.yoffset; helper.xadvance= typeof value.xadvance==='undefined' ? value.width : value.xadvance; this.mapInfo[key]= helper; // set a default spriteIndex if ( !count ) { this.setAnimationImageIndex( [key] ); } count++; } return this; }, /** * * @param image * @param map: Array<{c: "a", width: 40}> */ initializeAsFontMap : function( image, chars ) { this.initialize( image, 1, 1 ); var helper; var x=0; for( var i=0;i> 0; for( var i=0;iy ) { y=h; } } this.fontHeight= y; return this.fontHeight; }, drawString : function( ctx, str, x, y ) { var i, l, charInfo, w; var charArr = str.split(""); for( i=0; i * A TimerTask is defined at least by: *
      *
    • startTime: since when the timer will be active *
    • duration: from startTime to startTime+duration, the timerTask will be notifying (if set) the callback callback_tick. *
    *

    * Upon TimerTask expiration, the TimerTask will notify (if set) the callback function callback_timeout. * Upon a call to the method cancel, the timer will be set expired, and (if set) the callback to callback_cancel will be * invoked. *

    * Timer notifications will be performed BEFORE scene loop. * * @constructor * */ CAAT.TimerTask= function() { return this; }; CAAT.TimerTask.prototype= { startTime: 0, duration: 0, callback_timeout: null, callback_tick: null, callback_cancel: null, scene: null, taskId: 0, remove: false, /** * Create a TimerTask. * The taskId will be set by the scene. * @param startTime {number} an integer indicating TimerTask enable time. * @param duration {number} an integer indicating TimerTask duration. * @param callback_timeout {function( sceneTime {number}, timertaskTime{number}, timertask {CAAT.TimerTask} )} on timeout callback function. * @param callback_tick {function( sceneTime {number}, timertaskTime{number}, timertask {CAAT.TimerTask} )} on tick callback function. * @param callback_cancel {function( sceneTime {number}, timertaskTime{number}, timertask {CAAT.TimerTask} )} on cancel callback function. * * @return this */ create: function( startTime, duration, callback_timeout, callback_tick, callback_cancel ) { this.startTime= startTime; this.duration= duration; this.callback_timeout= callback_timeout; this.callback_tick= callback_tick; this.callback_cancel= callback_cancel; return this; }, /** * Performs TimerTask operation. The task will check whether it is in frame time, and will * either notify callback_timeout or callback_tick. * * @param time {number} an integer indicating scene time. * @return this * * @protected * */ checkTask : function(time) { var ttime= time; ttime-= this.startTime; if ( ttime>=this.duration ) { this.remove= true; if( this.callback_timeout ) { this.callback_timeout( time, ttime, this ); } } else { if ( this.callback_tick ) { this.callback_tick( time, ttime, this ); } } return this; }, /** * Reschedules this TimerTask by changing its startTime to current scene's time. * @param time {number} an integer indicating scene time. * @return this */ reset : function( time ) { this.remove= false; this.startTime= time; this.scene.ensureTimerTask(this); return this; }, /** * Cancels this timer by removing it on scene's next frame. The function callback_cancel will * be called. * @return this */ cancel : function() { this.remove= true; if ( null!=this.callback_cancel ) { this.callback_cancel( this.scene.time, this.scene.time-this.startTime, this ); } return this; } }; })(); /** * See LICENSE file. * */ (function() { /** * Scene is the top level ActorContainer of the Director at any given time. * The only time when 2 scenes could be active will be during scene change. * An scene controls the way it enters/exits the scene graph. It is also the entry point for all * input related and timed related events to every actor on screen. * * @constructor * @extends CAAT.ActorContainer * */ CAAT.Scene= function() { CAAT.Scene.superclass.constructor.call(this); this.timerList= []; this.fillStyle= null; return this; }; CAAT.Scene.prototype= { easeContainerBehaviour: null, // Behavior container used uniquely for Scene switching. easeContainerBehaviourListener: null, // who to notify about container behaviour events. Array. easeIn: false, // When Scene switching, this boolean identifies whether the // Scene is being brought in, or taken away. EASE_ROTATION: 1, // Constant values to identify the type of Scene transition EASE_SCALE: 2, // to perform on Scene switching by the Director. EASE_TRANSLATE: 3, timerList: null, // collection of CAAT.TimerTask objects. timerSequence: 0, // incremental CAAT.TimerTask id. paused: false, isPaused : function() { return this.paused; }, setPaused : function( paused ) { this.paused= paused; }, /** * Check and apply timers in frame time. * @param time {number} the current Scene time. */ checkTimers : function(time) { var tl= this.timerList; var i=tl.length-1; while( i>=0 ) { if ( !tl[i].remove ) { tl[i].checkTask(time); } i--; } }, /** * Make sure the timertask is contained in the timer task list by adding it to the list in case it * is not contained. * @param timertask {CAAT.TimerTask} a CAAT.TimerTask object. * @return this */ ensureTimerTask : function( timertask ) { if ( !this.hasTimer(timertask) ) { this.timerList.push(timertask); } return this; }, /** * Check whether the timertask is in this scene's timer task list. * @param timertask {CAAT.TimerTask} a CAAT.TimerTask object. * @return {boolean} a boolean indicating whether the timertask is in this scene or not. */ hasTimer : function( timertask ) { var tl= this.timerList; var i=tl.length-1; while( i>=0 ) { if ( tl[i]===timertask ) { return true; } i--; } return false; }, /** * Creates a timer task. Timertask object live and are related to scene's time, so when an Scene * is taken out of the Director the timer task is paused, and resumed on Scene restoration. * * @param startTime {number} an integer indicating the scene time this task must start executing at. * @param duration {number} an integer indicating the timerTask duration. * @param callback_timeout {function} timer on timeout callback function. * @param callback_tick {function} timer on tick callback function. * @param callback_cancel {function} timer on cancel callback function. * * @return {CAAT.TimerTask} a CAAT.TimerTask class instance. */ createTimer : function( startTime, duration, callback_timeout, callback_tick, callback_cancel ) { var tt= new CAAT.TimerTask().create( startTime, duration, callback_timeout, callback_tick, callback_cancel ); tt.taskId= this.timerSequence++; tt.sceneTime = this.time; tt.scene= this; this.timerList.push( tt ); return tt; }, /** * Removes expired timers. This method must not be called directly. */ removeExpiredTimers : function() { var i; var tl= this.timerList; for( i=0; iCAAT.Actor.prototype.ANCHOR_LEFT *

  • CAAT.Actor.prototype.ANCHOR_RIGHT *
  • CAAT.Actor.prototype.ANCHOR_TOP *
  • CAAT.Actor.prototype.ANCHOR_BOTTOM * if any other value is specified, any of the previous ones will be applied. * * @param time integer indicating time in milliseconds for the Scene. * @param alpha boolean indicating whether fading will be applied to the Scene. * @param anchor integer indicating the Scene switch anchor. * @param isIn boolean indicating whether the scene will be brought in. * @param interpolator {CAAT.Interpolator} a CAAT.Interpolator to apply to the Scene transition. */ easeTranslation : function( time, alpha, anchor, isIn, interpolator ) { this.easeContainerBehaviour= new CAAT.ContainerBehavior(); this.easeIn= isIn; var pb= new CAAT.PathBehavior(); if ( interpolator ) { pb.setInterpolator( interpolator ); } pb.setFrameTime( 0, time ); // BUGBUG anchors: 1..4 if ( anchor<1 ) { anchor=1; } else if ( anchor>4 ) { anchor= 4; } switch(anchor) { case CAAT.Actor.prototype.ANCHOR_TOP: if ( isIn ) { pb.setPath( new CAAT.Path().setLinear( 0, -this.height, 0, 0) ); } else { pb.setPath( new CAAT.Path().setLinear( 0, 0, 0, -this.height) ); } break; case CAAT.Actor.prototype.ANCHOR_BOTTOM: if ( isIn ) { pb.setPath( new CAAT.Path().setLinear( 0, this.height, 0, 0) ); } else { pb.setPath( new CAAT.Path().setLinear( 0, 0, 0, this.height) ); } break; case CAAT.Actor.prototype.ANCHOR_LEFT: if ( isIn ) { pb.setPath( new CAAT.Path().setLinear( -this.width, 0, 0, 0) ); } else { pb.setPath( new CAAT.Path().setLinear( 0, 0, -this.width, 0) ); } break; case CAAT.Actor.prototype.ANCHOR_RIGHT: if ( isIn ) { pb.setPath( new CAAT.Path().setLinear( this.width, 0, 0, 0) ); } else { pb.setPath( new CAAT.Path().setLinear( 0, 0, this.width, 0) ); } break; } if (alpha) { this.createAlphaBehaviour(time,isIn); } this.easeContainerBehaviour.addBehavior(pb); this.easeContainerBehaviour.setFrameTime( this.time, time ); this.easeContainerBehaviour.addListener(this); this.emptyBehaviorList(); CAAT.Scene.superclass.addBehavior.call( this, this.easeContainerBehaviour ); }, /** * Called from CAAT.Director to bring in a Scene. * A helper method for easeScale. * @param time integer indicating time in milliseconds for the Scene to be brought in. * @param alpha boolean indicating whether fading will be applied to the Scene. * @param anchor integer indicating the Scene switch anchor. * @param interpolator {CAAT.Interpolator} a CAAT.Interpolator to apply to the Scene transition. * @param starttime integer indicating in milliseconds from which scene time the behavior will be applied. */ easeScaleIn : function(starttime,time,alpha,anchor,interpolator) { this.easeScale(starttime,time,alpha,anchor,true,interpolator); this.easeIn= true; }, /** * Called from CAAT.Director to take away a Scene. * A helper method for easeScale. * @param time integer indicating time in milliseconds for the Scene to be taken away. * @param alpha boolean indicating whether fading will be applied to the Scene. * @param anchor integer indicating the Scene switch anchor. * @param interpolator {CAAT.Interpolator} a CAAT.Interpolator instance to apply to the Scene transition. * @param starttime integer indicating in milliseconds from which scene time the behavior will be applied. */ easeScaleOut : function(starttime,time,alpha,anchor,interpolator) { this.easeScale(starttime,time,alpha,anchor,false,interpolator); this.easeIn= false; }, /** * Called from CAAT.Director to bring in ot take away an Scene. * @param time integer indicating time in milliseconds for the Scene to be taken away. * @param alpha boolean indicating whether fading will be applied to the Scene. * @param anchor integer indicating the Scene switch anchor. * @param interpolator {CAAT.Interpolator} a CAAT.Interpolator to apply to the Scene transition. * @param starttime integer indicating in milliseconds from which scene time the behavior will be applied. * @param isIn boolean indicating whether the Scene is being brought in. */ easeScale : function(starttime,time,alpha,anchor,isIn,interpolator) { this.easeContainerBehaviour= new CAAT.ContainerBehavior(); var x=0; var y=0; var x2=0; var y2=0; switch(anchor) { case CAAT.Actor.prototype.ANCHOR_TOP_LEFT: case CAAT.Actor.prototype.ANCHOR_TOP_RIGHT: case CAAT.Actor.prototype.ANCHOR_BOTTOM_LEFT: case CAAT.Actor.prototype.ANCHOR_BOTTOM_RIGHT: case CAAT.Actor.prototype.ANCHOR_CENTER: x2=1; y2=1; break; case CAAT.Actor.prototype.ANCHOR_TOP: case CAAT.Actor.prototype.ANCHOR_BOTTOM: x=1; x2=1; y=0; y2=1; break; case CAAT.Actor.prototype.ANCHOR_LEFT: case CAAT.Actor.prototype.ANCHOR_RIGHT: y=1; y2=1; x=0; x2=1; break; default: alert('scale anchor ?? '+anchor); } if ( !isIn ) { var tmp; tmp= x; x= x2; x2= tmp; tmp= y; y= y2; y2= tmp; } if (alpha) { this.createAlphaBehaviour(time,isIn); } var anchorPercent= this.getAnchorPercent(anchor); var sb= new CAAT.ScaleBehavior(). setFrameTime( starttime, time ). setValues(x,x2,y,y2, anchorPercent.x, anchorPercent.y); if ( interpolator ) { sb.setInterpolator(interpolator); } this.easeContainerBehaviour.addBehavior(sb); this.easeContainerBehaviour.setFrameTime( this.time, time ); this.easeContainerBehaviour.addListener(this); this.emptyBehaviorList(); CAAT.Scene.superclass.addBehavior.call( this, this.easeContainerBehaviour ); }, /** * Overriden method to disallow default behavior. * Do not use directly. */ addBehavior : function(behaviour) { return this; }, /** * Called from CAAT.Director to use Rotations for bringing in. * This method is a Helper for the method easeRotation. * @param time integer indicating time in milliseconds for the Scene to be brought in. * @param alpha boolean indicating whether fading will be applied to the Scene. * @param anchor integer indicating the Scene switch anchor. * @param interpolator {CAAT.Interpolator} a CAAT.Interpolator to apply to the Scene transition. */ easeRotationIn : function(time,alpha,anchor,interpolator) { this.easeRotation(time,alpha,anchor,true, interpolator); this.easeIn= true; }, /** * Called from CAAT.Director to use Rotations for taking Scenes away. * This method is a Helper for the method easeRotation. * @param time integer indicating time in milliseconds for the Scene to be taken away. * @param alpha boolean indicating whether fading will be applied to the Scene. * @param anchor integer indicating the Scene switch anchor. * @param interpolator {CAAT.Interpolator} a CAAT.Interpolator to apply to the Scene transition. */ easeRotationOut : function(time,alpha,anchor,interpolator) { this.easeRotation(time,alpha,anchor,false,interpolator); this.easeIn= false; }, /** * Called from CAAT.Director to use Rotations for taking away or bringing Scenes in. * @param time integer indicating time in milliseconds for the Scene to be taken away or brought in. * @param alpha boolean indicating whether fading will be applied to the Scene. * @param anchor integer indicating the Scene switch anchor. * @param interpolator {CAAT.Interpolator} a CAAT.Interpolator to apply to the Scene transition. * @param isIn boolean indicating whehter the Scene is brought in. */ easeRotation : function(time,alpha,anchor,isIn,interpolator) { this.easeContainerBehaviour= new CAAT.ContainerBehavior(); var start=0; var end=0; if (anchor==CAAT.Actor.prototype.ANCHOR_CENTER ) { anchor= CAAT.Actor.prototype.ANCHOR_TOP; } switch(anchor) { case CAAT.Actor.prototype.ANCHOR_TOP: case CAAT.Actor.prototype.ANCHOR_BOTTOM: case CAAT.Actor.prototype.ANCHOR_LEFT: case CAAT.Actor.prototype.ANCHOR_RIGHT: start= Math.PI * (Math.random()<0.5 ? 1 : -1); break; case CAAT.Actor.prototype.ANCHOR_TOP_LEFT: case CAAT.Actor.prototype.ANCHOR_TOP_RIGHT: case CAAT.Actor.prototype.ANCHOR_BOTTOM_LEFT: case CAAT.Actor.prototype.ANCHOR_BOTTOM_RIGHT: start= Math.PI/2 * (Math.random()<0.5 ? 1 : -1); break; default: alert('rot anchor ?? '+anchor); } if ( false===isIn ) { var tmp= start; start=end; end= tmp; } if ( alpha ) { this.createAlphaBehaviour(time,isIn); } var anchorPercent= this.getAnchorPercent(anchor); var rb= new CAAT.RotateBehavior(). setFrameTime( 0, time ). setValues( start, end, anchorPercent.x, anchorPercent.y ); if ( interpolator ) { rb.setInterpolator(interpolator); } this.easeContainerBehaviour.addBehavior(rb); this.easeContainerBehaviour.setFrameTime( this.time, time ); this.easeContainerBehaviour.addListener(this); this.emptyBehaviorList(); CAAT.Scene.superclass.addBehavior.call( this, this.easeContainerBehaviour ); }, /** * Registers a listener for listen for transitions events. * Al least, the Director registers himself as Scene easing transition listener. * When the transition is done, it restores the Scene's capability of receiving events. * @param listener {function(caat_behavior,time,actor)} an object which contains a method of the form * behaviorExpired( caat_behaviour, time, actor); */ setEaseListener : function( listener ) { this.easeContainerBehaviourListener=listener; }, /** * Private. * listener for the Scene's easeContainerBehaviour. * @param actor */ behaviorExpired : function(actor) { this.easeContainerBehaviourListener.easeEnd(this, this.easeIn); }, /** * This method should be overriden in case the developer wants to do some special actions when * the scene has just been brought in. */ activated : function() { }, /** * Scenes, do not expire the same way Actors do. * It simply will be set expired=true, but the frameTime won't be modified. */ setExpired : function(bExpired) { this.expired= bExpired; }, /** * An scene by default does not paint anything because has not fillStyle set. * @param director * @param time */ paint : function(director, time) { if ( this.fillStyle ) { var ctx= director.crc; ctx.fillStyle= this.fillStyle; ctx.fillRect(0,0,this.width,this.height ); } }, /** * Find a pointed actor at position point. * This method tries lo find the correctly pointed actor in two different ways. * + first of all, if inputList is defined, it will look for an actor in it. * + if no inputList is defined, it will traverse the scene graph trying to find a pointed actor. * @param point */ findActorAtPosition : function(point) { var i,j; var p= new CAAT.Point(); if ( this.inputList ) { var il= this.inputList; for( i=0; i number of lists. */ enableInputList : function( size ) { this.inputList= []; for( var i=0; i an actor instance * @param index the inputList index to add the actor to. This value will be clamped to the number of * available lists. * @param position the position on the selected inputList to add the actor at. This value will be * clamped to the number of available lists. */ addActorToInputList : function( actor, index, position ) { if ( index<0 ) index=0; else if ( index>=this.inputList.length ) index= this.inputList.length-1; var il= this.inputList[index]; if ( typeof position==="undefined" || position>=il.length ) { il.push( actor ); } else if (position<=0) { il.unshift( actor ); } else { il.splice( position, 0, actor ); } return this; }, /** * Remove all elements from an input list. * @param index the inputList index to add the actor to. This value will be clamped to the number of * available lists so take care when emptying a non existant inputList index since you could end up emptying * an undesired input list. */ emptyInputList : function( index ) { if ( index<0 ) index=0; else if ( index>=this.inputList.length ) index= this.inputList.length-1; this.inputList[index]= []; return this; }, /** * remove an actor from a given input list index. * If no index is supplied, the actor will be removed from every input list. * @param actor * @param index an optional input list index. This value will be clamped to the number of * available lists. */ removeActorFromInputList : function( actor, index ) { if ( typeof index==="undefined" ) { var i,j; for( i=0; i=this.inputList.length ) index= this.inputList.length-1; var il= this.inputList[index]; for( j=0; j this.bounds.right) { aCircle.position.x = this.bounds.left + radius; } else if(boundsRule & wrapXMask && xpos+diameter < this.bounds.left) { aCircle.position.x = this.bounds.right - radius; } // Wrap Y if(boundsRule & wrapYMask && ypos-diameter > this.bounds.bottom) { aCircle.position.y = this.bounds.top - radius; } else if(boundsRule & wrapYMask && ypos+diameter < this.bounds.top) { aCircle.position.y = this.bounds.bottom + radius; } // Constrain X if(boundsRule & constrainXMask && xpos+radius >= this.bounds.right) { aCircle.position.x = aCircle.position.x = this.bounds.right-radius; } else if(boundsRule & constrainXMask && xpos-radius < this.bounds.left) { aCircle.position.x = this.bounds.left + radius; } // Constrain Y if(boundsRule & constrainYMask && ypos+radius > this.bounds.bottom) { aCircle.position.y = this.bounds.bottom - radius; } else if(boundsRule & constrainYMask && ypos-radius < this.bounds.top) { aCircle.position.y = this.bounds.top + radius; } }, /** * Given an x,y position finds circle underneath and sets it to the currently grabbed circle * @param {Number} xpos An x position * @param {Number} ypos A y position * @param {Number} buffer A radiusSquared around the point in question where something is considered to match */ getCircleAt: function(xpos, ypos, buffer) { var circleList = this.allCircles; var len = circleList.length; var grabVector = new CAAT.Point(xpos, ypos, 0); // These are set every time a better match i found var closestCircle = null; var closestDistance = Number.MAX_VALUE; // Loop thru and find the closest match for(var i = 0; i < len; i++) { var aCircle = circleList[i]; if(!aCircle) continue; var distanceSquared = aCircle.position.getDistanceSquared(grabVector); if(distanceSquared < closestDistance && distanceSquared < aCircle.radiusSquared + buffer) { closestDistance = distanceSquared; closestCircle = aCircle; } } return closestCircle; }, circlesCanCollide: function(circleA, circleB) { if(!circleA || !circleB || circleA===circleB) return false; // one is null (will be deleted next loop), or both point to same obj. // if(circleA.delegate == null || circleB.delegate == null) return false; // This circle will be removed next loop, it's entity is already removed // if(circleA.isFixed & circleB.isFixed) return false; // if(circleA.delegate .clientID === circleB.delegate.clientID) return false; // Don't let something collide with stuff it owns // They dont want to collide // if((circleA.collisionGroup & circleB.collisionMask) == 0) return false; // if((circleB.collisionGroup & circleA.collisionMask) == 0) return false; return true; }, /** * Accessors */ setBounds: function(x, y, w, h) { this.bounds.x = x; this.bounds.y = y; this.bounds.width = w; this.bounds.height = h; }, setNumberOfCollisionPasses: function(value) { this.numberOfCollisionPasses = value; return this; }, setNumberOfTargetingPasses: function(value) { this.numberOfTargetingPasses = value; return this; }, /** * Helpers */ sortOnDistanceToTarget: function(circleA, circleB) { var valueA = circleA.getDistanceSquaredFromPosition(circleA.targetPosition); var valueB = circleB.getDistanceSquaredFromPosition(circleA.targetPosition); var comparisonResult = 0; if(valueA > valueB) comparisonResult = -1; else if(valueA < valueB) comparisonResult = 1; return comparisonResult; }, /** * Memory Management */ removeExpiredElements: function() { // remove null elements for (var k = this.allCircles.length; k >= 0; k--) { if (this.allCircles[k] === null) this.allCircles.splice(k, 1); } }, initialize : function(overrides) { if (overrides) { for (var i in overrides) { this[i] = overrides[i]; } } return this; } }; })();/** * See LICENSE file. * **/ (function() { /** * Local storage management. * @constructor */ CAAT.modules.LocalStorage= function() { return this; }; CAAT.modules.LocalStorage.prototype= { /** * Stores an object in local storage. The data will be saved as JSON.stringify. * @param key {string} key to store data under. * @param data {object} an object. * @return this * * @static */ save : function( key, data ) { try { localStorage.setItem( key, JSON.stringify(data) ); } catch(e) { // eat it } return this; }, /** * Retrieve a value from local storage. * @param key {string} the key to retrieve. * @return {object} object stored under the key parameter. * * @static */ load : function( key ) { try { return JSON.parse(localStorage.getItem( key )); } catch(e) { return null; } }, /** * Removes a value stored in local storage. * @param key {string} * @return this * * @static */ remove : function( key ) { try { localStorage.removeItem(key); } catch(e) { // eat it } return this; } }; })(); /** * See LICENSE file. */ (function() { CAAT.modules.ImageUtil= {}; CAAT.modules.ImageUtil.createAlphaSpriteSheet= function(maxAlpha, minAlpha, sheetSize, image, bg_fill_style ) { if ( maxAlpha, bottom, left }�} */ CAAT.modules.ImageUtil.optimize= function(image, threshold, areas ) { threshold>>=0; var atop= true; var abottom= true; var aleft= true; var aright= true; if ( typeof areas!=='undefined' ) { if ( typeof areas.top!=='undefined' ) { atop= areas.top; } if ( typeof areas.bottom!=='undefined' ) { abottom= areas.bottom; } if ( typeof areas.left!=='undefined' ) { aleft= areas.left; } if ( typeof areas.right!=='undefined' ) { aright= areas.right; } } var canvas= document.createElement('canvas'); canvas.width= image.width; canvas.height=image.height; var ctx= canvas.getContext('2d'); ctx.fillStyle='rgba(0,0,0,0)'; ctx.fillRect(0,0,image.width,image.height); ctx.drawImage( image, 0, 0 ); var imageData= ctx.getImageData(0,0,image.width,image.height); var data= imageData.data; var i,j; var miny= 0, maxy=canvas.height-1; var minx= 0, maxx=canvas.width-1; var alpha= false; if ( atop ) { for( i=0; ithreshold ) { alpha= true; break; } } if ( alpha ) { break; } } // i contiene el indice del ultimo scan que no es transparente total. miny= i; } if ( abottom ) { alpha= false; for( i=canvas.height-1; i>=miny; i-- ) { for( j=0; jthreshold ) { alpha= true; break; } } if ( alpha ) { break; } } maxy= i; } if ( aleft ) { alpha= false; for( j=0; jthreshold ) { alpha= true; break; } } if ( alpha ) { break; } } minx= j; } if ( aright ) { alpha= false; for( j=canvas.width-1; j>=minx; j-- ) { for( i=miny; i<=maxy; i++ ) { if ( data[i*canvas.width*4 + 3+j*4 ]>threshold ) { alpha= true; break; } } if ( alpha ) { break; } } maxx= j; } if ( 0===minx && 0===miny && canvas.width-1===maxx && canvas.height-1===maxy ) { return canvas; } var width= maxx-minx+1; var height=maxy-miny+1; var id2= ctx.getImageData( minx, miny, width, height ); canvas.width= width; canvas.height= height; ctx= canvas.getContext('2d'); ctx.putImageData( id2, 0, 0 ); return canvas; }; CAAT.modules.ImageUtil.createThumb= function(image, w, h, best_fit) { w= w||24; h= h||24; var canvas= document.createElement('canvas'); canvas.width= w; canvas.height= h; var ctx= canvas.getContext('2d'); if ( best_fit ) { var max= Math.max( image.width, image.height ); var ww= image.width/max*w; var hh= image.height/max*h; ctx.drawImage( image, (w-ww)/2,(h-hh)/2,ww,hh ); } else { ctx.drawImage( image, 0, 0, w, h ); } return canvas; } })();/** * See LICENSE file. */ (function() { CAAT.modules.LayoutUtils= {}; CAAT.modules.LayoutUtils.row= function( dst, what_to_layout_array, constraint_object ) { var width= dst.width; var x=0, y=0, i=0, l=0; var actor_max_h= -Number.MAX_VALUE, actor_max_w= Number.MAX_VALUE; // compute max/min actor list size. for( i=what_to_layout_array.length-1; i; i-=1 ) { if ( actor_max_w>0)+1 ) + 2 * padding ; charWidth.push(cw); textWidth+= cw; } canvas.width= textWidth; canvas.height= (this.fontSize*1.5)>>0; ctx= canvas.getContext('2d'); ctx.textBaseline= 'top'; ctx.font= this.fontStyle+' '+this.fontSize+""+this.fontSizeUnit+" "+ this.font; ctx.fillStyle= this.fillStyle; ctx.strokeStyle= this.strokeStyle; this.charMap= {}; x=0; for( i=0; i * If a call to any method of the form addTo is called before this calling * this method, they will assume to start at -1,-1 and probably you'll get the wrong path. * @param px0 {number} * @param py0 {number} * * @return this */ beginPath : function( px0, py0 ) { this.trackPathX= px0; this.trackPathY= py0; this.beginPathX= px0; this.beginPathY= py0; return this; }, /** * Close the path by adding a line path segment from the current last path * coordinate to startCurvePosition coordinate. *

    * This method closes a path by setting its last path segment's last control point * to be the first path segment's first control point. *

    * This method also sets the path as finished, and calculates all path's information * such as length and bounding box. * * @return this */ closePath : function() { this.getLastPathSegment().setPoint( this.getFirstPathSegment().startCurvePosition(), this.getLastPathSegment().numControlPoints()-1 ); this.trackPathX= this.beginPathX; this.trackPathY= this.beginPathY; this.endPath(); return this; }, /** * Finishes the process of building the path. It involves calculating each path segments length * and proportional length related to a normalized path length of 1. * It also sets current paths length. * These calculi are needed to traverse the path appropriately. *

    * This method must be called explicitly, except when closing a path (that is, calling the * method closePath) which calls this method as well. * * @return this */ endPath : function() { this.pathSegmentStartTime=[]; this.pathSegmentDurationTime= []; this.updatePath(); return this; }, /** * This method, returns a CAAT.Point instance indicating a coordinate in the path. * The returned coordinate is the corresponding to normalizing the path's length to 1, * and then finding what path segment and what coordinate in that path segment corresponds * for the input time parameter. *

    * The parameter time must be a value ranging 0..1. * If not constrained to these values, the parameter will be modulus 1, and then, if less * than 0, be normalized to 1+time, so that the value always ranges from 0 to 1. *

    * This method is needed when traversing the path throughout a CAAT.Interpolator instance. * * @param time a value between 0 and 1 both inclusive. 0 will return path's starting coordinate. * 1 will return path's end coordinate. * * @return {CAAT.Point} */ getPosition : function(time) { if ( time>1 || time<0 ) { time%=1; } if ( time<0 ) { time= 1+time; } /* var found= false; for( var i=0; i=psstv )*/ { l= m+1; } } return this.endCurvePosition(); }, /** * Analogously to the method getPosition, this method returns a CAAT.Point instance with * the coordinate on the path that corresponds to the given length. The input length is * related to path's length. * * @param iLength {number} a float with the target length. * @return {CAAT.Point} */ getPositionFromLength : function(iLength) { iLength%=this.getLength(); if (iLength<0 ) { iLength+= this.getLength(); } var accLength=0; for( var i=0; i0 ) { this.pathSegmentStartTime[i]= this.pathSegmentStartTime[i-1]+this.pathSegmentDurationTime[i-1]; } else { this.pathSegmentStartTime[0]= 0; } this.pathSegments[i].endPath(); } this.extractPathPoints(); if ( typeof callback!=='undefined' ) { callback(this); } return this; }, /** * Sent by a CAAT.PathActor instance object to try to drag a path's control point. * @param x {number} * @param y {number} */ press: function(x,y) { if (!this.interactive) { return; } var HS= CAAT.Curve.prototype.HANDLE_SIZE/2; for( var i=0; i=point.x-HS && y>=point.y-HS && x} */ setPoints : function( points ) { if ( this.points.length===points.length ) { for( var i=0; i=0 && index * This class pre-creates a canvas of the given dimensions and extracts an imageData object to * hold the pixel manipulation. * * @constructor */ CAAT.ImageProcessor= function() { return this; }; CAAT.ImageProcessor.prototype= { canvas: null, ctx: null, width: 0, height: 0, imageData: null, bufferImage:null, /** * Grabs an image pixels. * * @param image {HTMLImageElement} * @return {ImageData} returns an ImageData object with the image representation or null in * case the pixels can not be grabbed. * * @static */ grabPixels : function(image) { var canvas= document.createElement('canvas'); if ( canvas!==null ) { canvas.width= image.width; canvas.height= image.height; var ctx= canvas.getContext('2d'); ctx.drawImage(image,0,0); try { var imageData= ctx.getImageData(0,0,canvas.width,canvas.height); return imageData; } catch(e) { CAAT.log('error pixelgrabbing.', image); return null; } } return null; }, /** * Helper method to create an array. * * @param size {number} integer number of elements in the array. * @param initValue {number} initial array values. * * @return {[]} an array of 'initialValue' elements. * * @static */ makeArray : function(size, initValue) { var a= []; for(var i=0; i * This image processor creates a color ramp of 256 elements from the colors of the parameter 'colors'. * Be aware of color definition since the alpha values count to create the ramp. * * @param width {number} * @param height {number} * @param colors {Array.} an array of color values. * * @return this */ initialize : function(width,height,colors) { CAAT.IMPlasma.superclass.initialize.call(this,width,height); this.wavetable= []; for (var x=0; x<256; x++) { this.wavetable.push( Math.floor(32 * (1 + Math.cos(x*2 * Math.PI / 256))) ); } this.pos1=Math.floor(255*Math.random()); this.pos2=Math.floor(255*Math.random()); this.pos3=Math.floor(255*Math.random()); this.pos4=Math.floor(255*Math.random()); this.m_colorMap= CAAT.Color.prototype.makeRGBColorRamp( colors!==null ? colors : this.color, 256, CAAT.Color.prototype.RampEnumeration.RAMP_CHANNEL_RGBA_ARRAY ); this.setB(); return this; }, /** * Initialize internal plasma structures. Calling repeatedly this method will make the plasma * look different. */ setB : function() { this.b1= Math.random()>0.5; this.b2= Math.random()>0.5; this.b3= Math.random()>0.5; this.b4= Math.random()>0.5; this.spd1= Math.floor((Math.random()*3+1)*(Math.random()<0.5?1:-1)); this.spd2= Math.floor((Math.random()*3+1)*(Math.random()<0.5?1:-1)); this.spd3= Math.floor((Math.random()*3+1)*(Math.random()<0.5?1:-1)); this.spd4= Math.floor((Math.random()*3+1)*(Math.random()<0.5?1:-1)); this.i1= Math.floor((Math.random()*2.4+1)*(Math.random()<0.5?1:-1)); this.i2= Math.floor((Math.random()*2.4+1)*(Math.random()<0.5?1:-1)); this.i3= Math.floor((Math.random()*2.4+1)*(Math.random()<0.5?1:-1)); this.i4= Math.floor((Math.random()*2.4+1)*(Math.random()<0.5?1:-1)); }, /** * Apply image processing to create the plasma and call superclass's apply to make the result * visible. * @param director {CAAT.Director} * @param time {number} * * @return this */ apply : function(director,time) { var v = 0; this.tpos1 = this.pos1; this.tpos2 = this.pos2; var bi= this.bufferImage; var cm= this.m_colorMap; var wt= this.wavetable; var z; var cmz; for (var x=0; x=this.m_radius) { dx=this.m_radius-1; } if (dy>=this.m_radius) { dy=this.m_radius-1; } var c= this.phong[ dx ] [ dy ]; var r=0; var g=0; var b=0; if ( c>=0 ) {// oscurecer r= (this.m_lightcolor[k][0]*c/128); g= (this.m_lightcolor[k][1]*c/128); b= (this.m_lightcolor[k][2]*c/128); } else { // blanquear. c=128+c; var rr= (this.m_lightcolor[k][0]); var gg= (this.m_lightcolor[k][1]); var bb= (this.m_lightcolor[k][2]); r= Math.floor(rr+ (255-rr)*c/128); g= Math.floor(gg+ (255-gg)*c/128); b= Math.floor(bb+ (255-bb)*c/128); } rrr+=r; ggg+=g; bbb+=b; } if ( rrr>255 ) { rrr=255; } if ( ggg>255 ) { ggg=255; } if ( bbb>255 ) { bbb=255; } var pos= (j+this.m_tt[i])*4; dstPixels[pos ]= rrr; dstPixels[pos+1]= ggg; dstPixels[pos+2]= bbb; dstPixels[pos+3]= 255; } } }, /** * Sets lights color. * @param colors_rgb_array an array of arrays. Each internal array has three integers defining an RGB color. * ie: * [ * [ 255,0,0 ], * [ 0,255,0 ] * ] * @return this */ setLightColors : function( colors_rgb_array ) { this.m_lightcolor= colors_rgb_array; this.lightPosition= []; for( var i=0; i> 1); break; case 2: hh = this.height - 1; break; } switch (this.m_alignv) { case 0: ww = 0; break; case 1: ww = (this.width >> 1); break; case 2: ww = this.width - 1; break; } var i = (((this.width >> 1) << 8) - ddx * ww + ddy * hh)&0xffff; var j = (((this.height >> 1) << 8) - ddy * ww - ddx * hh) & 0xffff; var srcwidth= this.sourceImageData.width; var srcheight= this.sourceImageData.height; var srcdata= this.sourceImageData.data; var bi= this.bufferImage; var dstoff; var addx; var addy; while (off < this.width * this.height * 4) { addx = i; addy = j; for (var m = 0; m < this.width; m++) { dstoff = ((addy >> this.shift) & this.mask) * srcwidth + ((addx >> this.shift) & this.mask); dstoff <<= 2; bi[ off++ ] = srcdata[ dstoff++ ]; bi[ off++ ] = srcdata[ dstoff++ ]; bi[ off++ ] = srcdata[ dstoff++ ]; bi[ off++ ] = srcdata[ dstoff++ ]; addx += ddx; addy += ddy; } dist += this.distortion; i -= ddy; j += ddx - dist; } }, /** * Perform and apply the rotozoom effect. * @param director {CAAT.Director} * @param time {number} * @return this */ apply : function(director,time) { if ( null!==this.sourceImageData ) { this.rotoZoom(director,time); } return CAAT.IMRotoZoom.superclass.apply.call(this,director,time); }, /** * Change the effect's rotation anchor. Call this method repeatedly to make the effect look * different. */ setCenter: function() { var d = Math.random(); if (d < 0.33) { this.m_alignv = 0; } else if (d < 0.66) { this.m_alignv = 1; } else { this.m_alignv = 2; } d = Math.random(); if (d < 0.33) { this.m_alignh = 0; } else if (d < 0.66) { this.m_alignh = 1; } else { this.m_alignh = 2; } } }; extend( CAAT.IMRotoZoom, CAAT.ImageProcessor, null); })();/** * See LICENSE file. */ (function() { CAAT.Program= function(gl) { this.gl= gl; return this; }; CAAT.Program.prototype= { shaderProgram: null, gl: null, /** * Set fragment shader's alpha composite value. * @param alpha {number} float value 0..1. */ setAlpha : function( alpha ) { }, getShader : function (gl,type,str) { var shader; if (type === "x-shader/x-fragment") { shader = gl.createShader(gl.FRAGMENT_SHADER); } else if (type === "x-shader/x-vertex") { shader = gl.createShader(gl.VERTEX_SHADER); } else { return null; } gl.shaderSource(shader, str); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert(gl.getShaderInfoLog(shader)); return null; } return shader; }, getDomShader : function(gl, id) { var shaderScript = document.getElementById(id); if (!shaderScript) { return null; } var str = ""; var k = shaderScript.firstChild; while (k) { if (k.nodeType === 3) { str += k.textContent; } k = k.nextSibling; } var shader; if (shaderScript.type === "x-shader/x-fragment") { shader = gl.createShader(gl.FRAGMENT_SHADER); } else if (shaderScript.type === "x-shader/x-vertex") { shader = gl.createShader(gl.VERTEX_SHADER); } else { return null; } gl.shaderSource(shader, str); gl.compileShader(shader); if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert(gl.getShaderInfoLog(shader)); return null; } return shader; }, initialize : function() { return this; }, getFragmentShader : function() { return null; }, getVertexShader : function() { return null; }, create : function() { var gl= this.gl; this.shaderProgram = gl.createProgram(); gl.attachShader(this.shaderProgram, this.getVertexShader()); gl.attachShader(this.shaderProgram, this.getFragmentShader()); gl.linkProgram(this.shaderProgram); gl.useProgram(this.shaderProgram); return this; }, setMatrixUniform : function( caatMatrix4 ) { this.gl.uniformMatrix4fv( this.shaderProgram.pMatrixUniform, false, new Float32Array(caatMatrix4.flatten())); }, useProgram : function() { this.gl.useProgram(this.shaderProgram); return this; } }; })(); (function() { CAAT.ColorProgram= function(gl) { CAAT.ColorProgram.superclass.constructor.call(this,gl); return this; }; CAAT.ColorProgram.prototype= { colorBuffer: null, vertexPositionBuffer: null, vertexPositionArray: null, getFragmentShader : function() { return this.getShader(this.gl, "x-shader/x-fragment", "#ifdef GL_ES \n"+ "precision highp float; \n"+ "#endif \n"+ "varying vec4 color; \n"+ "void main(void) { \n"+ " gl_FragColor = color;\n"+ "}\n" ); }, getVertexShader : function() { return this.getShader(this.gl, "x-shader/x-vertex", "attribute vec3 aVertexPosition; \n"+ "attribute vec4 aColor; \n"+ "uniform mat4 uPMatrix; \n"+ "varying vec4 color; \n"+ "void main(void) { \n"+ "gl_Position = uPMatrix * vec4(aVertexPosition, 1.0); \n"+ "color= aColor; \n"+ "}\n" ); }, initialize : function() { this.shaderProgram.vertexPositionAttribute = this.gl.getAttribLocation(this.shaderProgram, "aVertexPosition"); this.gl.enableVertexAttribArray( this.shaderProgram.vertexPositionAttribute); this.shaderProgram.vertexColorAttribute = this.gl.getAttribLocation(this.shaderProgram, "aColor"); this.gl.enableVertexAttribArray( this.shaderProgram.vertexColorAttribute); this.shaderProgram.pMatrixUniform = this.gl.getUniformLocation(this.shaderProgram, "uPMatrix"); this.useProgram(); this.colorBuffer= this.gl.createBuffer(); this.setColor( [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1] ); var maxTris=512, i; /// set vertex data this.vertexPositionBuffer = this.gl.createBuffer(); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexPositionBuffer ); this.vertexPositionArray= new Float32Array(maxTris*12); this.gl.bufferData(this.gl.ARRAY_BUFFER, this.vertexPositionArray, this.gl.DYNAMIC_DRAW); this.gl.vertexAttribPointer(this.shaderProgram.vertexPositionAttribute, 3, this.gl.FLOAT, false, 0, 0); return CAAT.ColorProgram.superclass.initialize.call(this); }, setColor : function( colorArray ) { this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.colorBuffer ); this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(colorArray), this.gl.STATIC_DRAW); this.gl.vertexAttribPointer( this.shaderProgram.vertexColorAttribute, this.colorBuffer, this.gl.FLOAT, false, 0, 0); } }; extend(CAAT.ColorProgram, CAAT.Program, null ); })(); (function() { CAAT.TextureProgram= function(gl) { CAAT.TextureProgram.superclass.constructor.call(this,gl); return this; }; CAAT.TextureProgram.prototype= { vertexPositionBuffer: null, vertexPositionArray: null, vertexUVBuffer: null, vertexUVArray: null, vertexIndexBuffer: null, linesBuffer: null, prevAlpha: -1, prevR: -1, prevG: -1, prevB: -1, prevA: -1, prevTexture: null, getFragmentShader : function() { return this.getShader( this.gl, "x-shader/x-fragment", "#ifdef GL_ES \n"+ "precision highp float; \n"+ "#endif \n"+ "varying vec2 vTextureCoord; \n"+ "uniform sampler2D uSampler; \n"+ "uniform float alpha; \n"+ "uniform bool uUseColor;\n"+ "uniform vec4 uColor;\n"+ "void main(void) { \n"+ "if ( uUseColor ) {\n"+ " gl_FragColor= vec4(uColor.r*alpha, uColor.g*alpha, uColor.b*alpha, uColor.a*alpha);\n"+ "} else { \n"+ " vec4 textureColor= texture2D(uSampler, vec2(vTextureCoord)); \n"+ // Fix FF " gl_FragColor = vec4(textureColor.rgb, textureColor.a * alpha); \n"+ " gl_FragColor = vec4(textureColor.r*alpha, textureColor.g*alpha, textureColor.b*alpha, textureColor.a * alpha ); \n"+ "}\n"+ "}\n" ); }, getVertexShader : function() { return this.getShader(this.gl, "x-shader/x-vertex", "attribute vec3 aVertexPosition; \n"+ "attribute vec2 aTextureCoord; \n"+ "uniform mat4 uPMatrix; \n"+ "varying vec2 vTextureCoord; \n"+ "void main(void) { \n"+ "gl_Position = uPMatrix * vec4(aVertexPosition, 1.0); \n"+ "vTextureCoord = aTextureCoord;\n"+ "}\n" ); }, useProgram : function() { CAAT.TextureProgram.superclass.useProgram.call(this); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexPositionBuffer ); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexUVBuffer); this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.vertexIndexBuffer); }, initialize : function() { var i; this.linesBuffer= this.gl.createBuffer(); this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.linesBuffer ); var arr= []; for( i=0; i<1024; i++ ) { arr[i]= i; } this.linesBufferArray= new Uint16Array(arr); this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, this.linesBufferArray, this.gl.DYNAMIC_DRAW); this.shaderProgram.vertexPositionAttribute = this.gl.getAttribLocation(this.shaderProgram, "aVertexPosition"); this.gl.enableVertexAttribArray( this.shaderProgram.vertexPositionAttribute); this.shaderProgram.textureCoordAttribute = this.gl.getAttribLocation(this.shaderProgram, "aTextureCoord"); this.gl.enableVertexAttribArray( this.shaderProgram.textureCoordAttribute); this.shaderProgram.pMatrixUniform = this.gl.getUniformLocation(this.shaderProgram, "uPMatrix"); this.shaderProgram.samplerUniform = this.gl.getUniformLocation(this.shaderProgram, "uSampler"); this.shaderProgram.alphaUniform = this.gl.getUniformLocation(this.shaderProgram, "alpha"); this.shaderProgram.useColor = this.gl.getUniformLocation(this.shaderProgram, "uUseColor"); this.shaderProgram.color = this.gl.getUniformLocation(this.shaderProgram, "uColor"); this.setAlpha(1); this.setUseColor(false); var maxTris=4096; /// set vertex data this.vertexPositionBuffer = this.gl.createBuffer(); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexPositionBuffer ); this.vertexPositionArray= new Float32Array(maxTris*12); this.gl.bufferData(this.gl.ARRAY_BUFFER, this.vertexPositionArray, this.gl.DYNAMIC_DRAW); this.gl.vertexAttribPointer(this.shaderProgram.vertexPositionAttribute, 3, this.gl.FLOAT, false, 0, 0); // uv info this.vertexUVBuffer= this.gl.createBuffer(); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexUVBuffer); this.vertexUVArray= new Float32Array(maxTris*8); this.gl.bufferData(this.gl.ARRAY_BUFFER, this.vertexUVArray, this.gl.DYNAMIC_DRAW); this.gl.vertexAttribPointer(this.shaderProgram.textureCoordAttribute, 2, this.gl.FLOAT, false, 0, 0); // vertex index this.vertexIndexBuffer = this.gl.createBuffer(); this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.vertexIndexBuffer); var vertexIndex = []; for( i=0; i0 ) { this.freeChunks.splice( i++,0,{position: lp, size:ls} ); } if ( rs>0 ) { this.freeChunks.splice( i,0,{position: rp, size:rs} ); } return true; } } return false; }, log : function(index) { if ( 0===this.freeChunks.length ) { CAAT.log('index '+index+' empty'); } else { var str='index '+index; for( var i=0; ithis.width||height>this.height) { return null; } // find first fitting point var i,j,initialPosition= 0; while( initialPosition<=this.scanMapHeight-height) { // para buscar sitio se buscara un sitio hasta el tamano de alto del trozo. // mas abajo no va a caber. // fitHorizontalPosition es un array con todas las posiciones de este scan donde // cabe un chunk de tamano width. var fitHorizontalPositions= null; var foundPositionOnScan= false; for( ; initialPosition<=this.scanMapHeight-height; initialPosition++ ) { fitHorizontalPositions= this.scanMap[ initialPosition ].findWhereFits( width ); // si no es nulo el array de resultados, quiere decir que en alguno de los puntos // nos cabe un trozo de tamano width. if ( null!==fitHorizontalPositions && fitHorizontalPositions.length>0 ) { foundPositionOnScan= true; break; } } if ( foundPositionOnScan ) { // j es el scan donde cabe un trozo de tamano width. // comprobamos desde este scan que en todos los scan verticales cabe el trozo. // se comprueba que cabe en alguno de los tamanos que la rutina de busqueda horizontal // nos ha devuelto antes. var minInitialPosition=Number.MAX_VALUE; for( j=0; jb.width ? -1 : 0; } else if ( me.criteria==='height' ) { return a.heightb.height ? -1 : 0; } return aareabarea ? -1 : 0; }); for( i=0; i