diff --git a/.gitattributes b/.gitattributes index f003c48..f43a7f4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,3 @@ -build/grapher.js binary -build/grapher-min.js binary +build/* binary doc/**/* binary + diff --git a/LICENSE b/LICENSE index 985e590..63837e7 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ Apache License same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2014 Ayasdi, Inc. + Copyright 2015 Ayasdi, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Makefile b/Makefile index 87f29fb..6d48f3e 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ else isbranch = no endif -all: grapher.js doc +all: grapher.js grapher-min.js doc test: grapher.js @grunt jasmine @@ -16,9 +16,12 @@ doc: grapher.js @groc grapher.js: - @npm run build + @npm run grapher.js -gh-pages: grapher.js doc +grapher-min.js: grapher.js + @npm run grapher-min.js + +gh-pages: grapher.js grapher-min.js doc # create a temporary branch and commit any changes @git checkout -b temp-$(commit) diff --git a/README.md b/README.md index de1cec3..aed9600 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,36 @@ -Ayasdi.Grapher +Grapher ============== -WebGL/Canvas Network Graphing using PIXI.js. - +Draw network graphs using WebGL and Canvas backup. Download -------- -Grapher comes bundled with PIXI.js. -* [bundled](http://ayasdi.github.io/grapher/build/grapher.js) -* [bundled-min](http://ayasdi.github.io/grapher/build/grapher-min.js) +* [grapher](http://ayasdi.github.io/grapher/build/grapher.js) +* [grapher-min](http://ayasdi.github.io/grapher/build/grapher-min.js) + +Installing +---------- + +You can import grapher in a script tag, or build grapher into your application +using [Duo](http://duojs.org/): + + var Grapher = require('ayasdi/grapher'); + +Additional Modules +------------------ -An unbundled version can be found in the modules folder. -This version is useful if you are already using some of the dependencies or -are using a package management system. +Need a specific feature? Create your own modules for Grapher! +Here are some modules we've made: +* [center](https://github.com/ayasdi/grapher-center): + Helpful functions for centering the graph. +* [palette](https://github.com/ayasdi/grapher-palette): + Set up custom palettes and set colors by palette indices. +* [target](https://github.com/ayasdi/grapher-target): + Target a node or link, or find the nearest nodes and links. +* [zoom](https://github.com/ayasdi/grapher-zoom): + Zoom adjusts Grapher's scale by ratio. Optionally target a point as the zoom center. Examples -------- diff --git a/build/grapher-min.js b/build/grapher-min.js index ab47456..34af84c 100644 --- a/build/grapher-min.js +++ b/build/grapher-min.js @@ -1,13 +1,4 @@ -// Ayasdi Inc. Copyright 2014 +// Ayasdi Inc. 2015 // Grapher.js may be freely distributed under the Apache 2.0 license. -(function outer(modules,cache,entries){var global=function(){return this}();function require(name,jumped){if(cache[name])return cache[name].exports;if(modules[name])return call(name,require);throw new Error('cannot find module "'+name+'"')}function call(id,require){var m=cache[id]={exports:{}};var mod=modules[id];var name=mod[2];var fn=mod[0];fn.call(m.exports,function(req){var dep=modules[id][1][req];return require(dep?dep:req)},m,m.exports,outer,modules,cache,entries);if(name)cache[name]=cache[id];return cache[id].exports}for(var id in entries){if(entries[id]){global[entries[id]]=require(id)}else{require(id)}}require.duo=true;require.cache=cache;require.modules=modules;return require})({1:[function(require,module,exports){var Grapher=require("./modules/grapher.js");if(module&&module.exports)module.exports=Grapher;if(typeof Ayasdi==="undefined")Ayasdi={};Ayasdi.Grapher=Grapher},{"./modules/grapher.js":2}],2:[function(require,module,exports){(function(){var PIXI=require("./vendor/pixi.js"),Color=require("./color.js"),u=require("./utilities.js");PIXI.dontSayHello=true;function Grapher(){this.initialize.apply(this,arguments);return this}if(module&&module.exports)module.exports=Grapher;Grapher.prototype={};Grapher.prototype.initialize=function(width,height,o){this.props=u.extend({lineWidth:2,foregroundColor:2236962,backgroundColor:16777215,antialias:true,resolution:typeof devicePixelRatio!=="undefined"?Math.max(devicePixelRatio,1):1},o);this.width=width;this.height=height;this.rendered=false;this.renderer=PIXI.autoDetectRenderer(width,height,this.props);this.view=this.renderer.view;this.stage=new PIXI.Stage(this.backgroundColor());this.network=new PIXI.DisplayObjectContainer;this.stage.addChild(this.network);this.batches={};this.batches.nodes={};this.batches.links={};this.links=[];this.nodes=[];this.willUpdate={};this.updateAll={};this._clearUpdateQueue();this.center();this.hasModifiedTransform=false;this._updateLink=u.bind(this._updateLink,this);this._updateNode=u.bind(this._updateNode,this);this._updateLinkByIndex=u.bind(this._updateLinkByIndex,this);this._updateNodeByIndex=u.bind(this._updateNodeByIndex,this);this.animate=u.bind(this.animate,this);this.listeners={};this.stage.interactive=true;this.stage.mousedown=this._onEvent("mousedown");this.stage.mousemove=this._onEvent("mousemove");this.stage.mouseup=this._onEvent("mouseup");this.view.addEventListener("webglcontextlost",u.bind(this._onContextLost,this));this.view.addEventListener("webglcontextrestored",u.bind(this._onContextRestored,this));u.eachKey(o,this.set,this)};Grapher.prototype.set=function(val,key){var setter=this[key];if(setter&&u.isFunction(setter))return setter.call(this,val)};Grapher.prototype.on=function(event,fn){if(u.isFunction(fn))this.listeners[event]=fn;return this};Grapher.prototype.off=function(event){if(event in this.listeners)this.listeners[event]=u.noop;return this};Grapher.prototype.palette=function(name){if(u.isUndefined(name))return this.props.palette;this.props.palette=Grapher.getPalette(name);this.update();return this};Grapher.prototype.data=function(data){if(u.isUndefined(data))return this.props.data;this.props.data=data;this.exit();this.enter();this.update();if(!this.hasModifiedTransform)this.center();return this};Grapher.prototype.enter=function(){var data=this.data(),entering=[];if(this.links.lengthmaxX)maxX=nodes[i].x;if(nodes[i].ymaxY)maxY=nodes[i].y}var dX=maxX-minX,dY=maxY-minY;scale=Math.min(width/dX,height/dY,2)/pad;x=(width-dX*scale)/2-minX*scale;y=(height-dY*scale)/2-minY*scale}return this.scale(scale).translate([x,y])};Grapher.prototype.transform=function(transform){if(u.isUndefined(transform))return{scale:this.props.scale,translate:this.props.translate};this.scale(transform.scale);this.translate(transform.translate);return this};Grapher.prototype.scale=function(scale){if(u.isUndefined(scale))return this.props.scale;if(u.isNumber(scale))this.props.scale=scale;this.updateTransform=true;this.hasModifiedTransform=true;return this};Grapher.prototype.translate=function(translate){if(u.isUndefined(translate))return this.props.translate;if(u.isArray(translate))this.props.translate=translate;this.updateTransform=true;this.hasModifiedTransform=true;return this};Grapher.prototype.backgroundColor=function(color){if(u.isUndefined(color))return this.props.backgroundColor;this.props.backgroundColor=Color.parse(color);this.stage.setBackgroundColor(this.props.backgroundColor);return this};Grapher.prototype.foregroundColor=function(color){if(u.isUndefined(color))return this.props.foregroundColor;this.props.foregroundColor=Color.parse(color);return this};Grapher.prototype.lineWidth=function(size){if(u.isUndefined(size))return this.props.lineWidth;this.props.lineWidth=size;return this};Grapher.prototype.getNodeIdAt=function(point){var node=-1,x=point.x,y=point.y;this.nodes.every(function(n,i){var inX=x<=n.position.x+n.width&&x>=n.position.x,inY=y<=n.position.y+n.height&&y>=n.position.y,found=inX&&inY;if(found)node=i;return!found});return node};Grapher.prototype._exit=function(sprite){return sprite.parent.removeChild(sprite)};Grapher.prototype._enter=function(data){var type=u.isUndefined(data.from)?NODES:LINKS,spriteSet=type===NODES?this.nodes:this.links;sprite=new PIXI.Sprite(Grapher.getTexture(type,this.foregroundColor()));spriteSet.push(sprite)};Grapher.prototype._addToUpdateQueue=function(type,indices){var willUpdate=type===NODES?this.willUpdate.nodes:this.willUpdate.links,updateAll=type===NODES?this.updateAll.nodes:this.updateAll.links,spriteSet=type===NODES?this.nodes:this.links;var insert=function(n){u.uniqueInsert(willUpdate,n)};if(!updateAll&&u.isArray(indices))u.each(indices,insert,this);updateAll=updateAll||willUpdate.length>=spriteSet.length;if(type===NODES)this.updateAll.nodes=updateAll;else this.updateAll.links=updateAll};Grapher.prototype._clearUpdateQueue=function(){this.willUpdate.links=[];this.willUpdate.nodes=[];this.updateAll.links=false;this.updateAll.nodes=false;this.updateTransform=false};Grapher.prototype._update=function(){var updatingLinks=this.willUpdate.links,updatingNodes=this.willUpdate.nodes,i;if(this.updateAll.links)u.each(this.links,this._updateLink);else if(updatingLinks&&updatingLinks.length)u.eachPop(updatingLinks,this._updateLinkByIndex);if(this.updateAll.nodes)u.each(this.nodes,this._updateNode);else if(updatingNodes&&updatingNodes.length)u.eachPop(updatingNodes,this._updateNodeByIndex);if(this.updateTransform){this.network.scale.set(this.props.scale);this.network.position.set.apply(this.network,this.props.translate)}this._clearUpdateQueue()};Grapher.prototype._updateLink=function(link,i){var data=this.data(),lw=this.lineWidth(),l=data.links[i],from=data.nodes[l.from],to=data.nodes[l.to],leftMost=from.x<=to.x?from:to;link.width=Math.sqrt(Math.pow(to.x-from.x,2)+Math.pow(to.y-from.y,2));link.height=lw;link.position.set(leftMost.x,leftMost.y-lw/2);link.pivot.set(0,lw/2);link.rotation=Math.atan((to.y-from.y)/(to.x-from.x));var color=!u.isUndefined(l.color)?this._findColor(l.color):Color.interpolate(this._findColor(from.color),this._findColor(to.color));this._setColor(LINKS,link,color)};Grapher.prototype._updateNode=function(node,i){var n=this.data().nodes[i];node.width=n.r*2;node.height=n.r*2;node.position.set(n.x-n.r,n.y-n.r);this._setColor(NODES,node,this._findColor(n.color))};Grapher.prototype._updateNodeByIndex=function(i){this._updateNode(this.nodes[i],i)};Grapher.prototype._updateLinkByIndex=function(i){this._updateLink(this.links[i],i)};var isLinked=function(indices,l){var i,len=indices.length,flag=false;for(i=0;i-1){var args=["%c %c %c Pixi.js "+PIXI.VERSION+" - "+type+" %c "+" %c "+" http://www.pixijs.com/ %c %c ♥%c♥%c♥ ","background: #ff66a5","background: #ff66a5","color: #ff66a5; background: #030307;","background: #ff66a5","background: #ffc3dc","background: #ff66a5","color: #ff2424; background: #fff","color: #ff2424; background: #fff","color: #ff2424; background: #fff"];console.log.apply(console,args)}else if(window["console"]){console.log("Pixi.js "+PIXI.VERSION+" - http://www.pixijs.com/")}PIXI.dontSayHello=true};PIXI.Point=function(x,y){this.x=x||0;this.y=y||0};PIXI.Point.prototype.clone=function(){return new PIXI.Point(this.x,this.y)};PIXI.Point.prototype.set=function(x,y){this.x=x||0;this.y=y||(y!==0?this.x:0)};PIXI.Point.prototype.constructor=PIXI.Point;PIXI.Rectangle=function(x,y,width,height){this.x=x||0;this.y=y||0;this.width=width||0;this.height=height||0};PIXI.Rectangle.prototype.clone=function(){return new PIXI.Rectangle(this.x,this.y,this.width,this.height)};PIXI.Rectangle.prototype.contains=function(x,y){if(this.width<=0||this.height<=0)return false;var x1=this.x;if(x>=x1&&x<=x1+this.width){var y1=this.y;if(y>=y1&&y<=y1+this.height){return true}}return false};PIXI.Rectangle.prototype.constructor=PIXI.Rectangle;PIXI.EmptyRectangle=new PIXI.Rectangle(0,0,0,0);PIXI.Polygon=function(points){if(!(points instanceof Array))points=Array.prototype.slice.call(arguments);if(points[0]instanceof PIXI.Point){var p=[];for(var i=0,il=points.length;iy!==yj>y&&x<(xj-xi)*(y-yi)/(yj-yi)+xi;if(intersect)inside=!inside}return inside};PIXI.Polygon.prototype.constructor=PIXI.Polygon;PIXI.Circle=function(x,y,radius){this.x=x||0;this.y=y||0;this.radius=radius||0};PIXI.Circle.prototype.clone=function(){return new PIXI.Circle(this.x,this.y,this.radius)};PIXI.Circle.prototype.contains=function(x,y){if(this.radius<=0)return false;var dx=this.x-x,dy=this.y-y,r2=this.radius*this.radius;dx*=dx;dy*=dy;return dx+dy<=r2};PIXI.Circle.prototype.getBounds=function(){return new PIXI.Rectangle(this.x-this.radius,this.y-this.radius,this.radius*2,this.radius*2)};PIXI.Circle.prototype.constructor=PIXI.Circle;PIXI.Ellipse=function(x,y,width,height){this.x=x||0;this.y=y||0;this.width=width||0;this.height=height||0};PIXI.Ellipse.prototype.clone=function(){return new PIXI.Ellipse(this.x,this.y,this.width,this.height)};PIXI.Ellipse.prototype.contains=function(x,y){if(this.width<=0||this.height<=0)return false;var normx=(x-this.x)/this.width,normy=(y-this.y)/this.height;normx*=normx;normy*=normy;return normx+normy<=1};PIXI.Ellipse.prototype.getBounds=function(){return new PIXI.Rectangle(this.x-this.width,this.y-this.height,this.width,this.height)};PIXI.Ellipse.prototype.constructor=PIXI.Ellipse;PIXI.RoundedRectangle=function(x,y,width,height,radius){this.x=x||0;this.y=y||0;this.width=width||0;this.height=height||0;this.radius=radius||20};PIXI.RoundedRectangle.prototype.clone=function(){return new PIXI.RoundedRectangle(this.x,this.y,this.width,this.height,this.radius)};PIXI.RoundedRectangle.prototype.contains=function(x,y){if(this.width<=0||this.height<=0)return false;var x1=this.x;if(x>=x1&&x<=x1+this.width){var y1=this.y;if(y>=y1&&y<=y1+this.height){return true}}return false};PIXI.RoundedRectangle.prototype.constructor=PIXI.RoundedRectangle;PIXI.Matrix=function(){this.a=1;this.b=0;this.c=0;this.d=1;this.tx=0;this.ty=0};PIXI.Matrix.prototype.fromArray=function(array){this.a=array[0];this.b=array[1];this.c=array[3];this.d=array[4];this.tx=array[2];this.ty=array[5]};PIXI.Matrix.prototype.toArray=function(transpose){if(!this.array)this.array=new PIXI.Float32Array(9);var array=this.array;if(transpose){array[0]=this.a;array[1]=this.b;array[2]=0;array[3]=this.c;array[4]=this.d;array[5]=0;array[6]=this.tx;array[7]=this.ty;array[8]=1}else{array[0]=this.a;array[1]=this.c;array[2]=this.tx;array[3]=this.b;array[4]=this.d;array[5]=this.ty;array[6]=0;array[7]=0;array[8]=1}return array};PIXI.Matrix.prototype.apply=function(pos,newPos){newPos=newPos||new PIXI.Point;newPos.x=this.a*pos.x+this.c*pos.y+this.tx;newPos.y=this.b*pos.x+this.d*pos.y+this.ty;return newPos};PIXI.Matrix.prototype.applyInverse=function(pos,newPos){newPos=newPos||new PIXI.Point;var id=1/(this.a*this.d+this.c*-this.b);newPos.x=this.d*id*pos.x+-this.c*id*pos.y+(this.ty*this.c-this.tx*this.d)*id;newPos.y=this.a*id*pos.y+-this.b*id*pos.x+(-this.ty*this.a+this.tx*this.b)*id;return newPos};PIXI.Matrix.prototype.translate=function(x,y){this.tx+=x;this.ty+=y;return this};PIXI.Matrix.prototype.scale=function(x,y){this.a*=x;this.d*=y;this.c*=x;this.b*=y;this.tx*=x;this.ty*=y;return this};PIXI.Matrix.prototype.rotate=function(angle){var cos=Math.cos(angle);var sin=Math.sin(angle);var a1=this.a;var c1=this.c;var tx1=this.tx;this.a=a1*cos-this.b*sin;this.b=a1*sin+this.b*cos;this.c=c1*cos-this.d*sin;this.d=c1*sin+this.d*cos;this.tx=tx1*cos-this.ty*sin;this.ty=tx1*sin+this.ty*cos;return this};PIXI.Matrix.prototype.append=function(matrix){var a1=this.a;var b1=this.b;var c1=this.c;var d1=this.d;this.a=matrix.a*a1+matrix.b*c1;this.b=matrix.a*b1+matrix.b*d1;this.c=matrix.c*a1+matrix.d*c1;this.d=matrix.c*b1+matrix.d*d1;this.tx=matrix.tx*a1+matrix.ty*c1+this.tx;this.ty=matrix.tx*b1+matrix.ty*d1+this.ty;return this};PIXI.Matrix.prototype.identity=function(){this.a=1;this.b=0;this.c=0;this.d=1;this.tx=0;this.ty=0;return this};PIXI.identityMatrix=new PIXI.Matrix;PIXI.DisplayObject=function(){this.position=new PIXI.Point;this.scale=new PIXI.Point(1,1);this.pivot=new PIXI.Point(0,0);this.rotation=0;this.alpha=1;this.visible=true;this.hitArea=null;this.buttonMode=false;this.renderable=false;this.parent=null;this.stage=null;this.worldAlpha=1;this._interactive=false;this.defaultCursor="pointer";this.worldTransform=new PIXI.Matrix;this._sr=0;this._cr=1;this.filterArea=null;this._bounds=new PIXI.Rectangle(0,0,1,1);this._currentBounds=null;this._mask=null;this._cacheAsBitmap=false;this._cacheIsDirty=false};PIXI.DisplayObject.prototype.constructor=PIXI.DisplayObject;Object.defineProperty(PIXI.DisplayObject.prototype,"interactive",{get:function(){return this._interactive},set:function(value){this._interactive=value;if(this.stage)this.stage.dirty=true}});Object.defineProperty(PIXI.DisplayObject.prototype,"worldVisible",{get:function(){var item=this;do{if(!item.visible)return false;item=item.parent}while(item);return true}});Object.defineProperty(PIXI.DisplayObject.prototype,"mask",{get:function(){return this._mask},set:function(value){if(this._mask)this._mask.isMask=false;this._mask=value;if(this._mask)this._mask.isMask=true}});Object.defineProperty(PIXI.DisplayObject.prototype,"filters",{get:function(){return this._filters},set:function(value){if(value){var passes=[];for(var i=0;i=0&&index<=this.children.length){if(child.parent){child.parent.removeChild(child)}child.parent=this;this.children.splice(index,0,child);if(this.stage)child.setStageReference(this.stage);return child}else{throw new Error(child+"addChildAt: The index "+index+" supplied is out of bounds "+this.children.length)}};PIXI.DisplayObjectContainer.prototype.swapChildren=function(child,child2){if(child===child2){return}var index1=this.getChildIndex(child);var index2=this.getChildIndex(child2);if(index1<0||index2<0){throw new Error("swapChildren: Both the supplied DisplayObjects must be a child of the caller.")}this.children[index1]=child2;this.children[index2]=child};PIXI.DisplayObjectContainer.prototype.getChildIndex=function(child){var index=this.children.indexOf(child);if(index===-1){throw new Error("The supplied DisplayObject must be a child of the caller")}return index};PIXI.DisplayObjectContainer.prototype.setChildIndex=function(child,index){if(index<0||index>=this.children.length){throw new Error("The supplied index is out of bounds")}var currentIndex=this.getChildIndex(child);this.children.splice(currentIndex,1);this.children.splice(index,0,child)};PIXI.DisplayObjectContainer.prototype.getChildAt=function(index){if(index<0||index>=this.children.length){throw new Error("getChildAt: Supplied index "+index+" does not exist in the child list, or the supplied DisplayObject must be a child of the caller")}return this.children[index]};PIXI.DisplayObjectContainer.prototype.removeChild=function(child){var index=this.children.indexOf(child);if(index===-1)return;return this.removeChildAt(index)};PIXI.DisplayObjectContainer.prototype.removeChildAt=function(index){var child=this.getChildAt(index);if(this.stage)child.removeStageReference();child.parent=undefined;this.children.splice(index,1);return child};PIXI.DisplayObjectContainer.prototype.removeChildren=function(beginIndex,endIndex){var begin=beginIndex||0;var end=typeof endIndex==="number"?endIndex:this.children.length;var range=end-begin;if(range>0&&range<=end){var removed=this.children.splice(begin,range);for(var i=0;ichildMaxX?maxX:childMaxX;maxY=maxY>childMaxY?maxY:childMaxY}if(!childVisible)return PIXI.EmptyRectangle;var bounds=this._bounds;bounds.x=minX;bounds.y=minY;bounds.width=maxX-minX;bounds.height=maxY-minY;return bounds};PIXI.DisplayObjectContainer.prototype.getLocalBounds=function(){var matrixCache=this.worldTransform;this.worldTransform=PIXI.identityMatrix;for(var i=0,j=this.children.length;imaxX?x1:maxX;maxX=x2>maxX?x2:maxX;maxX=x3>maxX?x3:maxX;maxX=x4>maxX?x4:maxX;maxY=y1>maxY?y1:maxY;maxY=y2>maxY?y2:maxY;maxY=y3>maxY?y3:maxY;maxY=y4>maxY?y4:maxY}var bounds=this._bounds;bounds.x=minX;bounds.width=maxX-minX;bounds.y=minY;bounds.height=maxY-minY;this._currentBounds=bounds;return bounds};PIXI.Sprite.prototype._renderWebGL=function(renderSession){if(!this.visible||this.alpha<=0)return;var i,j;if(this._mask||this._filters){var spriteBatch=renderSession.spriteBatch;if(this._filters){spriteBatch.flush();renderSession.filterManager.pushFilter(this._filterBlock)}if(this._mask){spriteBatch.stop();renderSession.maskManager.pushMask(this.mask,renderSession);spriteBatch.start()}spriteBatch.render(this);for(i=0,j=this.children.length;i=this.textures.length){this.gotoAndStop(this.textures.length-1);if(this.onComplete){this.onComplete()}}};PIXI.MovieClip.fromFrames=function(frames){var textures=[];for(var i=0;ibaseline;i--){for(j=0;jspaceLeft){if(j>0){result+="\n"}result+=words[j];spaceLeft=this.style.wordWrapWidth-wordWidth}else{spaceLeft-=wordWidthWithSpace;result+=" "+words[j]}}if(i=2?parseInt(font[font.length-2],10):PIXI.BitmapText.fonts[this.fontName].size;this.dirty=true;this.tint=style.tint};PIXI.BitmapText.prototype.updateText=function(){var data=PIXI.BitmapText.fonts[this.fontName];var pos=new PIXI.Point;var prevCharCode=null;var chars=[];var maxLineWidth=0;var lineWidths=[];var line=0;var scale=this.fontSize/data.size;for(var i=0;ilenChars){var child=this.getChildAt(this.children.length-1);this._pool.push(child);this.removeChild(child)}this.textWidth=maxLineWidth*scale;this.textHeight=(pos.y+data.lineHeight)*scale};PIXI.BitmapText.prototype.updateTransform=function(){if(this.dirty){this.updateText();this.dirty=false}PIXI.DisplayObjectContainer.prototype.updateTransform.call(this)};PIXI.BitmapText.fonts={};PIXI.InteractionData=function(){this.global=new PIXI.Point;this.target=null;this.originalEvent=null};PIXI.InteractionData.prototype.getLocalPosition=function(displayObject,point){var worldTransform=displayObject.worldTransform;var global=this.global;var a00=worldTransform.a,a01=worldTransform.c,a02=worldTransform.tx,a10=worldTransform.b,a11=worldTransform.d,a12=worldTransform.ty,id=1/(a00*a11+a01*-a10);point=point||new PIXI.Point;point.x=a11*id*global.x+-a01*id*global.y+(a12*a01-a02*a11)*id;point.y=a00*id*global.y+-a10*id*global.x+(-a12*a00+a02*a10)*id;return point};PIXI.InteractionData.prototype.constructor=PIXI.InteractionData;PIXI.InteractionManager=function(stage){this.stage=stage;this.mouse=new PIXI.InteractionData;this.touches={};this.tempPoint=new PIXI.Point;this.mouseoverEnabled=true;this.pool=[];this.interactiveItems=[];this.interactionDOMElement=null;this.onMouseMove=this.onMouseMove.bind(this);this.onMouseDown=this.onMouseDown.bind(this);this.onMouseOut=this.onMouseOut.bind(this);this.onMouseUp=this.onMouseUp.bind(this);this.onTouchStart=this.onTouchStart.bind(this);this.onTouchEnd=this.onTouchEnd.bind(this);this.onTouchMove=this.onTouchMove.bind(this);this.last=0;this.currentCursorStyle="inherit";this.mouseOut=false;this.resolution=1;this._tempPoint=new PIXI.Point};PIXI.InteractionManager.prototype.constructor=PIXI.InteractionManager;PIXI.InteractionManager.prototype.collectInteractiveSprite=function(displayObject,iParent){var children=displayObject.children;var length=children.length;for(var i=length-1;i>=0;i--){var child=children[i];if(child._interactive){iParent.interactiveChildren=true;this.interactiveItems.push(child);if(child.children.length>0){this.collectInteractiveSprite(child,child)}}else{child.__iParent=null;if(child.children.length>0){this.collectInteractiveSprite(child,iParent)}}}};PIXI.InteractionManager.prototype.setTarget=function(target){this.target=target;this.resolution=target.resolution;if(this.interactionDOMElement!==null)return;this.setTargetDomElement(target.view)};PIXI.InteractionManager.prototype.setTargetDomElement=function(domElement){this.removeEvents();if(window.navigator.msPointerEnabled){domElement.style["-ms-content-zooming"]="none";domElement.style["-ms-touch-action"]="none"}this.interactionDOMElement=domElement;domElement.addEventListener("mousemove",this.onMouseMove,true);domElement.addEventListener("mousedown",this.onMouseDown,true);domElement.addEventListener("mouseout",this.onMouseOut,true);domElement.addEventListener("touchstart",this.onTouchStart,true);domElement.addEventListener("touchend",this.onTouchEnd,true);domElement.addEventListener("touchmove",this.onTouchMove,true);window.addEventListener("mouseup",this.onMouseUp,true)};PIXI.InteractionManager.prototype.removeEvents=function(){if(!this.interactionDOMElement)return;this.interactionDOMElement.style["-ms-content-zooming"]="";this.interactionDOMElement.style["-ms-touch-action"]="";this.interactionDOMElement.removeEventListener("mousemove",this.onMouseMove,true);this.interactionDOMElement.removeEventListener("mousedown",this.onMouseDown,true);this.interactionDOMElement.removeEventListener("mouseout",this.onMouseOut,true);this.interactionDOMElement.removeEventListener("touchstart",this.onTouchStart,true);this.interactionDOMElement.removeEventListener("touchend",this.onTouchEnd,true);this.interactionDOMElement.removeEventListener("touchmove",this.onTouchMove,true);this.interactionDOMElement=null;window.removeEventListener("mouseup",this.onMouseUp,true)};PIXI.InteractionManager.prototype.update=function(){if(!this.target)return;var now=Date.now();var diff=now-this.last;diff=diff*PIXI.INTERACTION_FREQUENCY/1e3;if(diff<1)return;this.last=now;var i=0;if(this.dirty){this.rebuildInteractiveGraph()}var length=this.interactiveItems.length;var cursor="inherit";var over=false;for(i=0;ix1&&xy1&&y>16&255)/255,(hex>>8&255)/255,(hex&255)/255]};PIXI.rgb2hex=function(rgb){return(rgb[0]*255<<16)+(rgb[1]*255<<8)+rgb[2]*255};if(typeof Function.prototype.bind!=="function"){Function.prototype.bind=function(){return function(thisArg){var target=this,i=arguments.length-1,boundArgs=[];if(i>0){boundArgs.length=i;while(i--)boundArgs[i]=arguments[i+1]}if(typeof target!=="function")throw new TypeError;function bound(){var i=arguments.length,args=new Array(i);while(i--)args[i]=arguments[i];args=boundArgs.concat(args);return target.apply(this instanceof bound?this:thisArg,args)}bound.prototype=function F(proto){if(proto)F.prototype=proto;if(!(this instanceof F))return new F}(target.prototype);return bound}}()}PIXI.AjaxRequest=function(){var activexmodes=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.3.0","Microsoft.XMLHTTP"];if(window.ActiveXObject){for(var i=0;i0&&(number&number-1)===0)return number;else{var result=1;while(result0&&(width&width-1)===0&&height>0&&(height&height-1)===0};PIXI.EventTarget={call:function callCompat(obj){if(obj){obj=obj.prototype||obj;PIXI.EventTarget.mixin(obj)}},mixin:function mixin(obj){obj.listeners=function listeners(eventName){this._listeners=this._listeners||{};return this._listeners[eventName]?this._listeners[eventName].slice():[]};obj.emit=obj.dispatchEvent=function emit(eventName,data){this._listeners=this._listeners||{};if(typeof eventName==="object"){data=eventName;eventName=eventName.type}if(!data||data.__isEventObject!==true){data=new PIXI.Event(this,eventName,data)}if(this._listeners&&this._listeners[eventName]){var listeners=this._listeners[eventName].slice(0),length=listeners.length,fn=listeners[0],i;for(i=0;i0){if(list[i]===fn||list[i]._originalHandler===fn){list.splice(i,1)}}if(list.length===0){delete this._listeners[eventName]}return this};obj.removeAllListeners=function removeAllListeners(eventName){this._listeners=this._listeners||{};if(!this._listeners[eventName])return this;delete this._listeners[eventName];return this}}};PIXI.Event=function(target,name,data){this.__isEventObject=true;this.stopped=false;this.stoppedImmediate=false;this.target=target;this.type=name;this.data=data;this.content=data;this.timeStamp=Date.now()};PIXI.Event.prototype.stopPropagation=function stopPropagation(){this.stopped=true};PIXI.Event.prototype.stopImmediatePropagation=function stopImmediatePropagation(){this.stoppedImmediate=true};PIXI.autoDetectRenderer=function(width,height,options){if(!width)width=800;if(!height)height=600;var webgl=function(){try{var canvas=document.createElement("canvas");return!!window.WebGLRenderingContext&&(canvas.getContext("webgl")||canvas.getContext("experimental-webgl"))}catch(e){return false}}();if(webgl){return new PIXI.WebGLRenderer(width,height,options)}return new PIXI.CanvasRenderer(width,height,options)};PIXI.autoDetectRecommendedRenderer=function(width,height,options){if(!width)width=800;if(!height)height=600;var webgl=function(){try{var canvas=document.createElement("canvas");return!!window.WebGLRenderingContext&&(canvas.getContext("webgl")||canvas.getContext("experimental-webgl"))}catch(e){return false}}();var isAndroid=/Android/i.test(navigator.userAgent);if(webgl&&!isAndroid){return new PIXI.WebGLRenderer(width,height,options)}return new PIXI.CanvasRenderer(width,height,options)};PIXI.PolyK={};PIXI.PolyK.Triangulate=function(p){var sign=true;var n=p.length>>1;if(n<3)return[];var tgs=[];var avl=[];for(var i=0;i3){var i0=avl[(i+0)%al];var i1=avl[(i+1)%al];var i2=avl[(i+2)%al];var ax=p[2*i0],ay=p[2*i0+1];var bx=p[2*i1],by=p[2*i1+1];var cx=p[2*i2],cy=p[2*i2+1];var earFound=false;if(PIXI.PolyK._convex(ax,ay,bx,by,cx,cy,sign)){earFound=true;for(var j=0;j3*al){if(sign){tgs=[];avl=[];for(i=0;i=0&&v>=0&&u+v<1};PIXI.PolyK._convex=function(ax,ay,bx,by,cx,cy,sign){return(ay-by)*(cx-bx)+(bx-ax)*(cy-by)>=0===sign};PIXI.initDefaultShaders=function(){};PIXI.CompileVertexShader=function(gl,shaderSrc){return PIXI._CompileShader(gl,shaderSrc,gl.VERTEX_SHADER)};PIXI.CompileFragmentShader=function(gl,shaderSrc){return PIXI._CompileShader(gl,shaderSrc,gl.FRAGMENT_SHADER)};PIXI._CompileShader=function(gl,shaderSrc,shaderType){var src=shaderSrc.join("\n");var shader=gl.createShader(shaderType);gl.shaderSource(shader,src);gl.compileShader(shader);if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)){window.console.log(gl.getShaderInfoLog(shader));return null}return shader};PIXI.compileProgram=function(gl,vertexSrc,fragmentSrc){var fragmentShader=PIXI.CompileFragmentShader(gl,fragmentSrc);var vertexShader=PIXI.CompileVertexShader(gl,vertexSrc);var shaderProgram=gl.createProgram();gl.attachShader(shaderProgram,vertexShader);gl.attachShader(shaderProgram,fragmentShader);gl.linkProgram(shaderProgram);if(!gl.getProgramParameter(shaderProgram,gl.LINK_STATUS)){window.console.log("Could not initialise shaders")}return shaderProgram};PIXI.PixiShader=function(gl){this._UID=PIXI._UID++;this.gl=gl;this.program=null;this.fragmentSrc=["precision lowp float;","varying vec2 vTextureCoord;","varying vec4 vColor;","uniform sampler2D uSampler;","void main(void) {"," gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;","}"];this.textureCount=0;this.firstRun=true;this.dirty=true;this.attributes=[];this.init()};PIXI.PixiShader.prototype.constructor=PIXI.PixiShader;PIXI.PixiShader.prototype.init=function(){var gl=this.gl;var program=PIXI.compileProgram(gl,this.vertexSrc||PIXI.PixiShader.defaultVertexSrc,this.fragmentSrc);gl.useProgram(program);this.uSampler=gl.getUniformLocation(program,"uSampler");this.projectionVector=gl.getUniformLocation(program,"projectionVector");this.offsetVector=gl.getUniformLocation(program,"offsetVector");this.dimensions=gl.getUniformLocation(program,"dimensions");this.aVertexPosition=gl.getAttribLocation(program,"aVertexPosition");this.aTextureCoord=gl.getAttribLocation(program,"aTextureCoord");this.colorAttribute=gl.getAttribLocation(program,"aColor");if(this.colorAttribute===-1){this.colorAttribute=2}this.attributes=[this.aVertexPosition,this.aTextureCoord,this.colorAttribute];for(var key in this.uniforms){this.uniforms[key].uniformLocation=gl.getUniformLocation(program,key)}this.initUniforms();this.program=program};PIXI.PixiShader.prototype.initUniforms=function(){this.textureCount=1;var gl=this.gl;var uniform;for(var key in this.uniforms){uniform=this.uniforms[key];var type=uniform.type;if(type==="sampler2D"){uniform._init=false;if(uniform.value!==null){this.initSampler2D(uniform)}}else if(type==="mat2"||type==="mat3"||type==="mat4"){uniform.glMatrix=true;uniform.glValueLength=1;if(type==="mat2"){uniform.glFunc=gl.uniformMatrix2fv}else if(type==="mat3"){uniform.glFunc=gl.uniformMatrix3fv}else if(type==="mat4"){uniform.glFunc=gl.uniformMatrix4fv}}else{uniform.glFunc=gl["uniform"+type];if(type==="2f"||type==="2i"){uniform.glValueLength=2}else if(type==="3f"||type==="3i"){uniform.glValueLength=3}else if(type==="4f"||type==="4i"){uniform.glValueLength=4}else{uniform.glValueLength=1}}}};PIXI.PixiShader.prototype.initSampler2D=function(uniform){if(!uniform.value||!uniform.value.baseTexture||!uniform.value.baseTexture.hasLoaded){return}var gl=this.gl;gl.activeTexture(gl["TEXTURE"+this.textureCount]);gl.bindTexture(gl.TEXTURE_2D,uniform.value.baseTexture._glTextures[gl.id]);if(uniform.textureData){var data=uniform.textureData;var magFilter=data.magFilter?data.magFilter:gl.LINEAR;var minFilter=data.minFilter?data.minFilter:gl.LINEAR;var wrapS=data.wrapS?data.wrapS:gl.CLAMP_TO_EDGE;var wrapT=data.wrapT?data.wrapT:gl.CLAMP_TO_EDGE;var format=data.luminance?gl.LUMINANCE:gl.RGBA;if(data.repeat){wrapS=gl.REPEAT;wrapT=gl.REPEAT}gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,!!data.flipY);if(data.width){var width=data.width?data.width:512;var height=data.height?data.height:2;var border=data.border?data.border:0;gl.texImage2D(gl.TEXTURE_2D,0,format,width,height,border,format,gl.UNSIGNED_BYTE,null)}else{gl.texImage2D(gl.TEXTURE_2D,0,format,gl.RGBA,gl.UNSIGNED_BYTE,uniform.value.baseTexture.source)}gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,magFilter);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,minFilter);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,wrapS);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,wrapT)}gl.uniform1i(uniform.uniformLocation,this.textureCount);uniform._init=true;this.textureCount++};PIXI.PixiShader.prototype.syncUniforms=function(){this.textureCount=1;var uniform;var gl=this.gl;for(var key in this.uniforms){uniform=this.uniforms[key];if(uniform.glValueLength===1){if(uniform.glMatrix===true){uniform.glFunc.call(gl,uniform.uniformLocation,uniform.transpose,uniform.value)}else{uniform.glFunc.call(gl,uniform.uniformLocation,uniform.value)}}else if(uniform.glValueLength===2){uniform.glFunc.call(gl,uniform.uniformLocation,uniform.value.x,uniform.value.y)}else if(uniform.glValueLength===3){uniform.glFunc.call(gl,uniform.uniformLocation,uniform.value.x,uniform.value.y,uniform.value.z)}else if(uniform.glValueLength===4){uniform.glFunc.call(gl,uniform.uniformLocation,uniform.value.x,uniform.value.y,uniform.value.z,uniform.value.w)}else if(uniform.type==="sampler2D"){if(uniform._init){gl.activeTexture(gl["TEXTURE"+this.textureCount]);if(uniform.value.baseTexture._dirty[gl.id]){PIXI.instances[gl.id].updateTexture(uniform.value.baseTexture)}else{gl.bindTexture(gl.TEXTURE_2D,uniform.value.baseTexture._glTextures[gl.id])}gl.uniform1i(uniform.uniformLocation,this.textureCount);this.textureCount++}else{this.initSampler2D(uniform)}}}};PIXI.PixiShader.prototype.destroy=function(){this.gl.deleteProgram(this.program);this.uniforms=null;this.gl=null;this.attributes=null};PIXI.PixiShader.defaultVertexSrc=["attribute vec2 aVertexPosition;","attribute vec2 aTextureCoord;","attribute vec4 aColor;","uniform vec2 projectionVector;","uniform vec2 offsetVector;","varying vec2 vTextureCoord;","varying vec4 vColor;","const vec2 center = vec2(-1.0, 1.0);","void main(void) {"," gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);"," vTextureCoord = aTextureCoord;"," vColor = vec4(aColor.rgb * aColor.a, aColor.a);","}"];PIXI.PixiFastShader=function(gl){this._UID=PIXI._UID++;this.gl=gl;this.program=null;this.fragmentSrc=["precision lowp float;","varying vec2 vTextureCoord;","varying float vColor;","uniform sampler2D uSampler;","void main(void) {"," gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;","}"];this.vertexSrc=["attribute vec2 aVertexPosition;","attribute vec2 aPositionCoord;","attribute vec2 aScale;","attribute float aRotation;","attribute vec2 aTextureCoord;","attribute float aColor;","uniform vec2 projectionVector;","uniform vec2 offsetVector;","uniform mat3 uMatrix;","varying vec2 vTextureCoord;","varying float vColor;","const vec2 center = vec2(-1.0, 1.0);","void main(void) {"," vec2 v;"," vec2 sv = aVertexPosition * aScale;"," v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);"," v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);"," v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;"," gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);"," vTextureCoord = aTextureCoord;"," vColor = aColor;","}"];this.textureCount=0;this.init()};PIXI.PixiFastShader.prototype.constructor=PIXI.PixiFastShader;PIXI.PixiFastShader.prototype.init=function(){var gl=this.gl;var program=PIXI.compileProgram(gl,this.vertexSrc,this.fragmentSrc);gl.useProgram(program);this.uSampler=gl.getUniformLocation(program,"uSampler");this.projectionVector=gl.getUniformLocation(program,"projectionVector");this.offsetVector=gl.getUniformLocation(program,"offsetVector");this.dimensions=gl.getUniformLocation(program,"dimensions");this.uMatrix=gl.getUniformLocation(program,"uMatrix");this.aVertexPosition=gl.getAttribLocation(program,"aVertexPosition");this.aPositionCoord=gl.getAttribLocation(program,"aPositionCoord");this.aScale=gl.getAttribLocation(program,"aScale");this.aRotation=gl.getAttribLocation(program,"aRotation");this.aTextureCoord=gl.getAttribLocation(program,"aTextureCoord");this.colorAttribute=gl.getAttribLocation(program,"aColor");if(this.colorAttribute===-1){this.colorAttribute=2}this.attributes=[this.aVertexPosition,this.aPositionCoord,this.aScale,this.aRotation,this.aTextureCoord,this.colorAttribute];this.program=program};PIXI.PixiFastShader.prototype.destroy=function(){this.gl.deleteProgram(this.program);this.uniforms=null;this.gl=null;this.attributes=null};PIXI.StripShader=function(gl){this._UID=PIXI._UID++;this.gl=gl;this.program=null;this.fragmentSrc=["precision mediump float;","varying vec2 vTextureCoord;","uniform float alpha;","uniform sampler2D uSampler;","void main(void) {"," gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;","}"];this.vertexSrc=["attribute vec2 aVertexPosition;","attribute vec2 aTextureCoord;","uniform mat3 translationMatrix;","uniform vec2 projectionVector;","uniform vec2 offsetVector;","varying vec2 vTextureCoord;","void main(void) {"," vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);"," v -= offsetVector.xyx;"," gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);"," vTextureCoord = aTextureCoord;","}"];this.init()};PIXI.StripShader.prototype.constructor=PIXI.StripShader;PIXI.StripShader.prototype.init=function(){var gl=this.gl;var program=PIXI.compileProgram(gl,this.vertexSrc,this.fragmentSrc);gl.useProgram(program);this.uSampler=gl.getUniformLocation(program,"uSampler");this.projectionVector=gl.getUniformLocation(program,"projectionVector");this.offsetVector=gl.getUniformLocation(program,"offsetVector");this.colorAttribute=gl.getAttribLocation(program,"aColor");this.aVertexPosition=gl.getAttribLocation(program,"aVertexPosition");this.aTextureCoord=gl.getAttribLocation(program,"aTextureCoord");this.attributes=[this.aVertexPosition,this.aTextureCoord];this.translationMatrix=gl.getUniformLocation(program,"translationMatrix");this.alpha=gl.getUniformLocation(program,"alpha");this.program=program};PIXI.StripShader.prototype.destroy=function(){this.gl.deleteProgram(this.program);this.uniforms=null;this.gl=null;this.attribute=null};PIXI.PrimitiveShader=function(gl){this._UID=PIXI._UID++;this.gl=gl;this.program=null;this.fragmentSrc=["precision mediump float;","varying vec4 vColor;","void main(void) {"," gl_FragColor = vColor;","}"];this.vertexSrc=["attribute vec2 aVertexPosition;","attribute vec4 aColor;","uniform mat3 translationMatrix;","uniform vec2 projectionVector;","uniform vec2 offsetVector;","uniform float alpha;","uniform float flipY;","uniform vec3 tint;","varying vec4 vColor;","void main(void) {"," vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);"," v -= offsetVector.xyx;"," gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);"," vColor = aColor * vec4(tint * alpha, alpha);","}"];this.init()};PIXI.PrimitiveShader.prototype.constructor=PIXI.PrimitiveShader;PIXI.PrimitiveShader.prototype.init=function(){var gl=this.gl;var program=PIXI.compileProgram(gl,this.vertexSrc,this.fragmentSrc);gl.useProgram(program);this.projectionVector=gl.getUniformLocation(program,"projectionVector");this.offsetVector=gl.getUniformLocation(program,"offsetVector");this.tintColor=gl.getUniformLocation(program,"tint");this.flipY=gl.getUniformLocation(program,"flipY");this.aVertexPosition=gl.getAttribLocation(program,"aVertexPosition");this.colorAttribute=gl.getAttribLocation(program,"aColor");this.attributes=[this.aVertexPosition,this.colorAttribute];this.translationMatrix=gl.getUniformLocation(program,"translationMatrix");this.alpha=gl.getUniformLocation(program,"alpha");this.program=program};PIXI.PrimitiveShader.prototype.destroy=function(){this.gl.deleteProgram(this.program);this.uniforms=null;this.gl=null;this.attributes=null};PIXI.ComplexPrimitiveShader=function(gl){this._UID=PIXI._UID++;this.gl=gl;this.program=null;this.fragmentSrc=["precision mediump float;","varying vec4 vColor;","void main(void) {"," gl_FragColor = vColor;","}"];this.vertexSrc=["attribute vec2 aVertexPosition;","uniform mat3 translationMatrix;","uniform vec2 projectionVector;","uniform vec2 offsetVector;","uniform vec3 tint;","uniform float alpha;","uniform vec3 color;","uniform float flipY;","varying vec4 vColor;","void main(void) {"," vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);"," v -= offsetVector.xyx;"," gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);"," vColor = vec4(color * alpha * tint, alpha);","}"];this.init()};PIXI.ComplexPrimitiveShader.prototype.constructor=PIXI.ComplexPrimitiveShader;PIXI.ComplexPrimitiveShader.prototype.init=function(){var gl=this.gl;var program=PIXI.compileProgram(gl,this.vertexSrc,this.fragmentSrc);gl.useProgram(program);this.projectionVector=gl.getUniformLocation(program,"projectionVector");this.offsetVector=gl.getUniformLocation(program,"offsetVector");this.tintColor=gl.getUniformLocation(program,"tint");this.color=gl.getUniformLocation(program,"color");this.flipY=gl.getUniformLocation(program,"flipY");this.aVertexPosition=gl.getAttribLocation(program,"aVertexPosition");this.attributes=[this.aVertexPosition,this.colorAttribute];this.translationMatrix=gl.getUniformLocation(program,"translationMatrix");this.alpha=gl.getUniformLocation(program,"alpha");this.program=program};PIXI.ComplexPrimitiveShader.prototype.destroy=function(){this.gl.deleteProgram(this.program);this.uniforms=null;this.gl=null;this.attribute=null};PIXI.WebGLGraphics=function(){};PIXI.WebGLGraphics.renderGraphics=function(graphics,renderSession){var gl=renderSession.gl;var projection=renderSession.projection,offset=renderSession.offset,shader=renderSession.shaderManager.primitiveShader,webGLData;if(graphics.dirty){PIXI.WebGLGraphics.updateGraphics(graphics,gl)}var webGL=graphics._webGL[gl.id];for(var i=0;i=6){if(data.points.length<6*2){webGLData=PIXI.WebGLGraphics.switchMode(webGL,0);var canDrawUsingSimple=PIXI.WebGLGraphics.buildPoly(data,webGLData);if(!canDrawUsingSimple){webGLData=PIXI.WebGLGraphics.switchMode(webGL,1);PIXI.WebGLGraphics.buildComplexPoly(data,webGLData)}}else{webGLData=PIXI.WebGLGraphics.switchMode(webGL,1);PIXI.WebGLGraphics.buildComplexPoly(data,webGLData)}}}if(data.lineWidth>0){webGLData=PIXI.WebGLGraphics.switchMode(webGL,0);PIXI.WebGLGraphics.buildLine(data,webGLData)}}else{webGLData=PIXI.WebGLGraphics.switchMode(webGL,0);if(data.type===PIXI.Graphics.RECT){PIXI.WebGLGraphics.buildRectangle(data,webGLData)}else if(data.type===PIXI.Graphics.CIRC||data.type===PIXI.Graphics.ELIP){PIXI.WebGLGraphics.buildCircle(data,webGLData)}else if(data.type===PIXI.Graphics.RREC){PIXI.WebGLGraphics.buildRoundedRectangle(data,webGLData)}}webGL.lastIndex++}for(i=0;i140*140){perp3x=perpx-perp2x;perp3y=perpy-perp2y;dist=Math.sqrt(perp3x*perp3x+perp3y*perp3y);perp3x/=dist;perp3y/=dist;perp3x*=width;perp3y*=width; -verts.push(p2x-perp3x,p2y-perp3y);verts.push(r,g,b,alpha);verts.push(p2x+perp3x,p2y+perp3y);verts.push(r,g,b,alpha);verts.push(p2x-perp3x,p2y-perp3y);verts.push(r,g,b,alpha);indexCount++}else{verts.push(px,py);verts.push(r,g,b,alpha);verts.push(p2x-(px-p2x),p2y-(py-p2y));verts.push(r,g,b,alpha)}}p1x=points[(length-2)*2];p1y=points[(length-2)*2+1];p2x=points[(length-1)*2];p2y=points[(length-1)*2+1];perpx=-(p1y-p2y);perpy=p1x-p2x;dist=Math.sqrt(perpx*perpx+perpy*perpy);perpx/=dist;perpy/=dist;perpx*=width;perpy*=width;verts.push(p2x-perpx,p2y-perpy);verts.push(r,g,b,alpha);verts.push(p2x+perpx,p2y+perpy);verts.push(r,g,b,alpha);indices.push(indexStart);for(i=0;imaxX?x:maxX;minY=ymaxY?y:maxY}points.push(minX,minY,maxX,minY,maxX,maxY,minX,maxY);var length=points.length/2;for(i=0;i=this.size){this.flush();this.currentBaseTexture=texture.baseTexture}var uvs=texture._uvs;if(!uvs)return;var aX=sprite.anchor.x;var aY=sprite.anchor.y;var w0,w1,h0,h1;if(texture.trim){var trim=texture.trim;w1=trim.x-aX*trim.width;w0=w1+texture.crop.width;h1=trim.y-aY*trim.height;h0=h1+texture.crop.height}else{w0=texture.frame.width*(1-aX);w1=texture.frame.width*-aX;h0=texture.frame.height*(1-aY);h1=texture.frame.height*-aY}var index=this.currentBatchSize*4*this.vertSize;var resolution=texture.baseTexture.resolution;var worldTransform=sprite.worldTransform;var a=worldTransform.a/resolution;var b=worldTransform.b/resolution;var c=worldTransform.c/resolution;var d=worldTransform.d/resolution;var tx=worldTransform.tx;var ty=worldTransform.ty;var colors=this.colors;var positions=this.positions;if(this.renderSession.roundPixels){positions[index]=a*w1+c*h1+tx|0;positions[index+1]=d*h1+b*w1+ty|0;positions[index+5]=a*w0+c*h1+tx|0;positions[index+6]=d*h1+b*w0+ty|0;positions[index+10]=a*w0+c*h0+tx|0;positions[index+11]=d*h0+b*w0+ty|0;positions[index+15]=a*w1+c*h0+tx|0;positions[index+16]=d*h0+b*w1+ty|0}else{positions[index]=a*w1+c*h1+tx;positions[index+1]=d*h1+b*w1+ty;positions[index+5]=a*w0+c*h1+tx;positions[index+6]=d*h1+b*w0+ty;positions[index+10]=a*w0+c*h0+tx;positions[index+11]=d*h0+b*w0+ty;positions[index+15]=a*w1+c*h0+tx;positions[index+16]=d*h0+b*w1+ty}positions[index+2]=uvs.x0;positions[index+3]=uvs.y0;positions[index+7]=uvs.x1;positions[index+8]=uvs.y1;positions[index+12]=uvs.x2;positions[index+13]=uvs.y2;positions[index+17]=uvs.x3;positions[index+18]=uvs.y3;var tint=sprite.tint;colors[index+4]=colors[index+9]=colors[index+14]=colors[index+19]=(tint>>16)+(tint&65280)+((tint&255)<<16)+(sprite.worldAlpha*255<<24);this.sprites[this.currentBatchSize++]=sprite};PIXI.WebGLSpriteBatch.prototype.renderTilingSprite=function(tilingSprite){var texture=tilingSprite.tilingTexture;if(this.currentBatchSize>=this.size){this.flush();this.currentBaseTexture=texture.baseTexture}if(!tilingSprite._uvs)tilingSprite._uvs=new PIXI.TextureUvs;var uvs=tilingSprite._uvs;tilingSprite.tilePosition.x%=texture.baseTexture.width*tilingSprite.tileScaleOffset.x;tilingSprite.tilePosition.y%=texture.baseTexture.height*tilingSprite.tileScaleOffset.y;var offsetX=tilingSprite.tilePosition.x/(texture.baseTexture.width*tilingSprite.tileScaleOffset.x);var offsetY=tilingSprite.tilePosition.y/(texture.baseTexture.height*tilingSprite.tileScaleOffset.y);var scaleX=tilingSprite.width/texture.baseTexture.width/(tilingSprite.tileScale.x*tilingSprite.tileScaleOffset.x);var scaleY=tilingSprite.height/texture.baseTexture.height/(tilingSprite.tileScale.y*tilingSprite.tileScaleOffset.y);uvs.x0=0-offsetX;uvs.y0=0-offsetY;uvs.x1=1*scaleX-offsetX;uvs.y1=0-offsetY;uvs.x2=1*scaleX-offsetX;uvs.y2=1*scaleY-offsetY;uvs.x3=0-offsetX;uvs.y3=1*scaleY-offsetY;var tint=tilingSprite.tint;var color=(tint>>16)+(tint&65280)+((tint&255)<<16)+(tilingSprite.alpha*255<<24);var positions=this.positions;var colors=this.colors;var width=tilingSprite.width;var height=tilingSprite.height;var aX=tilingSprite.anchor.x;var aY=tilingSprite.anchor.y;var w0=width*(1-aX);var w1=width*-aX;var h0=height*(1-aY);var h1=height*-aY;var index=this.currentBatchSize*4*this.vertSize;var resolution=texture.baseTexture.resolution;var worldTransform=tilingSprite.worldTransform;var a=worldTransform.a/resolution;var b=worldTransform.b/resolution;var c=worldTransform.c/resolution;var d=worldTransform.d/resolution;var tx=worldTransform.tx;var ty=worldTransform.ty;positions[index++]=a*w1+c*h1+tx;positions[index++]=d*h1+b*w1+ty;positions[index++]=uvs.x0;positions[index++]=uvs.y0;colors[index++]=color;positions[index++]=a*w0+c*h1+tx;positions[index++]=d*h1+b*w0+ty;positions[index++]=uvs.x1;positions[index++]=uvs.y1;colors[index++]=color;positions[index++]=a*w0+c*h0+tx;positions[index++]=d*h0+b*w0+ty;positions[index++]=uvs.x2;positions[index++]=uvs.y2;colors[index++]=color;positions[index++]=a*w1+c*h0+tx;positions[index++]=d*h0+b*w1+ty;positions[index++]=uvs.x3;positions[index++]=uvs.y3;colors[index++]=color;this.sprites[this.currentBatchSize++]=tilingSprite};PIXI.WebGLSpriteBatch.prototype.flush=function(){if(this.currentBatchSize===0)return;var gl=this.gl;var shader;if(this.dirty){this.dirty=false;gl.activeTexture(gl.TEXTURE0);gl.bindBuffer(gl.ARRAY_BUFFER,this.vertexBuffer);gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,this.indexBuffer);shader=this.defaultShader.shaders[gl.id];var stride=this.vertSize*4;gl.vertexAttribPointer(shader.aVertexPosition,2,gl.FLOAT,false,stride,0);gl.vertexAttribPointer(shader.aTextureCoord,2,gl.FLOAT,false,stride,2*4);gl.vertexAttribPointer(shader.colorAttribute,4,gl.UNSIGNED_BYTE,true,stride,4*4)}if(this.currentBatchSize>this.size*.5){gl.bufferSubData(gl.ARRAY_BUFFER,0,this.vertices)}else{var view=this.positions.subarray(0,this.currentBatchSize*4*this.vertSize);gl.bufferSubData(gl.ARRAY_BUFFER,0,view)}var nextTexture,nextBlendMode,nextShader;var batchSize=0;var start=0;var currentBaseTexture=null;var currentBlendMode=this.renderSession.blendModeManager.currentBlendMode;var currentShader=null;var blendSwap=false;var shaderSwap=false;var sprite;for(var i=0,j=this.currentBatchSize;i=this.size){this.flush()}};PIXI.WebGLFastSpriteBatch.prototype.flush=function(){if(this.currentBatchSize===0)return;var gl=this.gl;if(!this.currentBaseTexture._glTextures[gl.id])this.renderSession.renderer.updateTexture(this.currentBaseTexture,gl);gl.bindTexture(gl.TEXTURE_2D,this.currentBaseTexture._glTextures[gl.id]);if(this.currentBatchSize>this.size*.5){gl.bufferSubData(gl.ARRAY_BUFFER,0,this.vertices)}else{var view=this.vertices.subarray(0,this.currentBatchSize*4*this.vertSize);gl.bufferSubData(gl.ARRAY_BUFFER,0,view)}gl.drawElements(gl.TRIANGLES,this.currentBatchSize*6,gl.UNSIGNED_SHORT,0);this.currentBatchSize=0;this.renderSession.drawCount++};PIXI.WebGLFastSpriteBatch.prototype.stop=function(){this.flush() -};PIXI.WebGLFastSpriteBatch.prototype.start=function(){var gl=this.gl;gl.activeTexture(gl.TEXTURE0);gl.bindBuffer(gl.ARRAY_BUFFER,this.vertexBuffer);gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,this.indexBuffer);var projection=this.renderSession.projection;gl.uniform2f(this.shader.projectionVector,projection.x,projection.y);gl.uniformMatrix3fv(this.shader.uMatrix,false,this.matrix);var stride=this.vertSize*4;gl.vertexAttribPointer(this.shader.aVertexPosition,2,gl.FLOAT,false,stride,0);gl.vertexAttribPointer(this.shader.aPositionCoord,2,gl.FLOAT,false,stride,2*4);gl.vertexAttribPointer(this.shader.aScale,2,gl.FLOAT,false,stride,4*4);gl.vertexAttribPointer(this.shader.aRotation,1,gl.FLOAT,false,stride,6*4);gl.vertexAttribPointer(this.shader.aTextureCoord,2,gl.FLOAT,false,stride,7*4);gl.vertexAttribPointer(this.shader.colorAttribute,1,gl.FLOAT,false,stride,9*4)};PIXI.WebGLFilterManager=function(){this.filterStack=[];this.offsetX=0;this.offsetY=0};PIXI.WebGLFilterManager.prototype.constructor=PIXI.WebGLFilterManager;PIXI.WebGLFilterManager.prototype.setContext=function(gl){this.gl=gl;this.texturePool=[];this.initShaderBuffers()};PIXI.WebGLFilterManager.prototype.begin=function(renderSession,buffer){this.renderSession=renderSession;this.defaultShader=renderSession.shaderManager.defaultShader;var projection=this.renderSession.projection;this.width=projection.x*2;this.height=-projection.y*2;this.buffer=buffer};PIXI.WebGLFilterManager.prototype.pushFilter=function(filterBlock){var gl=this.gl;var projection=this.renderSession.projection;var offset=this.renderSession.offset;filterBlock._filterArea=filterBlock.target.filterArea||filterBlock.target.getBounds();this.filterStack.push(filterBlock);var filter=filterBlock.filterPasses[0];this.offsetX+=filterBlock._filterArea.x;this.offsetY+=filterBlock._filterArea.y;var texture=this.texturePool.pop();if(!texture){texture=new PIXI.FilterTexture(this.gl,this.width,this.height)}else{texture.resize(this.width,this.height)}gl.bindTexture(gl.TEXTURE_2D,texture.texture);var filterArea=filterBlock._filterArea;var padding=filter.padding;filterArea.x-=padding;filterArea.y-=padding;filterArea.width+=padding*2;filterArea.height+=padding*2;if(filterArea.x<0)filterArea.x=0;if(filterArea.width>this.width)filterArea.width=this.width;if(filterArea.y<0)filterArea.y=0;if(filterArea.height>this.height)filterArea.height=this.height;gl.bindFramebuffer(gl.FRAMEBUFFER,texture.frameBuffer);gl.viewport(0,0,filterArea.width,filterArea.height);projection.x=filterArea.width/2;projection.y=-filterArea.height/2;offset.x=-filterArea.x;offset.y=-filterArea.y;gl.colorMask(true,true,true,true);gl.clearColor(0,0,0,0);gl.clear(gl.COLOR_BUFFER_BIT);filterBlock._glFilterTexture=texture};PIXI.WebGLFilterManager.prototype.popFilter=function(){var gl=this.gl;var filterBlock=this.filterStack.pop();var filterArea=filterBlock._filterArea;var texture=filterBlock._glFilterTexture;var projection=this.renderSession.projection;var offset=this.renderSession.offset;if(filterBlock.filterPasses.length>1){gl.viewport(0,0,filterArea.width,filterArea.height);gl.bindBuffer(gl.ARRAY_BUFFER,this.vertexBuffer);this.vertexArray[0]=0;this.vertexArray[1]=filterArea.height;this.vertexArray[2]=filterArea.width;this.vertexArray[3]=filterArea.height;this.vertexArray[4]=0;this.vertexArray[5]=0;this.vertexArray[6]=filterArea.width;this.vertexArray[7]=0;gl.bufferSubData(gl.ARRAY_BUFFER,0,this.vertexArray);gl.bindBuffer(gl.ARRAY_BUFFER,this.uvBuffer);this.uvArray[2]=filterArea.width/this.width;this.uvArray[5]=filterArea.height/this.height;this.uvArray[6]=filterArea.width/this.width;this.uvArray[7]=filterArea.height/this.height;gl.bufferSubData(gl.ARRAY_BUFFER,0,this.uvArray);var inputTexture=texture;var outputTexture=this.texturePool.pop();if(!outputTexture)outputTexture=new PIXI.FilterTexture(this.gl,this.width,this.height);outputTexture.resize(this.width,this.height);gl.bindFramebuffer(gl.FRAMEBUFFER,outputTexture.frameBuffer);gl.clear(gl.COLOR_BUFFER_BIT);gl.disable(gl.BLEND);for(var i=0;imaxRadius?maxRadius:radius;context.beginPath();context.moveTo(rx,ry+radius);context.lineTo(rx,ry+height-radius);context.quadraticCurveTo(rx,ry+height,rx+radius,ry+height);context.lineTo(rx+width-radius,ry+height);context.quadraticCurveTo(rx+width,ry+height,rx+width,ry+height-radius);context.lineTo(rx+width,ry+radius);context.quadraticCurveTo(rx+width,ry,rx+width-radius,ry);context.lineTo(rx+radius,ry);context.quadraticCurveTo(rx,ry,rx,ry+radius);context.closePath();if(data.fillColor||data.fillColor===0){context.globalAlpha=data.fillAlpha*worldAlpha;context.fillStyle="#"+("00000"+(fillColor|0).toString(16)).substr(-6);context.fill()}if(data.lineWidth){context.globalAlpha=data.lineAlpha*worldAlpha;context.strokeStyle="#"+("00000"+(lineColor|0).toString(16)).substr(-6);context.stroke()}}}};PIXI.CanvasGraphics.renderGraphicsMask=function(graphics,context){var len=graphics.graphicsData.length;if(len===0)return;if(len>1){len=1;window.console.log("Pixi.js warning: masks in canvas can only mask using the first path in the graphics object")}for(var i=0;i<1;i++){var data=graphics.graphicsData[i];var shape=data.shape;if(data.type===PIXI.Graphics.POLY){context.beginPath();var points=shape.points;context.moveTo(points[0],points[1]);for(var j=1;jmaxRadius?maxRadius:radius;context.beginPath();context.moveTo(rx,ry+radius);context.lineTo(rx,ry+height-radius);context.quadraticCurveTo(rx,ry+height,rx+radius,ry+height);context.lineTo(rx+width-radius,ry+height);context.quadraticCurveTo(rx+width,ry+height,rx+width,ry+height-radius);context.lineTo(rx+width,ry+radius);context.quadraticCurveTo(rx+width,ry,rx+width-radius,ry);context.lineTo(rx+radius,ry);context.quadraticCurveTo(rx,ry,rx,ry+radius);context.closePath()}}};PIXI.CanvasGraphics.updateGraphicsTint=function(graphics){if(graphics.tint===16777215)return;var tintR=(graphics.tint>>16&255)/255;var tintG=(graphics.tint>>8&255)/255;var tintB=(graphics.tint&255)/255;for(var i=0;i>16&255)/255*tintR*255<<16)+((fillColor>>8&255)/255*tintG*255<<8)+(fillColor&255)/255*tintB*255;data._lineTint=((lineColor>>16&255)/255*tintR*255<<16)+((lineColor>>8&255)/255*tintG*255<<8)+(lineColor&255)/255*tintB*255}};PIXI.Graphics=function(){PIXI.DisplayObjectContainer.call(this);this.renderable=true;this.fillAlpha=1;this.lineWidth=0;this.lineColor=0;this.graphicsData=[];this.tint=16777215;this.blendMode=PIXI.blendModes.NORMAL;this.currentPath=null;this._webGL=[];this.isMask=false;this.boundsPadding=0;this._localBounds=new PIXI.Rectangle(0,0,1,1);this.dirty=true;this.webGLDirty=false;this.cachedSpriteDirty=false};PIXI.Graphics.prototype=Object.create(PIXI.DisplayObjectContainer.prototype);PIXI.Graphics.prototype.constructor=PIXI.Graphics;Object.defineProperty(PIXI.Graphics.prototype,"cacheAsBitmap",{get:function(){return this._cacheAsBitmap},set:function(value){this._cacheAsBitmap=value;if(this._cacheAsBitmap){this._generateCachedSprite()}else{this.destroyCachedSprite();this.dirty=true}}});PIXI.Graphics.prototype.lineStyle=function(lineWidth,color,alpha){this.lineWidth=lineWidth||0;this.lineColor=color||0;this.lineAlpha=arguments.length<3?1:alpha;if(this.currentPath){if(this.currentPath.shape.points.length){this.drawShape(new PIXI.Polygon(this.currentPath.shape.points.slice(-2)));return this}this.currentPath.lineWidth=this.lineWidth;this.currentPath.lineColor=this.lineColor;this.currentPath.lineAlpha=this.lineAlpha}return this};PIXI.Graphics.prototype.moveTo=function(x,y){this.drawShape(new PIXI.Polygon([x,y]));return this};PIXI.Graphics.prototype.lineTo=function(x,y){this.currentPath.shape.points.push(x,y);this.dirty=true;return this};PIXI.Graphics.prototype.quadraticCurveTo=function(cpX,cpY,toX,toY){if(this.currentPath){if(this.currentPath.shape.points.length===0)this.currentPath.shape.points=[0,0]}else{this.moveTo(0,0)}var xa,ya,n=20,points=this.currentPath.shape.points;if(points.length===0)this.moveTo(0,0);var fromX=points[points.length-2];var fromY=points[points.length-1];var j=0;for(var i=1;i<=n;i++){j=i/n;xa=fromX+(cpX-fromX)*j;ya=fromY+(cpY-fromY)*j;points.push(xa+(cpX+(toX-cpX)*j-xa)*j,ya+(cpY+(toY-cpY)*j-ya)*j)}this.dirty=true;return this};PIXI.Graphics.prototype.bezierCurveTo=function(cpX,cpY,cpX2,cpY2,toX,toY){if(this.currentPath){if(this.currentPath.shape.points.length===0)this.currentPath.shape.points=[0,0]}else{this.moveTo(0,0)}var n=20,dt,dt2,dt3,t2,t3,points=this.currentPath.shape.points;var fromX=points[points.length-2];var fromY=points[points.length-1];var j=0;for(var i=1;i<=n;i++){j=i/n;dt=1-j;dt2=dt*dt;dt3=dt2*dt;t2=j*j;t3=t2*j;points.push(dt3*fromX+3*dt2*j*cpX+3*dt*t2*cpX2+t3*toX,dt3*fromY+3*dt2*j*cpY+3*dt*t2*cpY2+t3*toY)}this.dirty=true;return this};PIXI.Graphics.prototype.arcTo=function(x1,y1,x2,y2,radius){if(this.currentPath){if(this.currentPath.shape.points.length===0){this.currentPath.shape.points.push(x1,y1)}}else{this.moveTo(x1,y1)}var points=this.currentPath.shape.points;var fromX=points[points.length-2];var fromY=points[points.length-1];var a1=fromY-y1;var b1=fromX-x1;var a2=y2-y1;var b2=x2-x1;var mm=Math.abs(a1*b2-b1*a2);if(mm<1e-8||radius===0){if(points[points.length-2]!==x1||points[points.length-1]!==y1){points.push(x1,y1)}}else{var dd=a1*a1+b1*b1;var cc=a2*a2+b2*b2;var tt=a1*a2+b1*b2;var k1=radius*Math.sqrt(dd)/mm;var k2=radius*Math.sqrt(cc)/mm;var j1=k1*tt/dd;var j2=k2*tt/cc;var cx=k1*b2+k2*b1;var cy=k1*a2+k2*a1;var px=b1*(k2+j1);var py=a1*(k2+j1);var qx=b2*(k1+j2);var qy=a2*(k1+j2);var startAngle=Math.atan2(py-cy,px-cx);var endAngle=Math.atan2(qy-cy,qx-cx);this.arc(cx+x1,cy+y1,radius,startAngle,endAngle,b1*a2>b2*a1)}this.dirty=true;return this};PIXI.Graphics.prototype.arc=function(cx,cy,radius,startAngle,endAngle,anticlockwise){var startX=cx+Math.cos(startAngle)*radius;var startY=cy+Math.sin(startAngle)*radius;var points;if(this.currentPath){points=this.currentPath.shape.points;if(points.length===0){points.push(startX,startY)}else if(points[points.length-2]!==startX||points[points.length-1]!==startY){points.push(startX,startY)}}else{this.moveTo(startX,startY);points=this.currentPath.shape.points}if(startAngle===endAngle)return this;if(!anticlockwise&&endAngle<=startAngle){endAngle+=Math.PI*2}else if(anticlockwise&&startAngle<=endAngle){startAngle+=Math.PI*2}var sweep=anticlockwise?(startAngle-endAngle)*-1:endAngle-startAngle;var segs=Math.abs(sweep)/(Math.PI*2)*40;if(sweep===0)return this;var theta=sweep/(segs*2);var theta2=theta*2;var cTheta=Math.cos(theta);var sTheta=Math.sin(theta);var segMinus=segs-1;var remainder=segMinus%1/segMinus;for(var i=0;i<=segMinus;i++){var real=i+remainder*i;var angle=theta+startAngle+theta2*real;var c=Math.cos(angle);var s=-Math.sin(angle);points.push((cTheta*c+sTheta*s)*radius+cx,(cTheta*-s+sTheta*c)*radius+cy)}this.dirty=true;return this};PIXI.Graphics.prototype.beginFill=function(color,alpha){this.filling=true;this.fillColor=color||0;this.fillAlpha=alpha===undefined?1:alpha;if(this.currentPath){if(this.currentPath.shape.points.length<=2){this.currentPath.fill=this.filling;this.currentPath.fillColor=this.fillColor;this.currentPath.fillAlpha=this.fillAlpha}}return this};PIXI.Graphics.prototype.endFill=function(){this.filling=false;this.fillColor=null;this.fillAlpha=1;return this};PIXI.Graphics.prototype.drawRect=function(x,y,width,height){this.drawShape(new PIXI.Rectangle(x,y,width,height));return this};PIXI.Graphics.prototype.drawRoundedRect=function(x,y,width,height,radius){this.drawShape(new PIXI.RoundedRectangle(x,y,width,height,radius));return this};PIXI.Graphics.prototype.drawCircle=function(x,y,radius){this.drawShape(new PIXI.Circle(x,y,radius));return this};PIXI.Graphics.prototype.drawEllipse=function(x,y,width,height){this.drawShape(new PIXI.Ellipse(x,y,width,height));return this};PIXI.Graphics.prototype.drawPolygon=function(path){if(!(path instanceof Array))path=Array.prototype.slice.call(arguments);this.drawShape(new PIXI.Polygon(path));return this};PIXI.Graphics.prototype.clear=function(){this.lineWidth=0;this.filling=false;this.dirty=true;this.clearDirty=true;this.graphicsData=[];return this};PIXI.Graphics.prototype.generateTexture=function(resolution,scaleMode){resolution=resolution||1; -var bounds=this.getBounds();var canvasBuffer=new PIXI.CanvasBuffer(bounds.width*resolution,bounds.height*resolution);var texture=PIXI.Texture.fromCanvas(canvasBuffer.canvas,scaleMode);texture.baseTexture.resolution=resolution;canvasBuffer.context.scale(resolution,resolution);canvasBuffer.context.translate(-bounds.x,-bounds.y);PIXI.CanvasGraphics.renderGraphics(this,canvasBuffer.context);return texture};PIXI.Graphics.prototype._renderWebGL=function(renderSession){if(this.visible===false||this.alpha===0||this.isMask===true)return;if(this._cacheAsBitmap){if(this.dirty||this.cachedSpriteDirty){this._generateCachedSprite();this.updateCachedSpriteTexture();this.cachedSpriteDirty=false;this.dirty=false}this._cachedSprite.worldAlpha=this.worldAlpha;PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite,renderSession);return}else{renderSession.spriteBatch.stop();renderSession.blendModeManager.setBlendMode(this.blendMode);if(this._mask)renderSession.maskManager.pushMask(this._mask,renderSession);if(this._filters)renderSession.filterManager.pushFilter(this._filterBlock);if(this.blendMode!==renderSession.spriteBatch.currentBlendMode){renderSession.spriteBatch.currentBlendMode=this.blendMode;var blendModeWebGL=PIXI.blendModesWebGL[renderSession.spriteBatch.currentBlendMode];renderSession.spriteBatch.gl.blendFunc(blendModeWebGL[0],blendModeWebGL[1])}if(this.webGLDirty){this.dirty=true;this.webGLDirty=false}PIXI.WebGLGraphics.renderGraphics(this,renderSession);if(this.children.length){renderSession.spriteBatch.start();for(var i=0,j=this.children.length;imaxX?x2:maxX;maxX=x3>maxX?x3:maxX;maxX=x4>maxX?x4:maxX;maxY=y2>maxY?y2:maxY;maxY=y3>maxY?y3:maxY;maxY=y4>maxY?y4:maxY;this._bounds.x=minX;this._bounds.width=maxX-minX;this._bounds.y=minY;this._bounds.height=maxY-minY;return this._bounds};PIXI.Graphics.prototype.updateLocalBounds=function(){var minX=Infinity;var maxX=-Infinity;var minY=Infinity;var maxY=-Infinity;if(this.graphicsData.length){var shape,points,x,y,w,h;for(var i=0;imaxX?x+w:maxX;minY=ymaxY?y+h:maxY}else if(type===PIXI.Graphics.CIRC){x=shape.x;y=shape.y;w=shape.radius+lineWidth/2;h=shape.radius+lineWidth/2;minX=x-wmaxX?x+w:maxX;minY=y-hmaxY?y+h:maxY}else if(type===PIXI.Graphics.ELIP){x=shape.x;y=shape.y;w=shape.width+lineWidth/2;h=shape.height+lineWidth/2;minX=x-wmaxX?x+w:maxX;minY=y-hmaxY?y+h:maxY}else{points=shape.points;for(var j=0;jmaxX?x+lineWidth:maxX;minY=y-lineWidthmaxY?y+lineWidth:maxY}}}}else{minX=0;maxX=0;minY=0;maxY=0}var padding=this.boundsPadding;this._localBounds.x=minX-padding;this._localBounds.width=maxX-minX+padding*2;this._localBounds.y=minY-padding;this._localBounds.height=maxY-minY+padding*2};PIXI.Graphics.prototype._generateCachedSprite=function(){var bounds=this.getLocalBounds();if(!this._cachedSprite){var canvasBuffer=new PIXI.CanvasBuffer(bounds.width,bounds.height);var texture=PIXI.Texture.fromCanvas(canvasBuffer.canvas);this._cachedSprite=new PIXI.Sprite(texture);this._cachedSprite.buffer=canvasBuffer;this._cachedSprite.worldTransform=this.worldTransform}else{this._cachedSprite.buffer.resize(bounds.width,bounds.height)}this._cachedSprite.anchor.x=-(bounds.x/bounds.width);this._cachedSprite.anchor.y=-(bounds.y/bounds.height);this._cachedSprite.buffer.context.translate(-bounds.x,-bounds.y);this.worldAlpha=1;PIXI.CanvasGraphics.renderGraphics(this,this._cachedSprite.buffer.context);this._cachedSprite.alpha=this.alpha};PIXI.Graphics.prototype.updateCachedSpriteTexture=function(){var cachedSprite=this._cachedSprite;var texture=cachedSprite.texture;var canvas=cachedSprite.buffer.canvas;texture.baseTexture.width=canvas.width;texture.baseTexture.height=canvas.height;texture.crop.width=texture.frame.width=canvas.width;texture.crop.height=texture.frame.height=canvas.height;cachedSprite._width=canvas.width;cachedSprite._height=canvas.height;texture.baseTexture.dirty()};PIXI.Graphics.prototype.destroyCachedSprite=function(){this._cachedSprite.texture.destroy(true);this._cachedSprite=null};PIXI.Graphics.prototype.drawShape=function(shape){if(this.currentPath){if(this.currentPath.shape.points.length<=2)this.graphicsData.pop()}this.currentPath=null;var data=new PIXI.GraphicsData(this.lineWidth,this.lineColor,this.lineAlpha,this.fillColor,this.fillAlpha,this.filling,shape);this.graphicsData.push(data);if(data.type===PIXI.Graphics.POLY){data.shape.closed=this.filling;this.currentPath=data}this.dirty=true;return data};PIXI.GraphicsData=function(lineWidth,lineColor,lineAlpha,fillColor,fillAlpha,fill,shape){this.lineWidth=lineWidth;this.lineColor=lineColor;this.lineAlpha=lineAlpha;this._lineTint=lineColor;this.fillColor=fillColor;this.fillAlpha=fillAlpha;this._fillTint=fillColor;this.fill=fill;this.shape=shape;this.type=shape.type};PIXI.Graphics.POLY=0;PIXI.Graphics.RECT=1;PIXI.Graphics.CIRC=2;PIXI.Graphics.ELIP=3;PIXI.Graphics.RREC=4;PIXI.Polygon.prototype.type=PIXI.Graphics.POLY;PIXI.Rectangle.prototype.type=PIXI.Graphics.RECT;PIXI.Circle.prototype.type=PIXI.Graphics.CIRC;PIXI.Ellipse.prototype.type=PIXI.Graphics.ELIP;PIXI.RoundedRectangle.prototype.type=PIXI.Graphics.RREC;PIXI.Strip=function(texture){PIXI.DisplayObjectContainer.call(this);this.texture=texture;this.uvs=new PIXI.Float32Array([0,1,1,1,1,0,0,1]);this.vertices=new PIXI.Float32Array([0,0,100,0,100,100,0,100]);this.colors=new PIXI.Float32Array([1,1,1,1]);this.indices=new PIXI.Uint16Array([0,1,2,3]);this.dirty=true;this.blendMode=PIXI.blendModes.NORMAL;this.canvasPadding=0;this.drawMode=PIXI.Strip.DrawModes.TRIANGLE_STRIP};PIXI.Strip.prototype=Object.create(PIXI.DisplayObjectContainer.prototype);PIXI.Strip.prototype.constructor=PIXI.Strip;PIXI.Strip.prototype._renderWebGL=function(renderSession){if(!this.visible||this.alpha<=0)return;renderSession.spriteBatch.stop();if(!this._vertexBuffer)this._initWebGL(renderSession);renderSession.shaderManager.setShader(renderSession.shaderManager.stripShader);this._renderStrip(renderSession);renderSession.spriteBatch.start()};PIXI.Strip.prototype._initWebGL=function(renderSession){var gl=renderSession.gl;this._vertexBuffer=gl.createBuffer();this._indexBuffer=gl.createBuffer();this._uvBuffer=gl.createBuffer();this._colorBuffer=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,this._vertexBuffer);gl.bufferData(gl.ARRAY_BUFFER,this.vertices,gl.DYNAMIC_DRAW);gl.bindBuffer(gl.ARRAY_BUFFER,this._uvBuffer);gl.bufferData(gl.ARRAY_BUFFER,this.uvs,gl.STATIC_DRAW);gl.bindBuffer(gl.ARRAY_BUFFER,this._colorBuffer);gl.bufferData(gl.ARRAY_BUFFER,this.colors,gl.STATIC_DRAW);gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,this._indexBuffer);gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,this.indices,gl.STATIC_DRAW)};PIXI.Strip.prototype._renderStrip=function(renderSession){var gl=renderSession.gl;var projection=renderSession.projection,offset=renderSession.offset,shader=renderSession.shaderManager.stripShader;var drawMode=this.drawMode===PIXI.Strip.DrawModes.TRIANGLE_STRIP?gl.TRIANGLE_STRIP:gl.TRIANGLES;renderSession.blendModeManager.setBlendMode(this.blendMode);gl.uniformMatrix3fv(shader.translationMatrix,false,this.worldTransform.toArray(true));gl.uniform2f(shader.projectionVector,projection.x,-projection.y);gl.uniform2f(shader.offsetVector,-offset.x,-offset.y);gl.uniform1f(shader.alpha,this.worldAlpha);if(!this.dirty){gl.bindBuffer(gl.ARRAY_BUFFER,this._vertexBuffer);gl.bufferSubData(gl.ARRAY_BUFFER,0,this.vertices);gl.vertexAttribPointer(shader.aVertexPosition,2,gl.FLOAT,false,0,0);gl.bindBuffer(gl.ARRAY_BUFFER,this._uvBuffer);gl.vertexAttribPointer(shader.aTextureCoord,2,gl.FLOAT,false,0,0);gl.activeTexture(gl.TEXTURE0);if(this.texture.baseTexture._dirty[gl.id]){renderSession.renderer.updateTexture(this.texture.baseTexture)}else{gl.bindTexture(gl.TEXTURE_2D,this.texture.baseTexture._glTextures[gl.id])}gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,this._indexBuffer)}else{this.dirty=false;gl.bindBuffer(gl.ARRAY_BUFFER,this._vertexBuffer);gl.bufferData(gl.ARRAY_BUFFER,this.vertices,gl.STATIC_DRAW);gl.vertexAttribPointer(shader.aVertexPosition,2,gl.FLOAT,false,0,0);gl.bindBuffer(gl.ARRAY_BUFFER,this._uvBuffer);gl.bufferData(gl.ARRAY_BUFFER,this.uvs,gl.STATIC_DRAW);gl.vertexAttribPointer(shader.aTextureCoord,2,gl.FLOAT,false,0,0);gl.activeTexture(gl.TEXTURE0);if(this.texture.baseTexture._dirty[gl.id]){renderSession.renderer.updateTexture(this.texture.baseTexture)}else{gl.bindTexture(gl.TEXTURE_2D,this.texture.baseTexture._glTextures[gl.id])}gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,this._indexBuffer);gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,this.indices,gl.STATIC_DRAW)}gl.drawElements(drawMode,this.indices.length,gl.UNSIGNED_SHORT,0)};PIXI.Strip.prototype._renderCanvas=function(renderSession){var context=renderSession.context;var transform=this.worldTransform;if(renderSession.roundPixels){context.setTransform(transform.a,transform.b,transform.c,transform.d,transform.tx|0,transform.ty|0)}else{context.setTransform(transform.a,transform.b,transform.c,transform.d,transform.tx,transform.ty)}if(this.drawMode===PIXI.Strip.DrawModes.TRIANGLE_STRIP){this._renderCanvasTriangleStrip(context)}else{this._renderCanvasTriangles(context)}};PIXI.Strip.prototype._renderCanvasTriangleStrip=function(context){var vertices=this.vertices;var uvs=this.uvs;var length=vertices.length/2;this.count++;for(var i=0;i0){var paddingX=this.canvasPadding/this.worldTransform.a;var paddingY=this.canvasPadding/this.worldTransform.d;var centerX=(x0+x1+x2)/3;var centerY=(y0+y1+y2)/3;var normX=x0-centerX;var normY=y0-centerY;var dist=Math.sqrt(normX*normX+normY*normY);x0=centerX+normX/dist*(dist+paddingX);y0=centerY+normY/dist*(dist+paddingY);normX=x1-centerX;normY=y1-centerY;dist=Math.sqrt(normX*normX+normY*normY);x1=centerX+normX/dist*(dist+paddingX);y1=centerY+normY/dist*(dist+paddingY);normX=x2-centerX;normY=y2-centerY;dist=Math.sqrt(normX*normX+normY*normY);x2=centerX+normX/dist*(dist+paddingX);y2=centerY+normY/dist*(dist+paddingY)}context.save();context.beginPath();context.moveTo(x0,y0);context.lineTo(x1,y1);context.lineTo(x2,y2);context.closePath();context.clip();var delta=u0*v1+v0*u2+u1*v2-v1*u2-v0*u1-u0*v2;var deltaA=x0*v1+v0*x2+x1*v2-v1*x2-v0*x1-x0*v2;var deltaB=u0*x1+x0*u2+u1*x2-x1*u2-x0*u1-u0*x2;var deltaC=u0*v1*x2+v0*x1*u2+x0*u1*v2-x0*v1*u2-v0*u1*x2-u0*x1*v2;var deltaD=y0*v1+v0*y2+y1*v2-v1*y2-v0*y1-y0*v2;var deltaE=u0*y1+y0*u2+u1*y2-y1*u2-y0*u1-u0*y2;var deltaF=u0*v1*y2+v0*y1*u2+y0*u1*v2-y0*v1*u2-v0*u1*y2-u0*y1*v2;context.transform(deltaA/delta,deltaD/delta,deltaB/delta,deltaE/delta,deltaC/delta,deltaF/delta);context.drawImage(textureSource,0,0);context.restore()};PIXI.Strip.prototype.renderStripFlat=function(strip){var context=this.context;var vertices=strip.vertices;var length=vertices.length/2;this.count++;context.beginPath();for(var i=1;imaxX?x:maxX;maxY=y>maxY?y:maxY}if(minX===-Infinity||maxY===Infinity){return PIXI.EmptyRectangle}var bounds=this._bounds;bounds.x=minX;bounds.width=maxX-minX;bounds.y=minY;bounds.height=maxY-minY;this._currentBounds=bounds;return bounds};PIXI.Strip.DrawModes={TRIANGLE_STRIP:0,TRIANGLES:1};PIXI.Rope=function(texture,points){PIXI.Strip.call(this,texture);this.points=points;this.vertices=new PIXI.Float32Array(points.length*4);this.uvs=new PIXI.Float32Array(points.length*4);this.colors=new PIXI.Float32Array(points.length*2);this.indices=new PIXI.Uint16Array(points.length*2);this.refresh()};PIXI.Rope.prototype=Object.create(PIXI.Strip.prototype);PIXI.Rope.prototype.constructor=PIXI.Rope;PIXI.Rope.prototype.refresh=function(){var points=this.points;if(points.length<1)return;var uvs=this.uvs;var lastPoint=points[0];var indices=this.indices;var colors=this.colors;this.count-=.2;uvs[0]=0;uvs[1]=0;uvs[2]=0;uvs[3]=1;colors[0]=1;colors[1]=1;indices[0]=0;indices[1]=1;var total=points.length,point,index,amount;for(var i=1;i1)ratio=1;perpLength=Math.sqrt(perp.x*perp.x+perp.y*perp.y);num=this.texture.height/2;perp.x/=perpLength;perp.y/=perpLength;perp.x*=num;perp.y*=num;vertices[index]=point.x+perp.x;vertices[index+1]=point.y+perp.y;vertices[index+2]=point.x-perp.x;vertices[index+3]=point.y-perp.y;lastPoint=point}PIXI.DisplayObjectContainer.prototype.updateTransform.call(this)};PIXI.Rope.prototype.setTexture=function(texture){this.texture=texture};PIXI.TilingSprite=function(texture,width,height){PIXI.Sprite.call(this,texture);this._width=width||100;this._height=height||100;this.tileScale=new PIXI.Point(1,1);this.tileScaleOffset=new PIXI.Point(1,1);this.tilePosition=new PIXI.Point(0,0);this.renderable=true;this.tint=16777215;this.blendMode=PIXI.blendModes.NORMAL};PIXI.TilingSprite.prototype=Object.create(PIXI.Sprite.prototype);PIXI.TilingSprite.prototype.constructor=PIXI.TilingSprite;Object.defineProperty(PIXI.TilingSprite.prototype,"width",{get:function(){return this._width},set:function(value){this._width=value}});Object.defineProperty(PIXI.TilingSprite.prototype,"height",{get:function(){return this._height},set:function(value){this._height=value}});PIXI.TilingSprite.prototype.setTexture=function(texture){if(this.texture===texture)return;this.texture=texture;this.refreshTexture=true;this.cachedTint=16777215};PIXI.TilingSprite.prototype._renderWebGL=function(renderSession){if(this.visible===false||this.alpha===0)return;var i,j;if(this._mask){renderSession.spriteBatch.stop();renderSession.maskManager.pushMask(this.mask,renderSession);renderSession.spriteBatch.start()}if(this._filters){renderSession.spriteBatch.flush();renderSession.filterManager.pushFilter(this._filterBlock)}if(!this.tilingTexture||this.refreshTexture){this.generateTilingTexture(true);if(this.tilingTexture&&this.tilingTexture.needsUpdate){renderSession.renderer.updateTexture(this.tilingTexture.baseTexture);this.tilingTexture.needsUpdate=false}}else{renderSession.spriteBatch.renderTilingSprite(this)}for(i=0,j=this.children.length;imaxX?x1:maxX;maxX=x2>maxX?x2:maxX;maxX=x3>maxX?x3:maxX;maxX=x4>maxX?x4:maxX;maxY=y1>maxY?y1:maxY;maxY=y2>maxY?y2:maxY;maxY=y3>maxY?y3:maxY;maxY=y4>maxY?y4:maxY;var bounds=this._bounds;bounds.x=minX;bounds.width=maxX-minX;bounds.y=minY;bounds.height=maxY-minY;this._currentBounds=bounds;return bounds};PIXI.TilingSprite.prototype.onTextureUpdate=function(){};PIXI.TilingSprite.prototype.generateTilingTexture=function(forcePowerOfTwo){if(!this.texture.baseTexture.hasLoaded)return;var texture=this.originalTexture||this.texture;var frame=texture.frame;var targetWidth,targetHeight;var isFrame=frame.width!==texture.baseTexture.width||frame.height!==texture.baseTexture.height;var newTextureRequired=false;if(!forcePowerOfTwo){if(isFrame){targetWidth=frame.width;targetHeight=frame.height;newTextureRequired=true}}else{targetWidth=PIXI.getNextPowerOfTwo(frame.width);targetHeight=PIXI.getNextPowerOfTwo(frame.height);if(frame.width!==targetWidth||frame.height!==targetHeight||texture.baseTexture.width!==targetWidth||texture.baseTexture.height||targetHeight)newTextureRequired=true}if(newTextureRequired){var canvasBuffer;if(this.tilingTexture&&this.tilingTexture.isTiling){canvasBuffer=this.tilingTexture.canvasBuffer;canvasBuffer.resize(targetWidth,targetHeight);this.tilingTexture.baseTexture.width=targetWidth;this.tilingTexture.baseTexture.height=targetHeight;this.tilingTexture.needsUpdate=true}else{canvasBuffer=new PIXI.CanvasBuffer(targetWidth,targetHeight);this.tilingTexture=PIXI.Texture.fromCanvas(canvasBuffer.canvas);this.tilingTexture.canvasBuffer=canvasBuffer;this.tilingTexture.isTiling=true}canvasBuffer.context.drawImage(texture.baseTexture.source,texture.crop.x,texture.crop.y,texture.crop.width,texture.crop.height,0,0,targetWidth,targetHeight);this.tileScaleOffset.x=frame.width/targetWidth;this.tileScaleOffset.y=frame.height/targetHeight}else{if(this.tilingTexture&&this.tilingTexture.isTiling){this.tilingTexture.destroy(true)}this.tileScaleOffset.x=1;this.tileScaleOffset.y=1;this.tilingTexture=texture}this.refreshTexture=false;this.originalTexture=this.texture;this.texture=this.tilingTexture;this.tilingTexture.baseTexture._powerOf2=true};PIXI.TilingSprite.prototype.destroy=function(){PIXI.Sprite.prototype.destroy.call(this);this.tileScale=null;this.tileScaleOffset=null;this.tilePosition=null;this.tilingTexture.destroy(true);this.tilingTexture=null};var spine={radDeg:180/Math.PI,degRad:Math.PI/180,temp:[],Float32Array:typeof Float32Array==="undefined"?Array:Float32Array,Uint16Array:typeof Uint16Array==="undefined"?Array:Uint16Array};spine.BoneData=function(name,parent){this.name=name;this.parent=parent};spine.BoneData.prototype={length:0,x:0,y:0,rotation:0,scaleX:1,scaleY:1,inheritScale:true,inheritRotation:true,flipX:false,flipY:false};spine.SlotData=function(name,boneData){this.name=name;this.boneData=boneData};spine.SlotData.prototype={r:1,g:1,b:1,a:1,attachmentName:null,additiveBlending:false};spine.IkConstraintData=function(name){this.name=name;this.bones=[]};spine.IkConstraintData.prototype={target:null,bendDirection:1,mix:1};spine.Bone=function(boneData,skeleton,parent){this.data=boneData;this.skeleton=skeleton;this.parent=parent;this.setToSetupPose()};spine.Bone.yDown=false;spine.Bone.prototype={x:0,y:0,rotation:0,rotationIK:0,scaleX:1,scaleY:1,flipX:false,flipY:false,m00:0,m01:0,worldX:0,m10:0,m11:0,worldY:0,worldRotation:0,worldScaleX:1,worldScaleY:1,worldFlipX:false,worldFlipY:false,updateWorldTransform:function(){var parent=this.parent;if(parent){this.worldX=this.x*parent.m00+this.y*parent.m01+parent.worldX;this.worldY=this.x*parent.m10+this.y*parent.m11+parent.worldY;if(this.data.inheritScale){this.worldScaleX=parent.worldScaleX*this.scaleX;this.worldScaleY=parent.worldScaleY*this.scaleY}else{this.worldScaleX=this.scaleX;this.worldScaleY=this.scaleY}this.worldRotation=this.data.inheritRotation?parent.worldRotation+this.rotationIK:this.rotationIK;this.worldFlipX=parent.worldFlipX!=this.flipX;this.worldFlipY=parent.worldFlipY!=this.flipY}else{var skeletonFlipX=this.skeleton.flipX,skeletonFlipY=this.skeleton.flipY;this.worldX=skeletonFlipX?-this.x:this.x;this.worldY=skeletonFlipY!=spine.Bone.yDown?-this.y:this.y;this.worldScaleX=this.scaleX;this.worldScaleY=this.scaleY;this.worldRotation=this.rotationIK;this.worldFlipX=skeletonFlipX!=this.flipX;this.worldFlipY=skeletonFlipY!=this.flipY}var radians=this.worldRotation*spine.degRad;var cos=Math.cos(radians);var sin=Math.sin(radians);if(this.worldFlipX){this.m00=-cos*this.worldScaleX;this.m01=sin*this.worldScaleY}else{this.m00=cos*this.worldScaleX;this.m01=-sin*this.worldScaleY}if(this.worldFlipY!=spine.Bone.yDown){this.m10=-sin*this.worldScaleX;this.m11=-cos*this.worldScaleY}else{this.m10=sin*this.worldScaleX;this.m11=cos*this.worldScaleY}},setToSetupPose:function(){var data=this.data;this.x=data.x;this.y=data.y;this.rotation=data.rotation;this.rotationIK=this.rotation;this.scaleX=data.scaleX;this.scaleY=data.scaleY;this.flipX=data.flipX;this.flipY=data.flipY},worldToLocal:function(world){var dx=world[0]-this.worldX,dy=world[1]-this.worldY;var m00=this.m00,m10=this.m10,m01=this.m01,m11=this.m11;if(this.worldFlipX!=(this.worldFlipY!=spine.Bone.yDown)){m00=-m00;m11=-m11}var invDet=1/(m00*m11-m01*m10);world[0]=dx*m00*invDet-dy*m01*invDet;world[1]=dy*m11*invDet-dx*m10*invDet},localToWorld:function(local){var localX=local[0],localY=local[1];local[0]=localX*this.m00+localY*this.m01+this.worldX;local[1]=localX*this.m10+localY*this.m11+this.worldY}};spine.Slot=function(slotData,bone){this.data=slotData;this.bone=bone;this.setToSetupPose()};spine.Slot.prototype={r:1,g:1,b:1,a:1,_attachmentTime:0,attachment:null,attachmentVertices:[],setAttachment:function(attachment){this.attachment=attachment;this._attachmentTime=this.bone.skeleton.time;this.attachmentVertices.length=0},setAttachmentTime:function(time){this._attachmentTime=this.bone.skeleton.time-time},getAttachmentTime:function(){return this.bone.skeleton.time-this._attachmentTime},setToSetupPose:function(){var data=this.data;this.r=data.r;this.g=data.g;this.b=data.b;this.a=data.a;var slotDatas=this.bone.skeleton.data.slots;for(var i=0,n=slotDatas.length;i1)cos=1;var childAngle=Math.acos(cos)*bendDirection;var adjacent=len1+len2*cos,opposite=len2*Math.sin(childAngle);var parentAngle=Math.atan2(targetY*adjacent-targetX*opposite,targetX*adjacent+targetY*opposite);var rotation=(parentAngle-offset)*spine.radDeg-parentRotation;if(rotation>180)rotation-=360;else if(rotation<-180)rotation+=360;parent.rotationIK=parentRotation+rotation*alpha;rotation=(childAngle+offset)*spine.radDeg-childRotation;if(rotation>180)rotation-=360;else if(rotation<-180)rotation+=360;child.rotationIK=childRotation+(rotation+parent.worldRotation-child.parent.worldRotation)*alpha};spine.Skin=function(name){this.name=name;this.attachments={}};spine.Skin.prototype={addAttachment:function(slotIndex,name,attachment){this.attachments[slotIndex+":"+name]=attachment},getAttachment:function(slotIndex,name){return this.attachments[slotIndex+":"+name]},_attachAll:function(skeleton,oldSkin){for(var key in oldSkin.attachments){var colon=key.indexOf(":");var slotIndex=parseInt(key.substring(0,colon));var name=key.substring(colon+1);var slot=skeleton.slots[slotIndex];if(slot.attachment&&slot.attachment.name==name){var attachment=this.getAttachment(slotIndex,name);if(attachment)slot.setAttachment(attachment)}}}};spine.Animation=function(name,timelines,duration){this.name=name;this.timelines=timelines;this.duration=duration};spine.Animation.prototype={apply:function(skeleton,lastTime,time,loop,events){if(loop&&this.duration!=0){time%=this.duration;lastTime%=this.duration}var timelines=this.timelines;for(var i=0,n=timelines.length;i>>1;while(true){if(values[(current+1)*step]<=target)low=current+1;else high=current;if(low==high)return(low+1)*step;current=low+high>>>1}};spine.Animation.binarySearch1=function(values,target){var low=0;var high=values.length-2;if(!high)return 1;var current=high>>>1;while(true){if(values[current+1]<=target)low=current+1;else high=current;if(low==high)return low+1;current=low+high>>>1}};spine.Animation.linearSearch=function(values,target,step){for(var i=0,last=values.length-step;i<=last;i+=step)if(values[i]>target)return i;return-1};spine.Curves=function(frameCount){this.curves=[]};spine.Curves.prototype={setLinear:function(frameIndex){this.curves[frameIndex*19]=0},setStepped:function(frameIndex){this.curves[frameIndex*19]=1},setCurve:function(frameIndex,cx1,cy1,cx2,cy2){var subdiv1=1/10,subdiv2=subdiv1*subdiv1,subdiv3=subdiv2*subdiv1;var pre1=3*subdiv1,pre2=3*subdiv2,pre4=6*subdiv2,pre5=6*subdiv3;var tmp1x=-cx1*2+cx2,tmp1y=-cy1*2+cy2,tmp2x=(cx1-cx2)*3+1,tmp2y=(cy1-cy2)*3+1;var dfx=cx1*pre1+tmp1x*pre2+tmp2x*subdiv3,dfy=cy1*pre1+tmp1y*pre2+tmp2y*subdiv3;var ddfx=tmp1x*pre4+tmp2x*pre5,ddfy=tmp1y*pre4+tmp2y*pre5;var dddfx=tmp2x*pre5,dddfy=tmp2y*pre5;var i=frameIndex*19;var curves=this.curves;curves[i++]=2;var x=dfx,y=dfy;for(var n=i+19-1;i1?1:percent;var curves=this.curves;var i=frameIndex*19;var type=curves[i];if(type===0)return percent;if(type==1)return 0;i++;var x=0;for(var start=i,n=i+19-1;i=percent){var prevX,prevY;if(i==start){prevX=0;prevY=0}else{prevX=curves[i-2];prevY=curves[i-1]}return prevY+(curves[i+1]-prevY)*(percent-prevX)/(x-prevX)}}var y=curves[i-1];return y+(1-y)*(percent-x)/(1-x)}};spine.RotateTimeline=function(frameCount){this.curves=new spine.Curves(frameCount);this.frames=[];this.frames.length=frameCount*2};spine.RotateTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/2},setFrame:function(frameIndex,time,angle){frameIndex*=2;this.frames[frameIndex]=time;this.frames[frameIndex+1]=angle},apply:function(skeleton,lastTime,time,firedEvents,alpha){var frames=this.frames;if(time=frames[frames.length-2]){var amount=bone.data.rotation+frames[frames.length-1]-bone.rotation;while(amount>180)amount-=360;while(amount<-180)amount+=360;bone.rotation+=amount*alpha;return}var frameIndex=spine.Animation.binarySearch(frames,time,2);var prevFrameValue=frames[frameIndex-1];var frameTime=frames[frameIndex];var percent=1-(time-frameTime)/(frames[frameIndex-2]-frameTime);percent=this.curves.getCurvePercent(frameIndex/2-1,percent);var amount=frames[frameIndex+1]-prevFrameValue;while(amount>180)amount-=360;while(amount<-180)amount+=360;amount=bone.data.rotation+(prevFrameValue+amount*percent)-bone.rotation;while(amount>180)amount-=360;while(amount<-180)amount+=360;bone.rotation+=amount*alpha}};spine.TranslateTimeline=function(frameCount){this.curves=new spine.Curves(frameCount);this.frames=[];this.frames.length=frameCount*3};spine.TranslateTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/3},setFrame:function(frameIndex,time,x,y){frameIndex*=3;this.frames[frameIndex]=time;this.frames[frameIndex+1]=x;this.frames[frameIndex+2]=y},apply:function(skeleton,lastTime,time,firedEvents,alpha){var frames=this.frames;if(time=frames[frames.length-3]){bone.x+=(bone.data.x+frames[frames.length-2]-bone.x)*alpha;bone.y+=(bone.data.y+frames[frames.length-1]-bone.y)*alpha;return}var frameIndex=spine.Animation.binarySearch(frames,time,3);var prevFrameX=frames[frameIndex-2];var prevFrameY=frames[frameIndex-1];var frameTime=frames[frameIndex];var percent=1-(time-frameTime)/(frames[frameIndex+-3]-frameTime);percent=this.curves.getCurvePercent(frameIndex/3-1,percent);bone.x+=(bone.data.x+prevFrameX+(frames[frameIndex+1]-prevFrameX)*percent-bone.x)*alpha;bone.y+=(bone.data.y+prevFrameY+(frames[frameIndex+2]-prevFrameY)*percent-bone.y)*alpha}};spine.ScaleTimeline=function(frameCount){this.curves=new spine.Curves(frameCount);this.frames=[];this.frames.length=frameCount*3};spine.ScaleTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/3},setFrame:function(frameIndex,time,x,y){frameIndex*=3;this.frames[frameIndex]=time;this.frames[frameIndex+1]=x;this.frames[frameIndex+2]=y},apply:function(skeleton,lastTime,time,firedEvents,alpha){var frames=this.frames;if(time=frames[frames.length-3]){bone.scaleX+=(bone.data.scaleX*frames[frames.length-2]-bone.scaleX)*alpha;bone.scaleY+=(bone.data.scaleY*frames[frames.length-1]-bone.scaleY)*alpha;return}var frameIndex=spine.Animation.binarySearch(frames,time,3);var prevFrameX=frames[frameIndex-2];var prevFrameY=frames[frameIndex-1];var frameTime=frames[frameIndex];var percent=1-(time-frameTime)/(frames[frameIndex+-3]-frameTime);percent=this.curves.getCurvePercent(frameIndex/3-1,percent);bone.scaleX+=(bone.data.scaleX*(prevFrameX+(frames[frameIndex+1]-prevFrameX)*percent)-bone.scaleX)*alpha;bone.scaleY+=(bone.data.scaleY*(prevFrameY+(frames[frameIndex+2]-prevFrameY)*percent)-bone.scaleY)*alpha}};spine.ColorTimeline=function(frameCount){this.curves=new spine.Curves(frameCount);this.frames=[];this.frames.length=frameCount*5};spine.ColorTimeline.prototype={slotIndex:0,getFrameCount:function(){return this.frames.length/5},setFrame:function(frameIndex,time,r,g,b,a){frameIndex*=5;this.frames[frameIndex]=time;this.frames[frameIndex+1]=r;this.frames[frameIndex+2]=g;this.frames[frameIndex+3]=b;this.frames[frameIndex+4]=a},apply:function(skeleton,lastTime,time,firedEvents,alpha){var frames=this.frames;if(time=frames[frames.length-5]){var i=frames.length-1;r=frames[i-3];g=frames[i-2];b=frames[i-1];a=frames[i]}else{var frameIndex=spine.Animation.binarySearch(frames,time,5);var prevFrameR=frames[frameIndex-4];var prevFrameG=frames[frameIndex-3];var prevFrameB=frames[frameIndex-2];var prevFrameA=frames[frameIndex-1];var frameTime=frames[frameIndex];var percent=1-(time-frameTime)/(frames[frameIndex-5]-frameTime);percent=this.curves.getCurvePercent(frameIndex/5-1,percent);r=prevFrameR+(frames[frameIndex+1]-prevFrameR)*percent;g=prevFrameG+(frames[frameIndex+2]-prevFrameG)*percent;b=prevFrameB+(frames[frameIndex+3]-prevFrameB)*percent;a=prevFrameA+(frames[frameIndex+4]-prevFrameA)*percent}var slot=skeleton.slots[this.slotIndex];if(alpha<1){slot.r+=(r-slot.r)*alpha;slot.g+=(g-slot.g)*alpha;slot.b+=(b-slot.b)*alpha;slot.a+=(a-slot.a)*alpha}else{slot.r=r;slot.g=g;slot.b=b;slot.a=a}}};spine.AttachmentTimeline=function(frameCount){this.curves=new spine.Curves(frameCount);this.frames=[];this.frames.length=frameCount;this.attachmentNames=[];this.attachmentNames.length=frameCount};spine.AttachmentTimeline.prototype={slotIndex:0,getFrameCount:function(){return this.frames.length},setFrame:function(frameIndex,time,attachmentName){this.frames[frameIndex]=time;this.attachmentNames[frameIndex]=attachmentName},apply:function(skeleton,lastTime,time,firedEvents,alpha){var frames=this.frames;if(timetime)this.apply(skeleton,lastTime,Number.MAX_VALUE,null,0);return}else if(lastTime>time)lastTime=-1;var frameIndex=time>=frames[frames.length-1]?frames.length-1:spine.Animation.binarySearch1(frames,time)-1;if(frames[frameIndex]time){this.apply(skeleton,lastTime,Number.MAX_VALUE,firedEvents,alpha);lastTime=-1}else if(lastTime>=frames[frameCount-1])return;if(time0){if(frames[frameIndex-1]!=frame)break;frameIndex--}}var events=this.events;for(;frameIndex=frames[frameIndex];frameIndex++)firedEvents.push(events[frameIndex])}};spine.DrawOrderTimeline=function(frameCount){this.frames=[];this.frames.length=frameCount;this.drawOrders=[];this.drawOrders.length=frameCount};spine.DrawOrderTimeline.prototype={getFrameCount:function(){return this.frames.length},setFrame:function(frameIndex,time,drawOrder){this.frames[frameIndex]=time;this.drawOrders[frameIndex]=drawOrder},apply:function(skeleton,lastTime,time,firedEvents,alpha){var frames=this.frames;if(time=frames[frames.length-1])frameIndex=frames.length-1;else frameIndex=spine.Animation.binarySearch1(frames,time)-1;var drawOrder=skeleton.drawOrder;var slots=skeleton.slots;var drawOrderToSetupIndex=this.drawOrders[frameIndex];if(!drawOrderToSetupIndex){for(var i=0,n=slots.length;i=frames[frames.length-1]){var lastVertices=frameVertices[frames.length-1];if(alpha<1){for(var i=0;i1?1:percent);var prevVertices=frameVertices[frameIndex-1];var nextVertices=frameVertices[frameIndex];if(alpha<1){for(var i=0;i=frames[frames.length-3]){ikConstraint.mix+=(frames[frames.length-2]-ikConstraint.mix)*alpha;ikConstraint.bendDirection=frames[frames.length-1];return}var frameIndex=spine.Animation.binarySearch(frames,time,3);var prevFrameMix=frames[frameIndex+-2];var frameTime=frames[frameIndex];var percent=1-(time-frameTime)/(frames[frameIndex+-3]-frameTime);percent=this.curves.getCurvePercent(frameIndex/3-1,percent);var mix=prevFrameMix+(frames[frameIndex+1]-prevFrameMix)*percent;ikConstraint.mix+=(mix-ikConstraint.mix)*alpha;ikConstraint.bendDirection=frames[frameIndex+-1]}};spine.FlipXTimeline=function(frameCount){this.curves=new spine.Curves(frameCount);this.frames=[];this.frames.length=frameCount*2};spine.FlipXTimeline.prototype={boneIndex:0,getFrameCount:function(){return this.frames.length/2},setFrame:function(frameIndex,time,flip){frameIndex*=2;this.frames[frameIndex]=time;this.frames[frameIndex+1]=flip?1:0},apply:function(skeleton,lastTime,time,firedEvents,alpha){var frames=this.frames;if(timetime)this.apply(skeleton,lastTime,Number.MAX_VALUE,null,0);return}else if(lastTime>time)lastTime=-1;var frameIndex=(time>=frames[frames.length-2]?frames.length:spine.Animation.binarySearch(frames,time,2))-2;if(frames[frameIndex]time)this.apply(skeleton,lastTime,Number.MAX_VALUE,null,0);return}else if(lastTime>time)lastTime=-1;var frameIndex=(time>=frames[frames.length-2]?frames.length:spine.Animation.binarySearch(frames,time,2))-2;if(frames[frameIndex]arrayCount)boneCache.length=arrayCount;for(var i=0,n=boneCache.length;i=0)this.setCurrent(i,next)}else{if(!current.loop&¤t.lastTime>=current.endTime)this.clearTrack(i)}}},apply:function(skeleton){for(var i=0;iendTime)time=endTime;var previous=current.previous;if(!previous){if(current.mix==1)current.animation.apply(skeleton,current.lastTime,time,loop,this.events);else current.animation.mix(skeleton,current.lastTime,time,loop,this.events,current.mix)}else{var previousTime=previous.time;if(!previous.loop&&previousTime>previous.endTime)previousTime=previous.endTime;previous.animation.apply(skeleton,previousTime,previousTime,previous.loop,null);var alpha=current.mixTime/current.mixDuration*current.mix;if(alpha>=1){alpha=1;current.previous=null}current.animation.mix(skeleton,current.lastTime,time,loop,this.events,alpha)}for(var ii=0,nn=this.events.length;iitime%endTime:lastTime=endTime){var count=Math.floor(time/endTime);if(current.onComplete)current.onComplete(i,count);if(this.onComplete)this.onComplete(i,count)}current.lastTime=current.time}},clearTracks:function(){for(var i=0,n=this.tracks.length;i=this.tracks.length)return;var current=this.tracks[trackIndex];if(!current)return;if(current.onEnd)current.onEnd(trackIndex);if(this.onEnd)this.onEnd(trackIndex);this.tracks[trackIndex]=null},_expandToIndex:function(index){if(index=this.tracks.length)this.tracks.push(null);return null},setCurrent:function(index,entry){var current=this._expandToIndex(index);if(current){var previous=current.previous;current.previous=null;if(current.onEnd)current.onEnd(index);if(this.onEnd)this.onEnd(index);entry.mixDuration=this.data.getMix(current.animation,entry.animation);if(entry.mixDuration>0){entry.mixTime=0;if(previous&¤t.mixTime/current.mixDuration<.5)entry.previous=previous;else entry.previous=current}}this.tracks[index]=entry;if(entry.onStart)entry.onStart(index);if(this.onStart)this.onStart(index)},setAnimationByName:function(trackIndex,animationName,loop){var animation=this.data.skeletonData.findAnimation(animationName);if(!animation)throw"Animation not found: "+animationName;return this.setAnimation(trackIndex,animation,loop)},setAnimation:function(trackIndex,animation,loop){var entry=new spine.TrackEntry;entry.animation=animation;entry.loop=loop;entry.endTime=animation.duration;this.setCurrent(trackIndex,entry);return entry},addAnimationByName:function(trackIndex,animationName,loop,delay){var animation=this.data.skeletonData.findAnimation(animationName);if(!animation)throw"Animation not found: "+animationName;return this.addAnimation(trackIndex,animation,loop,delay)},addAnimation:function(trackIndex,animation,loop,delay){var entry=new spine.TrackEntry;entry.animation=animation;entry.loop=loop;entry.endTime=animation.duration;var last=this._expandToIndex(trackIndex);if(last){while(last.next)last=last.next;last.next=entry}else this.tracks[trackIndex]=entry;if(delay<=0){if(last)delay+=last.endTime-this.data.getMix(last.animation,animation);else delay=0}entry.delay=delay;return entry},getCurrent:function(trackIndex){if(trackIndex>=this.tracks.length)return null;return this.tracks[trackIndex]}};spine.SkeletonJson=function(attachmentLoader){this.attachmentLoader=attachmentLoader};spine.SkeletonJson.prototype={scale:1,readSkeletonData:function(root,name){var skeletonData=new spine.SkeletonData;skeletonData.name=name;var skeletonMap=root["skeleton"];if(skeletonMap){skeletonData.hash=skeletonMap["hash"]; -skeletonData.version=skeletonMap["spine"];skeletonData.width=skeletonMap["width"]||0;skeletonData.height=skeletonMap["height"]||0}var bones=root["bones"];for(var i=0,n=bones.length;i=0;ii--)drawOrder[ii]=-1;var offsets=drawOrderMap["offsets"];var unchanged=[];unchanged.length=slotCount-offsets.length;var originalIndex=0,unchangedIndex=0;for(var ii=0,nn=offsets.length;ii=0;ii--)if(drawOrder[ii]==-1)drawOrder[ii]=unchanged[--unchangedIndex]}timeline.setFrame(frameIndex++,drawOrderMap["time"],drawOrder)}timelines.push(timeline);duration=Math.max(duration,timeline.frames[timeline.getFrameCount()-1])}var events=map["events"];if(events){var timeline=new spine.EventTimeline(events.length);var frameIndex=0;for(var i=0,n=events.length;i=this.lines.length)return null;return this.lines[this.index++]},readValue:function(){var line=this.readLine();var colon=line.indexOf(":");if(colon==-1)throw"Invalid line: "+line;return this.trim(line.substring(colon+1))},readTuple:function(tuple){var line=this.readLine();var colon=line.indexOf(":");if(colon==-1)throw"Invalid line: "+line;var i=0,lastMatch=colon+1;for(;i<3;i++){var comma=line.indexOf(",",lastMatch);if(comma==-1)break;tuple[i]=this.trim(line.substr(lastMatch,comma-lastMatch));lastMatch=comma+1}tuple[i]=this.trim(line.substring(lastMatch));return i+1}};spine.AtlasAttachmentLoader=function(atlas){this.atlas=atlas};spine.AtlasAttachmentLoader.prototype={newRegionAttachment:function(skin,name,path){var region=this.atlas.findRegion(path);if(!region)throw"Region not found in atlas: "+path+" (region attachment: "+name+")";var attachment=new spine.RegionAttachment(name);attachment.rendererObject=region;attachment.setUVs(region.u,region.v,region.u2,region.v2,region.rotate);attachment.regionOffsetX=region.offsetX;attachment.regionOffsetY=region.offsetY;attachment.regionWidth=region.width;attachment.regionHeight=region.height;attachment.regionOriginalWidth=region.originalWidth;attachment.regionOriginalHeight=region.originalHeight;return attachment},newMeshAttachment:function(skin,name,path){var region=this.atlas.findRegion(path);if(!region)throw"Region not found in atlas: "+path+" (mesh attachment: "+name+")";var attachment=new spine.MeshAttachment(name);attachment.rendererObject=region;attachment.regionU=region.u;attachment.regionV=region.v;attachment.regionU2=region.u2;attachment.regionV2=region.v2;attachment.regionRotate=region.rotate;attachment.regionOffsetX=region.offsetX;attachment.regionOffsetY=region.offsetY;attachment.regionWidth=region.width;attachment.regionHeight=region.height;attachment.regionOriginalWidth=region.originalWidth;attachment.regionOriginalHeight=region.originalHeight;return attachment},newSkinnedMeshAttachment:function(skin,name,path){var region=this.atlas.findRegion(path);if(!region)throw"Region not found in atlas: "+path+" (skinned mesh attachment: "+name+")";var attachment=new spine.SkinnedMeshAttachment(name);attachment.rendererObject=region;attachment.regionU=region.u;attachment.regionV=region.v;attachment.regionU2=region.u2;attachment.regionV2=region.v2;attachment.regionRotate=region.rotate;attachment.regionOffsetX=region.offsetX;attachment.regionOffsetY=region.offsetY;attachment.regionWidth=region.width;attachment.regionHeight=region.height;attachment.regionOriginalWidth=region.originalWidth;attachment.regionOriginalHeight=region.originalHeight;return attachment},newBoundingBoxAttachment:function(skin,name){return new spine.BoundingBoxAttachment(name)}};spine.SkeletonBounds=function(){this.polygonPool=[];this.polygons=[];this.boundingBoxes=[]};spine.SkeletonBounds.prototype={minX:0,minY:0,maxX:0,maxY:0,update:function(skeleton,updateAabb){var slots=skeleton.slots;var slotCount=slots.length;var x=skeleton.x,y=skeleton.y;var boundingBoxes=this.boundingBoxes;var polygonPool=this.polygonPool;var polygons=this.polygons;boundingBoxes.length=0;for(var i=0,n=polygons.length;i0){polygon=polygonPool[poolCount-1];polygonPool.splice(poolCount-1,1)}else polygon=[];polygons.push(polygon);polygon.length=boundingBox.vertices.length;boundingBox.computeWorldVertices(x,y,slot.bone,polygon)}if(updateAabb)this.aabbCompute()},aabbCompute:function(){var polygons=this.polygons;var minX=Number.MAX_VALUE,minY=Number.MAX_VALUE,maxX=Number.MIN_VALUE,maxY=Number.MIN_VALUE;for(var i=0,n=polygons.length;i=this.minX&&x<=this.maxX&&y>=this.minY&&y<=this.maxY},aabbIntersectsSegment:function(x1,y1,x2,y2){var minX=this.minX,minY=this.minY,maxX=this.maxX,maxY=this.maxY;if(x1<=minX&&x2<=minX||y1<=minY&&y2<=minY||x1>=maxX&&x2>=maxX||y1>=maxY&&y2>=maxY)return false;var m=(y2-y1)/(x2-x1);var y=m*(minX-x1)+y1;if(y>minY&&yminY&&yminX&&xminX&&xbounds.minX&&this.minYbounds.minY},containsPoint:function(x,y){var polygons=this.polygons;for(var i=0,n=polygons.length;i=y||prevY=y){var vertexX=polygon[ii];if(vertexX+(y-vertexY)/(prevY-vertexY)*(polygon[prevIndex]-vertexX)=x3&&x<=x4||x>=x4&&x<=x3)&&(x>=x1&&x<=x2||x>=x2&&x<=x1)){var y=(det1*height34-height12*det2)/det3;if((y>=y3&&y<=y4||y>=y4&&y<=y3)&&(y>=y1&&y<=y2||y>=y2&&y<=y1))return true}x3=x4;y3=y4}return false},getPolygon:function(attachment){var index=this.boundingBoxes.indexOf(attachment);return index==-1?null:this.polygons[index]},getWidth:function(){return this.maxX-this.minX},getHeight:function(){return this.maxY-this.minY}};spine.Bone.yDown=true;PIXI.AnimCache={};PIXI.SpineTextureLoader=function(basePath,crossorigin){PIXI.EventTarget.call(this);this.basePath=basePath;this.crossorigin=crossorigin;this.loadingCount=0};PIXI.SpineTextureLoader.prototype=PIXI.SpineTextureLoader;PIXI.SpineTextureLoader.prototype.load=function(page,file){page.rendererObject=PIXI.BaseTexture.fromImage(this.basePath+"/"+file,this.crossorigin);if(!page.rendererObject.hasLoaded){var scope=this;++scope.loadingCount;page.rendererObject.addEventListener("loaded",function(){--scope.loadingCount;scope.dispatchEvent({type:"loadedBaseTexture",content:scope})})}};PIXI.SpineTextureLoader.prototype.unload=function(texture){texture.destroy(true)};PIXI.Spine=function(url){PIXI.DisplayObjectContainer.call(this);this.spineData=PIXI.AnimCache[url];if(!this.spineData){throw new Error("Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: "+url)}this.skeleton=new spine.Skeleton(this.spineData);this.skeleton.updateWorldTransform();this.stateData=new spine.AnimationStateData(this.spineData);this.state=new spine.AnimationState(this.stateData);this.slotContainers=[];for(var i=0,n=this.skeleton.drawOrder.length;i=0;i--){var glTexture=this._glTextures[i];var gl=PIXI.glContexts[i];if(gl&&glTexture){gl.deleteTexture(glTexture)}}this._glTextures.length=0;this.dirty()};PIXI.BaseTexture.fromImage=function(imageUrl,crossorigin,scaleMode){var baseTexture=PIXI.BaseTextureCache[imageUrl];if(crossorigin===undefined&&imageUrl.indexOf("data:")===-1)crossorigin=true;if(!baseTexture){var image=new Image;if(crossorigin){image.crossOrigin=""}image.src=imageUrl;baseTexture=new PIXI.BaseTexture(image,scaleMode);baseTexture.imageUrl=imageUrl;PIXI.BaseTextureCache[imageUrl]=baseTexture;if(imageUrl.indexOf(PIXI.RETINA_PREFIX+".")!==-1){baseTexture.resolution=2}}return baseTexture};PIXI.BaseTexture.fromCanvas=function(canvas,scaleMode){if(!canvas._pixiId){canvas._pixiId="canvas_"+PIXI.TextureCacheIdGenerator++}var baseTexture=PIXI.BaseTextureCache[canvas._pixiId];if(!baseTexture){baseTexture=new PIXI.BaseTexture(canvas,scaleMode);PIXI.BaseTextureCache[canvas._pixiId]=baseTexture}return baseTexture};PIXI.TextureCache={};PIXI.FrameCache={};PIXI.TextureCacheIdGenerator=0;PIXI.Texture=function(baseTexture,frame,crop,trim){this.noFrame=false;if(!frame){this.noFrame=true;frame=new PIXI.Rectangle(0,0,1,1)}if(baseTexture instanceof PIXI.Texture){baseTexture=baseTexture.baseTexture}this.baseTexture=baseTexture;this.frame=frame;this.trim=trim;this.valid=false;this.requiresUpdate=false;this._uvs=null;this.width=0;this.height=0;this.crop=crop||new PIXI.Rectangle(0,0,1,1);if(baseTexture.hasLoaded){if(this.noFrame)frame=new PIXI.Rectangle(0,0,baseTexture.width,baseTexture.height);this.setFrame(frame)}else{baseTexture.addEventListener("loaded",this.onBaseTextureLoaded.bind(this))}};PIXI.Texture.prototype.constructor=PIXI.Texture;PIXI.EventTarget.mixin(PIXI.Texture.prototype);PIXI.Texture.prototype.onBaseTextureLoaded=function(){var baseTexture=this.baseTexture;baseTexture.removeEventListener("loaded",this.onLoaded);if(this.noFrame)this.frame=new PIXI.Rectangle(0,0,baseTexture.width,baseTexture.height);this.setFrame(this.frame);this.dispatchEvent({type:"update",content:this})};PIXI.Texture.prototype.destroy=function(destroyBase){if(destroyBase)this.baseTexture.destroy();this.valid=false};PIXI.Texture.prototype.setFrame=function(frame){this.noFrame=false;this.frame=frame;this.width=frame.width;this.height=frame.height;this.crop.x=frame.x;this.crop.y=frame.y;this.crop.width=frame.width;this.crop.height=frame.height;if(!this.trim&&(frame.x+frame.width>this.baseTexture.width||frame.y+frame.height>this.baseTexture.height)){throw new Error("Texture Error: frame does not fit inside the base Texture dimensions "+this)}this.valid=frame&&frame.width&&frame.height&&this.baseTexture.source&&this.baseTexture.hasLoaded;if(this.trim){this.width=this.trim.width;this.height=this.trim.height;this.frame.width=this.trim.width;this.frame.height=this.trim.height}if(this.valid)this._updateUvs()};PIXI.Texture.prototype._updateUvs=function(){if(!this._uvs)this._uvs=new PIXI.TextureUvs;var frame=this.crop;var tw=this.baseTexture.width;var th=this.baseTexture.height;this._uvs.x0=frame.x/tw;this._uvs.y0=frame.y/th;this._uvs.x1=(frame.x+frame.width)/tw;this._uvs.y1=frame.y/th;this._uvs.x2=(frame.x+frame.width)/tw;this._uvs.y2=(frame.y+frame.height)/th;this._uvs.x3=frame.x/tw;this._uvs.y3=(frame.y+frame.height)/th};PIXI.Texture.fromImage=function(imageUrl,crossorigin,scaleMode){var texture=PIXI.TextureCache[imageUrl];if(!texture){texture=new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl,crossorigin,scaleMode));PIXI.TextureCache[imageUrl]=texture}return texture};PIXI.Texture.fromFrame=function(frameId){var texture=PIXI.TextureCache[frameId];if(!texture)throw new Error('The frameId "'+frameId+'" does not exist in the texture cache ');return texture};PIXI.Texture.fromCanvas=function(canvas,scaleMode){var baseTexture=PIXI.BaseTexture.fromCanvas(canvas,scaleMode);return new PIXI.Texture(baseTexture)};PIXI.Texture.addTextureToCache=function(texture,id){PIXI.TextureCache[id]=texture};PIXI.Texture.removeTextureFromCache=function(id){var texture=PIXI.TextureCache[id];delete PIXI.TextureCache[id];delete PIXI.BaseTextureCache[id];return texture};PIXI.TextureUvs=function(){this.x0=0;this.y0=0;this.x1=0;this.y1=0;this.x2=0;this.y2=0;this.x3=0;this.y3=0};PIXI.Texture.emptyTexture=new PIXI.Texture(new PIXI.BaseTexture);PIXI.RenderTexture=function(width,height,renderer,scaleMode,resolution){this.width=width||100;this.height=height||100;this.resolution=resolution||1;this.frame=new PIXI.Rectangle(0,0,this.width*this.resolution,this.height*this.resolution);this.crop=new PIXI.Rectangle(0,0,this.width*this.resolution,this.height*this.resolution);this.baseTexture=new PIXI.BaseTexture;this.baseTexture.width=this.width*this.resolution;this.baseTexture.height=this.height*this.resolution;this.baseTexture._glTextures=[];this.baseTexture.resolution=this.resolution;this.baseTexture.scaleMode=scaleMode||PIXI.scaleModes.DEFAULT;this.baseTexture.hasLoaded=true;PIXI.Texture.call(this,this.baseTexture,new PIXI.Rectangle(0,0,this.width,this.height));this.renderer=renderer||PIXI.defaultRenderer;if(this.renderer.type===PIXI.WEBGL_RENDERER){var gl=this.renderer.gl;this.baseTexture._dirty[gl.id]=false;this.textureBuffer=new PIXI.FilterTexture(gl,this.width*this.resolution,this.height*this.resolution,this.baseTexture.scaleMode);this.baseTexture._glTextures[gl.id]=this.textureBuffer.texture;this.render=this.renderWebGL;this.projection=new PIXI.Point(this.width*.5,-this.height*.5)}else{this.render=this.renderCanvas;this.textureBuffer=new PIXI.CanvasBuffer(this.width*this.resolution,this.height*this.resolution);this.baseTexture.source=this.textureBuffer.canvas}this.valid=true;this._updateUvs()};PIXI.RenderTexture.prototype=Object.create(PIXI.Texture.prototype);PIXI.RenderTexture.prototype.constructor=PIXI.RenderTexture;PIXI.RenderTexture.prototype.resize=function(width,height,updateBase){if(width===this.width&&height===this.height)return;this.valid=width>0&&height>0;this.width=this.frame.width=this.crop.width=width;this.height=this.frame.height=this.crop.height=height;if(updateBase){this.baseTexture.width=this.width;this.baseTexture.height=this.height}if(this.renderer.type===PIXI.WEBGL_RENDERER){this.projection.x=this.width/2;this.projection.y=-this.height/2}if(!this.valid)return;this.textureBuffer.resize(this.width*this.resolution,this.height*this.resolution)};PIXI.RenderTexture.prototype.clear=function(){if(!this.valid)return;if(this.renderer.type===PIXI.WEBGL_RENDERER){this.renderer.gl.bindFramebuffer(this.renderer.gl.FRAMEBUFFER,this.textureBuffer.frameBuffer)}this.textureBuffer.clear()};PIXI.RenderTexture.prototype.renderWebGL=function(displayObject,matrix,clear){if(!this.valid)return;var wt=displayObject.worldTransform;wt.identity();wt.translate(0,this.projection.y*2);if(matrix)wt.append(matrix);wt.scale(1,-1);displayObject.worldAlpha=1;var children=displayObject.children;for(var i=0,j=children.length;i0){textureLoader.addEventListener("loadedBaseTexture",function(evt){if(evt.content.content.loadingCount<=0){originalLoader.onLoaded()}})}else{originalLoader.onLoaded()}};atlasLoader.load()}}else{this.onLoaded()}};PIXI.JsonLoader.prototype.onLoaded=function(){this.loaded=true;this.dispatchEvent({type:"loaded",content:this})};PIXI.JsonLoader.prototype.onError=function(){this.dispatchEvent({type:"error",content:this})};PIXI.AtlasLoader=function(url,crossorigin){this.url=url;this.baseUrl=url.replace(/[^\/]*$/,"");this.crossorigin=crossorigin;this.loaded=false};PIXI.AtlasLoader.constructor=PIXI.AtlasLoader;PIXI.EventTarget.mixin(PIXI.AtlasLoader.prototype);PIXI.AtlasLoader.prototype.load=function(){this.ajaxRequest=new PIXI.AjaxRequest;this.ajaxRequest.onreadystatechange=this.onAtlasLoaded.bind(this);this.ajaxRequest.open("GET",this.url,true);if(this.ajaxRequest.overrideMimeType)this.ajaxRequest.overrideMimeType("application/json");this.ajaxRequest.send(null)};PIXI.AtlasLoader.prototype.onAtlasLoaded=function(){if(this.ajaxRequest.readyState===4){if(this.ajaxRequest.status===200||window.location.href.indexOf("http")===-1){this.atlas={meta:{image:[]},frames:[]};var result=this.ajaxRequest.responseText.split(/\r?\n/);var lineCount=-3;var currentImageId=0;var currentFrame=null;var nameInNextLine=false;var i=0,j=0,selfOnLoaded=this.onLoaded.bind(this);for(i=0;i0){if(nameInNextLine===i){this.atlas.meta.image.push(result[i]);currentImageId=this.atlas.meta.image.length-1;this.atlas.frames.push({});lineCount=-3}else if(lineCount>0){if(lineCount%7===1){if(currentFrame!=null){this.atlas.frames[currentImageId][currentFrame.name]=currentFrame}currentFrame={name:result[i],frame:{}}}else{var text=result[i].split(" ");if(lineCount%7===3){currentFrame.frame.x=Number(text[1].replace(",",""));currentFrame.frame.y=Number(text[2])}else if(lineCount%7===4){currentFrame.frame.w=Number(text[1].replace(",",""));currentFrame.frame.h=Number(text[2])}else if(lineCount%7===5){var realSize={x:0,y:0,w:Number(text[1].replace(",","")),h:Number(text[2])};if(realSize.w>currentFrame.frame.w||realSize.h>currentFrame.frame.h){currentFrame.trimmed=true;currentFrame.realSize=realSize}else{currentFrame.trimmed=false}}}}lineCount++}}if(currentFrame!=null){this.atlas.frames[currentImageId][currentFrame.name]=currentFrame}if(this.atlas.meta.image.length>0){this.images=[];for(j=0;jthis.currentImageId){this.currentImageId++;this.images[this.currentImageId].load()}else{this.loaded=true;this.emit("loaded",{content:this})}};PIXI.AtlasLoader.prototype.onError=function(){this.emit("error",{content:this})};PIXI.SpriteSheetLoader=function(url,crossorigin){this.url=url;this.crossorigin=crossorigin;this.baseUrl=url.replace(/[^\/]*$/,"");this.texture=null;this.frames={}};PIXI.SpriteSheetLoader.prototype.constructor=PIXI.SpriteSheetLoader;PIXI.EventTarget.mixin(PIXI.SpriteSheetLoader.prototype);PIXI.SpriteSheetLoader.prototype.load=function(){var scope=this;var jsonLoader=new PIXI.JsonLoader(this.url,this.crossorigin);jsonLoader.on("loaded",function(event){scope.json=event.data.content.json;scope.onLoaded()});jsonLoader.load()};PIXI.SpriteSheetLoader.prototype.onLoaded=function(){this.emit("loaded",{content:this})};PIXI.ImageLoader=function(url,crossorigin){this.texture=PIXI.Texture.fromImage(url,crossorigin);this.frames=[]};PIXI.ImageLoader.prototype.constructor=PIXI.ImageLoader;PIXI.EventTarget.mixin(PIXI.ImageLoader.prototype);PIXI.ImageLoader.prototype.load=function(){if(!this.texture.baseTexture.hasLoaded){this.texture.baseTexture.on("loaded",this.onLoaded.bind(this))}else{this.onLoaded()}};PIXI.ImageLoader.prototype.onLoaded=function(){this.emit("loaded",{content:this})};PIXI.ImageLoader.prototype.loadFramedSpriteSheet=function(frameWidth,frameHeight,textureName){this.frames=[];var cols=Math.floor(this.texture.width/frameWidth);var rows=Math.floor(this.texture.height/frameHeight);var i=0;for(var y=0;y>16&255,g:hex>>8&255,b:hex&255}}function rgbToHex(r,g,b){return r<<16|g<<8|b}function interpolate(a,b,amt){amt=amt===undefined?.5:amt;var colorA=hexToRgb(a),colorB=hexToRgb(b),interpolated={r:colorA.r+(colorB.r-colorA.r)*amt,g:colorA.g+(colorB.g-colorA.g)*amt,b:colorA.b+(colorB.b-colorA.b)*amt};return rgbToHex(interpolated.r,interpolated.g,interpolated.b)}function parse(c){var color=parseInt(c);if(typeof c==="string"){if(c.split("#").length>1){color=parseInt(c.replace("#",""),16)}else if(c.split("rgb(").length>1){var rgb=c.substring(4,c.length-1).replace(/ /g,"").split(",");color=rgbToHex(rgb[0],rgb[1],rgb[2])}}return color}},{}],5:[function(require,module,exports){var Utilities=module.exports={each:each,eachPop:eachPop,eachKey:eachKey,map:map,clean:clean,range:range,sortedIndex:sortedIndex,indexOf:indexOf,uniqueInsert:uniqueInsert,extend:extend,bind:bind,noop:noop,isUndefined:isUndefined,isFunction:isFunction,isObject:isObject,isArray:Array.isArray,isNumber:isNumber,isNaN:isNaN};function noop(){}function each(arr,fn,ctx){fn=bind(fn,ctx);var i=arr.length;while(--i>-1){fn(arr[i],i)}return arr}function eachPop(arr,fn,ctx){fn=bind(fn,ctx);while(arr.length){fn(arr.pop())}return arr}function eachKey(obj,fn,ctx){fn=bind(fn,ctx);if(isObject(obj)){var keys=Object.keys(obj);while(keys.length){var key=keys.pop();fn(obj[key],key)}}return obj}function map(arr,fn,ctx){fn=bind(fn,ctx);var i=arr.length,mapped=new Array(i);while(--i>-1){mapped[i]=fn(arr[i],i)}return mapped}function clean(arr){eachPop(arr,noop);return arr}function range(start,end,step){step=isNumber(step)?step:1;if(isUndefined(end)){end=start;start=0}var i=Math.max(Math.ceil((end-start)/step),0),result=new Array(i);while(--i>-1){result[i]=start+step*i}return result}function sortedIndex(arr,n){var min=0,max=arr.length;while(min>>1;if(n-1){if(arr[i]===n)return i}return i}function uniqueInsert(arr,n){if(indexOf(arr,n)===-1)arr.push(n);return arr}function extend(obj,source){if(isObject(obj)&&isObject(source)){var props=Object.getOwnPropertyNames(source),i=props.length;while(--i>-1){var prop=props[i];obj[prop]=source[prop]}}return obj}function bind(fn,ctx){if(!ctx)return fn;return function(){return fn.apply(ctx,arguments)}}function isUndefined(o){return typeof o==="undefined"}function isFunction(o){return typeof o==="function"}function isObject(o){return typeof o==="object"&&!!o}function isNumber(o){return typeof o==="number"}function isNaN(o){return isNumber(o)&&o!==+o}},{}]},{},{1:""}); \ No newline at end of file +(function umd(require){if("object"==typeof exports){module.exports=require("1")}else if("function"==typeof define&&define.amd){define(function(){return require("1")})}else{this["Grapher"]=require("1")}})(function outer(modules,cache,entries){var global=function(){return this}();function require(name,jumped){if(cache[name])return cache[name].exports;if(modules[name])return call(name,require);throw new Error('cannot find module "'+name+'"')}function call(id,require){var m=cache[id]={exports:{}};var mod=modules[id];var name=mod[2];var fn=mod[0];fn.call(m.exports,function(req){var dep=modules[id][1][req];return require(dep?dep:req)},m,m.exports,outer,modules,cache,entries);if(name)cache[name]=cache[id];return cache[id].exports}for(var id in entries){if(entries[id]){global[entries[id]]=require(id)}else{require(id)}}require.duo=true;require.cache=cache;require.modules=modules;return require}({1:[function(require,module,exports){(function(){function Grapher(){this.initialize.apply(this,arguments);return this}var WebGLRenderer=Grapher.WebGLRenderer=require("./renderers/gl/renderer.js"),CanvasRenderer=Grapher.CanvasRenderer=require("./renderers/canvas/renderer.js"),Color=Grapher.Color=require("./helpers/color.js"),Link=Grapher.Link=require("./helpers/link.js"),Node=Grapher.Node=require("./helpers/node.js"),u=Grapher.utils=require("./helpers/utilities.js");Grapher.prototype={};Grapher.prototype.initialize=function(o){if(!o)o={};this.props=u.extend({color:2236962,scale:1,translate:[0,0],resolution:window.devicePixelRatio||1},o);if(!o.canvas)this.props.canvas=document.createElement("canvas");if(!o.width)this.props.width=this.props.canvas.clientWidth;if(!o.height)this.props.height=this.props.canvas.clientHeight;this.canvas=this.props.canvas;var webGL=this._getWebGL();if(webGL){this.props.webGL=webGL;this.props.canvas.addEventListener("webglcontextlost",function(e){this._onContextLost(e)}.bind(this));this.props.canvas.addEventListener("webglcontextrestored",function(e){this._onContextRestored(e)}.bind(this))}this.renderer=webGL?new WebGLRenderer(this.props):new CanvasRenderer(this.props);this.rendered=false;this.resize(this.props.width,this.props.height);this.links=[];this.nodes=[];this.renderer.setLinks(this.links);this.renderer.setNodes(this.nodes);this.willUpdate={};this.updateAll={};this._clearUpdateQueue();this._updateLink=u.bind(this._updateLink,this);this._updateNode=u.bind(this._updateNode,this);this._updateLinkByIndex=u.bind(this._updateLinkByIndex,this);this._updateNodeByIndex=u.bind(this._updateNodeByIndex,this);this.animate=u.bind(this.animate,this);this.handlers={};u.eachKey(o,this.set,this)};Grapher.prototype.set=function(val,key){var setter=this[key];if(setter&&u.isFunction(setter))return setter.call(this,val)};Grapher.prototype.on=function(event,fn){this.handlers[event]=this.handlers[event]||[];this.handlers[event].push(fn);this.canvas.addEventListener(event,fn,false);return this};Grapher.prototype.off=function(event,fn){var i=u.indexOf(this.handlers[event],fn);if(i>-1)this.handlers[event].splice(i,1);this.canvas.removeEventListener(event,fn,false);return this};Grapher.prototype.data=function(data){if(u.isUndefined(data))return this.props.data;this.props.data=data;this.exit();this.enter();this.update();return this};Grapher.prototype.enter=function(){var data=this.data();if(this.links.length=spriteSet.length;if(type===NODES)this.updateAll.nodes=updateAll;else this.updateAll.links=updateAll};Grapher.prototype._clearUpdateQueue=function(){this.willUpdate.links=[];this.willUpdate.nodes=[];this.updateAll.links=false;this.updateAll.nodes=false;this.updateTransform=false};Grapher.prototype._update=function(){var updatingLinks=this.willUpdate.links,updatingNodes=this.willUpdate.nodes,i;if(this.updateAll.links)u.each(this.links,this._updateLink);else if(updatingLinks&&updatingLinks.length)u.eachPop(updatingLinks,this._updateLinkByIndex);if(this.updateAll.nodes)u.each(this.nodes,this._updateNode);else if(updatingNodes&&updatingNodes.length)u.eachPop(updatingNodes,this._updateNodeByIndex);if(this.updateTransform){this.renderer.setScale(this.props.scale);this.renderer.setTranslate(this.props.translate)}this._clearUpdateQueue()};Grapher.prototype._updateLink=function(link,i){var data=this.data(),l=data.links[i],from=data.nodes[l.from],to=data.nodes[l.to];var color=!u.isUndefined(l.color)?this._findColor(l.color):Color.interpolate(this._findColor(from.color),this._findColor(to.color));link.update(from.x,from.y,to.x,to.y,color)};Grapher.prototype._updateNode=function(node,i){var n=this.data().nodes[i];node.update(n.x,n.y,n.r,this._findColor(n.color))};Grapher.prototype._updateNodeByIndex=function(i){this._updateNode(this.nodes[i],i)};Grapher.prototype._updateLinkByIndex=function(i){this._updateLink(this.links[i],i)};var isLinked=function(indices,l){var i,len=indices.length,flag=false;for(i=0;i>16&255,g:hex>>8&255,b:hex&255}}function rgbToHex(r,g,b){return r<<16|g<<8|b}function interpolate(a,b,amt){amt=amt===undefined?.5:amt;var colorA=hexToRgb(a),colorB=hexToRgb(b),interpolated={r:colorA.r+(colorB.r-colorA.r)*amt,g:colorA.g+(colorB.g-colorA.g)*amt,b:colorA.b+(colorB.b-colorA.b)*amt};return rgbToHex(interpolated.r,interpolated.g,interpolated.b)}function parse(c){var color=parseInt(c);if(typeof c==="string"){if(c.split("#").length>1){color=parseInt(c.replace("#",""),16)}else if(c.split("rgb(").length>1){var rgb=c.substring(4,c.length-1).replace(/ /g,"").split(",");color=rgbToHex(rgb[0],rgb[1],rgb[2])}}return color}function toRgb(intColor){var r=intColor>>16&255;var g=intColor>>8&255;var b=intColor&255;return"rgb("+r+", "+g+", "+b+")"}},{}],5:[function(require,module,exports){(function(){function Link(){this.x1=0;this.y1=0;this.x2=0;this.y2=0;this.color=0;return this}Link.prototype.update=function(x1,y1,x2,y2,color){this.x1=x1;this.y1=y1;this.x2=x2;this.y2=y2;this.color=color;return this};if(module&&module.exports)module.exports=Link})()},{}],6:[function(require,module,exports){(function(){function Node(){this.x=0;this.y=0;this.r=10;this.color=0;return this}Node.prototype.update=function(x,y,r,color){this.x=x;this.y=y;this.r=r;this.color=color;return this};if(module&&module.exports)module.exports=Node})()},{}],7:[function(require,module,exports){var Utilities=module.exports={each:each,eachPop:eachPop,eachKey:eachKey,map:map,clean:clean,range:range,sortedIndex:sortedIndex,indexOf:indexOf,uniqueInsert:uniqueInsert,extend:extend,bind:bind,noop:noop,isUndefined:isUndefined,isFunction:isFunction,isObject:isObject,isArray:Array.isArray,isNumber:isNumber,isNaN:isNaN};function noop(){}function each(arr,fn,ctx){fn=bind(fn,ctx);var i=arr.length;while(--i>-1){fn(arr[i],i)}return arr}function eachPop(arr,fn,ctx){fn=bind(fn,ctx);while(arr.length){fn(arr.pop())}return arr}function eachKey(obj,fn,ctx){fn=bind(fn,ctx);if(isObject(obj)){var keys=Object.keys(obj);while(keys.length){var key=keys.pop();fn(obj[key],key)}}return obj}function map(arr,fn,ctx){fn=bind(fn,ctx);var i=arr.length,mapped=new Array(i);while(--i>-1){mapped[i]=fn(arr[i],i)}return mapped}function clean(arr){eachPop(arr,noop);return arr}function range(start,end,step){step=isNumber(step)?step:1;if(isUndefined(end)){end=start;start=0}var i=Math.max(Math.ceil((end-start)/step),0),result=new Array(i);while(--i>-1){result[i]=start+step*i}return result}function sortedIndex(arr,n){var min=0,max=arr.length;while(min>>1;if(n-1){if(arr[i]===n)return i}return i}function uniqueInsert(arr,n){if(indexOf(arr,n)===-1)arr.push(n);return arr}function extend(obj,source){if(isObject(obj)&&isObject(source)){var props=Object.getOwnPropertyNames(source),i=props.length;while(--i>-1){var prop=props[i];obj[prop]=source[prop]}}return obj}function bind(fn,ctx){if(!ctx)return fn;return function(){return fn.apply(ctx,arguments)}}function isUndefined(o){return typeof o==="undefined"}function isFunction(o){return typeof o==="function"}function isObject(o){return typeof o==="object"&&!!o}function isNumber(o){return typeof o==="number"}function isNaN(o){return isNumber(o)&&o!==+o}},{}]},{},{1:""})); \ No newline at end of file diff --git a/build/grapher.js b/build/grapher.js index b2c594f..1372ba5 100644 --- a/build/grapher.js +++ b/build/grapher.js @@ -1,4 +1,12 @@ -(function outer(modules, cache, entries){ +(function umd(require){ + if ('object' == typeof exports) { + module.exports = require('1'); + } else if ('function' == typeof define && define.amd) { + define(function(){ return require('1'); }); + } else { + this['Grapher'] = require('1'); + } +})((function outer(modules, cache, entries){ /** * Global @@ -86,31 +94,8 @@ // Ayasdi Inc. Copyright 2014 // Grapher.js may be freely distributed under the Apache 2.0 license -var Grapher = require('./modules/grapher.js'); - -if (module && module.exports) module.exports = Grapher; -if (typeof Ayasdi === 'undefined') Ayasdi = {}; -Ayasdi.Grapher = Grapher; - -}, {"./modules/grapher.js":2}], -2: [function(require, module, exports) { -// Ayasdi Inc. Copyright 2014 -// Grapher.js may be freely distributed under the Apache 2.0 license - ;(function () { -/** - * Dependencies - * ============== - * Grapher uses PIXI.js as a dependency and uses Color and Utilities found in - * modules. - */ - var PIXI = require('./vendor/pixi.js'), - Color = require('./color.js'), - u = require('./utilities.js'); - - // We suppress PIXI's initial console log. - PIXI.dontSayHello = true; - + /** * Grapher * ======= @@ -121,7 +106,18 @@ Ayasdi.Grapher = Grapher; return this; } - if (module && module.exports) module.exports = Grapher; +/** + * Helpers and Renderers + * ===================== + * Load helpers and renderers. + */ + var WebGLRenderer = Grapher.WebGLRenderer = require('./renderers/gl/renderer.js'), + CanvasRenderer = Grapher.CanvasRenderer = require('./renderers/canvas/renderer.js'), + Color = Grapher.Color = require('./helpers/color.js'), + Link = Grapher.Link = require('./helpers/link.js'), + Node = Grapher.Node = require('./helpers/node.js'), + u = Grapher.utils = require('./helpers/utilities.js'); + Grapher.prototype = {}; /** @@ -133,47 +129,48 @@ Ayasdi.Grapher = Grapher; * var grapher = new Grapher(width, height, options); * */ - Grapher.prototype.initialize = function (width, height, o) { + Grapher.prototype.initialize = function (o) { + if (!o) o = {}; + // Extend default properties with options this.props = u.extend({ - lineWidth: 2, - foregroundColor: 0x222222, - backgroundColor: 0xffffff, - antialias: true, - resolution: typeof devicePixelRatio !== 'undefined' ? Math.max(devicePixelRatio, 1) : 1 + color: 0x222222, + scale: 1, + translate: [0, 0], + resolution: window.devicePixelRatio || 1 }, o); - this.width = width; - this.height = height; + if (!o.canvas) this.props.canvas = document.createElement('canvas'); + if (!o.width) this.props.width = this.props.canvas.clientWidth; + if (!o.height) this.props.height = this.props.canvas.clientHeight; + this.canvas = this.props.canvas; + + var webGL = this._getWebGL(); + if (webGL) { + this.props.webGL = webGL; + this.props.canvas.addEventListener('webglcontextlost', function (e) { this._onContextLost(e); }.bind(this)); + this.props.canvas.addEventListener('webglcontextrestored', function (e) { this._onContextRestored(e); }.bind(this)); + } // Renderer and view + this.renderer = webGL ? new WebGLRenderer(this.props) : new CanvasRenderer(this.props); this.rendered = false; - this.renderer = PIXI.autoDetectRenderer(width, height, this.props); - this.view = this.renderer.view; - - // Stage and containers - this.stage = new PIXI.Stage(this.backgroundColor()); - this.network = new PIXI.DisplayObjectContainer(); - this.stage.addChild(this.network); - // SpriteBatch containers - this.batches = {}; - this.batches.nodes = {}; - this.batches.links = {}; + // Initialize sizes + this.resize(this.props.width, this.props.height); // Sprite array this.links = []; this.nodes = []; - // indices that will update + this.renderer.setLinks(this.links); + this.renderer.setNodes(this.nodes); + + // Indices that will update this.willUpdate = {}; this.updateAll = {}; this._clearUpdateQueue(); - // Set initial transform - this.center(); - this.hasModifiedTransform = false; - // Bind some updaters this._updateLink = u.bind(this._updateLink, this); this._updateNode = u.bind(this._updateNode, this); @@ -181,18 +178,8 @@ Ayasdi.Grapher = Grapher; this._updateNodeByIndex = u.bind(this._updateNodeByIndex, this); this.animate = u.bind(this.animate, this); - // Listeners - this.listeners = {}; - - // Interactions - this.stage.interactive = true; - this.stage.mousedown = this._onEvent('mousedown'); - this.stage.mousemove = this._onEvent('mousemove'); - this.stage.mouseup = this._onEvent('mouseup'); - - // Add lost context listeners - this.view.addEventListener('webglcontextlost', u.bind(this._onContextLost, this)); - this.view.addEventListener('webglcontextrestored', u.bind(this._onContextRestored, this)); + // Event Handlers + this.handlers = {}; // Do any additional setup u.eachKey(o, this.set, this); @@ -224,7 +211,9 @@ Ayasdi.Grapher = Grapher; * * mouseup */ Grapher.prototype.on = function (event, fn) { - if (u.isFunction(fn)) this.listeners[event] = fn; + this.handlers[event] = this.handlers[event] || []; + this.handlers[event].push(fn); + this.canvas.addEventListener(event, fn, false); return this; }; @@ -234,23 +223,10 @@ Ayasdi.Grapher = Grapher; * * Remove a listener from an event. */ - Grapher.prototype.off = function (event) { - if (event in this.listeners) this.listeners[event] = u.noop; - return this; - }; - - /** - * grapher.palette - * ------------------ - * - * Set a grapher to use a pre-defined palette. Palettes can be pre-defined - * with the static function Grapher.setPalette. - */ - Grapher.prototype.palette = function (name) { - if (u.isUndefined(name)) return this.props.palette; - - this.props.palette = Grapher.getPalette(name); - this.update(); + Grapher.prototype.off = function (event, fn) { + var i = u.indexOf(this.handlers[event], fn); + if (i > -1) this.handlers[event].splice(i, 1); + this.canvas.removeEventListener(event, fn, false); return this; }; @@ -273,7 +249,6 @@ Ayasdi.Grapher = Grapher; this.enter(); this.update(); - if (!this.hasModifiedTransform) this.center(); return this; }; @@ -285,15 +260,17 @@ Ayasdi.Grapher = Grapher; * data. */ Grapher.prototype.enter = function () { - var data = this.data(), - entering = []; + var data = this.data(); + if (this.links.length < data.links.length) { + var links = data.links.slice(this.links.length, data.links.length); + u.eachPop(links, u.bind(function () { this.links.push(new Link()); }, this)); + } - if (this.links.length < data.links.length) - entering = entering.concat(data.links.slice(this.links.length, data.links.length - this.links.length)); - if (this.nodes.length < data.nodes.length) - entering = entering.concat(data.nodes.slice(this.nodes.length, data.nodes.length - this.nodes.length)); + if (this.nodes.length < data.nodes.length) { + var nodes = data.nodes.slice(this.nodes.length, data.nodes.length); + u.eachPop(nodes, u.bind(function () { this.nodes.push(new Node()); }, this)); + } - u.each(entering, this._enter, this); return this; }; @@ -308,12 +285,13 @@ Ayasdi.Grapher = Grapher; var data = this.data(), exiting = []; - if (data.links.length < this.links.length) - exiting = exiting.concat(this.links.splice(data.links.length, this.links.length - data.links.length)); - if (data.nodes.length < this.nodes.length) - exiting = exiting.concat(this.nodes.splice(data.nodes.length, this.nodes.length - data.nodes.length)); + if (data.links.length < this.links.length) { + this.links.splice(data.links.length, this.links.length - data.links.length); + } + if (data.nodes.length < this.nodes.length) { + this.nodes.splice(data.nodes.length, this.nodes.length - data.nodes.length); + } - u.each(exiting, this._exit); return this; }; @@ -378,7 +356,7 @@ Ayasdi.Grapher = Grapher; Grapher.prototype.render = function () { this.rendered = true; this._update(); - this.renderer.render(this.stage); + this.renderer.render(); return this; }; @@ -416,7 +394,6 @@ Ayasdi.Grapher = Grapher; return this; }; - /** * grapher.resize * ------------------ @@ -424,50 +401,35 @@ Ayasdi.Grapher = Grapher; * Resize the grapher view. */ Grapher.prototype.resize = function (width, height) { - this.width = width; - this.height = height; + this.props.width = width; + this.props.height = height; this.renderer.resize(width, height); return this; }; /** - * grapher.center + * grapher.width * ------------------ * - * Center the network in the view. This function modifies the scale and translate. + * Specify or retrieve the width. */ - Grapher.prototype.center = function () { - var x = 0, - y = 0, - scale = 1, - nodes = this.data() ? this.data().nodes : null, - numNodes = nodes ? nodes.length : 0; - - if (numNodes) { // get initial transform - var minX = Infinity, maxX = -Infinity, - minY = Infinity, maxY = -Infinity, - width = this.renderer.width / this.renderer.resolution, - height = this.renderer.height / this.renderer.resolution, - pad = 1.1, - i; - - for (i = 0; i < numNodes; i++) { - if (nodes[i].x < minX) minX = nodes[i].x; - if (nodes[i].x > maxX) maxX = nodes[i].x; - if (nodes[i].y < minY) minY = nodes[i].y; - if (nodes[i].y > maxY) maxY = nodes[i].y; - } - - var dX = maxX - minX, - dY = maxY - minY; - - scale = Math.min(width / dX, height / dY, 2) / pad; - x = (width - dX * scale) / 2 - minX * scale; - y = (height - dY * scale) / 2 - minY * scale; - } + Grapher.prototype.width = function (width) { + if (u.isUndefined(width)) return this.props.width; + this.resize(width, this.props.height); + return this; + }; - return this.scale(scale).translate([x, y]); + /** + * grapher.height + * ------------------ + * + * Specify or retrieve the height. + */ + Grapher.prototype.height = function (height) { + if (u.isUndefined(height)) return this.props.height; + this.resize(this.props.width, height); + return this; }; /** @@ -497,7 +459,6 @@ Ayasdi.Grapher = Grapher; if (u.isUndefined(scale)) return this.props.scale; if (u.isNumber(scale)) this.props.scale = scale; this.updateTransform = true; - this.hasModifiedTransform = true; return this; }; @@ -512,74 +473,50 @@ Ayasdi.Grapher = Grapher; if (u.isUndefined(translate)) return this.props.translate; if (u.isArray(translate)) this.props.translate = translate; this.updateTransform = true; - this.hasModifiedTransform = true; - return this; - }; - - /** - * grapher.backgroundColor - * ------------------ - * - * Set the backgroundColor. This is the color of the background. - * If no arguments are passed in, returns the current backgroundColor. - */ - Grapher.prototype.backgroundColor = function (color) { - if (u.isUndefined(color)) return this.props.backgroundColor; - this.props.backgroundColor = Color.parse(color); - this.stage.setBackgroundColor(this.props.backgroundColor); return this; }; /** - * grapher.foregroundColor + * grapher.color * ------------------ * - * Set the foregroundColor. This is the default color of nodes and links. - * If no arguments are passed in, returns the current foregroundColor. + * Set the default color of nodes and links. + * If no arguments are passed in, returns the current default color. */ - Grapher.prototype.foregroundColor = function (color) { - if (u.isUndefined(color)) return this.props.foregroundColor; - this.props.foregroundColor = Color.parse(color); + Grapher.prototype.color = function (color) { + if (u.isUndefined(color)) return this.props.color; + this.props.color = Color.parse(color); return this; }; /** - * grapher.lineWidth + * grapher.getDataPosition * ------------------ * - * Set the lineWidth. This is the line width of the links. - * If no arguments are passed in, returns the current lineWidth. + * Returns data space coordinates given display coordinates. + * If a single argument passed in, function considers first argument an object with x and y props. */ - Grapher.prototype.lineWidth = function (size) { - if (u.isUndefined(size)) return this.props.lineWidth; - this.props.lineWidth = size; - return this; + Grapher.prototype.getDataPosition = function (x, y) { + var xCoord = u.isUndefined(y) ? x.x : x; + var yCoord = u.isUndefined(y) ? x.y : y; + x = this.renderer.untransformX(xCoord); + y = this.renderer.untransformY(yCoord); + return {x: x, y: y}; }; /** - * grapher.getNodeIdAt - * ------------------- - * - * Search for a node index at a certain point. - * - * var point = {x: 10, y: 10}; - * var foundNode = grapher.getNodeIdAt(point); - * - * Returns -1 if no node is found. - */ - Grapher.prototype.getNodeIdAt = function (point) { - var node = -1, - x = point.x, y = point.y; - - this.nodes.every(function (n, i) { // we'll want to look for ways to optimize this - var inX = x <= n.position.x + n.width && x >= n.position.x, - inY = y <= n.position.y + n.height && y >= n.position.y, - found = inX && inY; - if (found) node = i; - return !found; - }); - - return node; + * grapher.getDisplayPosition + * ------------------ + * + * Returns display space coordinates given data coordinates. + * If a single argument passed in, function considers first argument an object with x and y props. + */ + Grapher.prototype.getDisplayPosition = function (x, y) { + var xCoord = u.isUndefined(y) ? x.x : x; + var yCoord = u.isUndefined(y) ? x.y : y; + x = this.renderer.transformX(xCoord); + y = this.renderer.transformY(yCoord); + return {x: x, y: y}; }; /** @@ -588,29 +525,6 @@ Ayasdi.Grapher = Grapher; * */ - /** - * grapher._exit - * ------------------- - * - * Remove a sprite from it's parent. - * - */ - Grapher.prototype._exit = function (sprite) { return sprite.parent.removeChild(sprite); }; - - /** - * grapher._enter - * ------------------- - * - * Create a new sprite from the node or link data provided. - * - */ - Grapher.prototype._enter = function (data) { - var type = u.isUndefined(data.from) ? NODES : LINKS, - spriteSet = type === NODES ? this.nodes : this.links; - sprite = new PIXI.Sprite(Grapher.getTexture(type, this.foregroundColor())); - spriteSet.push(sprite); - }; - /** * grapher._addToUpdateQueue * ------------------- @@ -666,8 +580,8 @@ Ayasdi.Grapher = Grapher; else if (updatingNodes && updatingNodes.length) u.eachPop(updatingNodes, this._updateNodeByIndex); if (this.updateTransform) { - this.network.scale.set(this.props.scale); - this.network.position.set.apply(this.network, this.props.translate); + this.renderer.setScale(this.props.scale); + this.renderer.setTranslate(this.props.translate); } this._clearUpdateQueue(); @@ -675,31 +589,19 @@ Ayasdi.Grapher = Grapher; Grapher.prototype._updateLink = function (link, i) { var data = this.data(), - lw = this.lineWidth(), l = data.links[i], from = data.nodes[l.from], - to = data.nodes[l.to], - leftMost = from.x <= to.x ? from : to; - - link.width = Math.sqrt(Math.pow(to.x - from.x, 2) + Math.pow(to.y - from.y, 2)); - link.height = lw; - link.position.set(leftMost.x, leftMost.y - lw / 2); - link.pivot.set(0, lw / 2); - link.rotation = Math.atan((to.y - from.y) / (to.x - from.x)); + to = data.nodes[l.to]; var color = !u.isUndefined(l.color) ? this._findColor(l.color) : Color.interpolate(this._findColor(from.color), this._findColor(to.color)); - this._setColor(LINKS, link, color); + link.update(from.x, from.y, to.x, to.y, color); }; Grapher.prototype._updateNode = function (node, i) { var n = this.data().nodes[i]; - node.width = n.r * 2; - node.height = n.r * 2; - node.position.set(n.x - n.r, n.y - n.r); - - this._setColor(NODES, node, this._findColor(n.color)); + node.update(n.x, n.y, n.r, this._findColor(n.color)); }; Grapher.prototype._updateNodeByIndex = function (i) { this._updateNode(this.nodes[i], i); }; @@ -746,68 +648,28 @@ Ayasdi.Grapher = Grapher; * integer. */ Grapher.prototype._findColor = function (c) { - var color = NaN, - palette = this.palette(); - - if (palette && palette[c]) color = palette[c]; - else color = Color.parse(c); + var color = Color.parse(c); // if color is still not set, use the default - if (u.isNaN(color)) color = this.foregroundColor(); + if (u.isNaN(color)) color = this.color(); return color; }; /** - * grapher._setColor - * ------------------- - * - * Set color on a sprite. This function moves the sprite into the appropriate - * sprite batch. - * - */ - Grapher.prototype._setColor = function (type, sprite, color) { - var texture = Grapher.getTexture(type, color); - - sprite.setTexture(texture); - if (sprite.parent) this._exit(sprite); - this._getBatch(type, color).addChild(sprite); - }; - - /** - * grapher._getBatch - * ------------------- - * - * Get the sprite batch for the sprite type and color. - * - */ - Grapher.prototype._getBatch = function (type, color) { - var batchSet = type === NODES ? this.batches.nodes : this.batches.links; - if (!batchSet[color]) { - var batch = new PIXI.SpriteBatch(); - if (type === LINKS) this.network.addChildAt(batch, 0); - else this.network.addChild(batch); - batchSet[color] = batch; - } - return batchSet[color]; - }; - - /** - * grapher._onEvent + * grapher._getWebGL * ------------------- * - * Wraps listeners. + *get webGL context if available * */ - Grapher.prototype._onEvent = function (event) { - return function (e) { - var callback = this.listeners[event] ? this.listeners[event] : u.noop; - e.offset = e.getLocalPosition(this.stage); - e.offsetData = e.getLocalPosition(this.network); - callback(e); - }.bind(this); + Grapher.prototype._getWebGL = function () { + var gl = null; + try { gl = this.canvas.getContext("webgl") || this.canvas.getContext("experimental-webgl"); } + catch (x) { gl = null; } + return gl; }; - /** + /** * grapher._onContextLost * ---------------------- * @@ -827,20239 +689,361 @@ Ayasdi.Grapher = Grapher; * */ Grapher.prototype._onContextRestored = function (e) { - if (this.renderer.contextLost) this.renderer.initContext(); - var gl = this.renderer.gl; - - var restoreBatch = function (batch) { - if (batch.fastSpriteBatch) batch.fastSpriteBatch.setContext(gl); - }; - - if (gl) { - // Restore each spriteBatch manually. - u.eachKey(this.batches.nodes, restoreBatch); - u.eachKey(this.batches.links, restoreBatch); - - // Re-enable vertex attrib array indices. - // PIXI remembers to re-enable 4 and 5, but not 0-3. - gl.enableVertexAttribArray(0); - gl.enableVertexAttribArray(1); - gl.enableVertexAttribArray(2); - gl.enableVertexAttribArray(3); - - // Reset the blend mode (to PIXI.blendModesWebGL[PIXI.blendModes.NORMAL]). - gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - } - - this.resize(this.width, this.height); - + var webGL = this._getWebGL(); + this.renderer.initGL(webGL); if (this.currentFrame) this.play(); // Play the graph if it was running. else if (this.rendered) this.render(); }; + /** * Grapher Static Properties * ========================= */ var NODES = Grapher.NODES = 'nodes'; var LINKS = Grapher.LINKS = 'links'; - Grapher.palettes = {}; // Store palettes and textures staticly. - Grapher.textures = {}; - Grapher.textures.nodes = {}; - Grapher.textures.links = {}; -/** - * Grapher Static Methods - * ====================== - */ + if (module && module.exports) module.exports = Grapher; +})(); - /** - * Grapher.getPalette - * ------------------- - * - * Get a palette that has been defined. - * - */ - Grapher.getPalette = function (name) { return this.palettes[name]; }; +}, {"./renderers/gl/renderer.js":2,"./renderers/canvas/renderer.js":3,"./helpers/color.js":4,"./helpers/link.js":5,"./helpers/node.js":6,"./helpers/utilities.js":7}], +2: [function(require, module, exports) { +;(function () { + var LinkVertexShaderSource = require('./shaders/link.vert'), + LinkFragmentShaderSource = require('./shaders/link.frag'), + NodeVertexShaderSource = require('./shaders/node.vert'), + NodeFragmentShaderSource = require('./shaders/node.frag'), + Renderer = require('../renderer.js'); + + var WebGLRenderer = Renderer.extend({ + init: function (o) { + this._super(o); + this.initGL(o.webGL); + + this.NODE_ATTRIBUTES = 6; + this.LINKS_ATTRIBUTES = 3; + }, - /** - * Grapher.setPalette - * ------------------- - * - * Define a palette with a name and an array of color swatches. - * - */ - Grapher.setPalette = function (name, swatches) { - var palette = this.palettes[name] = {}; - swatches = u.map(swatches, Color.parse); + initGL: function (gl) { + this.gl = gl; + this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); - u.each(swatches, function (swatch, i) { - this.getTexture(LINKS, swatch); - this.getTexture(NODES, swatch); - palette[i] = swatch; + this.linksProgram = this.initShaders(LinkVertexShaderSource, LinkFragmentShaderSource); + this.nodesProgram = this.initShaders(NodeVertexShaderSource, NodeFragmentShaderSource); - var j; - for (j = 0; j < i; j++) { // Interpolate 'in-between' link colors 50% between node colors. - var color = Color.interpolate(swatches[j], swatch, 0.5); - this.getTexture(LINKS, color); - palette[j + '-' + i] = color; - } - }, this); - return this; - }; + this.gl.linkProgram(this.linksProgram); + this.gl.linkProgram(this.nodesProgram); - /** - * Grapher.getTexture - * ------------------- - * - * Get a texture by 'nodes' or 'links' and a color. - * - */ - Grapher.getTexture = function (type, color) { - var textureSet = type === NODES ? this.textures.nodes : this.textures.links; - if (!textureSet[color]) { - // Generate the textures from Canvas - var isNode = type === NODES, - size = isNode ? 100 : 1, - renderer = new PIXI.CanvasRenderer(size, size, {transparent: isNode, resolution: 1}), - stage = new PIXI.Stage(color), - graphics = null, - img = ''; + this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA); + this.gl.enable(this.gl.BLEND); + }, + + initShaders: function (vertexShaderSource, fragmentShaderSource) { + var vertexShader = this.getShaders(this.gl.VERTEX_SHADER, vertexShaderSource); + var fragmentShader = this.getShaders(this.gl.FRAGMENT_SHADER, fragmentShaderSource); + var shaderProgram = this.gl.createProgram(); + this.gl.attachShader(shaderProgram, vertexShader); + this.gl.attachShader(shaderProgram, fragmentShader); + return shaderProgram; + }, - if (isNode) { - graphics = new PIXI.Graphics(); - graphics.beginFill(color); - graphics.drawCircle(size / 2, size / 2, size / 2); - graphics.endFill(); + getShaders: function (type, source) { + var shader = this.gl.createShader(type); + this.gl.shaderSource(shader, source); + this.gl.compileShader(shader); + return shader; + }, - stage.addChild(graphics); + updateNodesBuffer: function () { + var j = 0; + this.nodes = []; + for (var i = 0; i < this.nodeObjects.length; i++) { + var node = this.nodeObjects[i]; + var cx = this.transformX(node.x) * this.resolution; + var cy = this.transformY(node.y) * this.resolution; + var r = node.r * Math.abs(this.scale * this.resolution); + var color = node.color; + + + this.nodes[j++] = (cx - r); + this.nodes[j++] = (cy - r); + this.nodes[j++] = color; + this.nodes[j++] = cx; + this.nodes[j++] = cy; + this.nodes[j++] = r; + + this.nodes[j++] = (cx + (1 + Math.sqrt(2))*r); + this.nodes[j++] = cy - r; + this.nodes[j++] = color; + this.nodes[j++] = cx; + this.nodes[j++] = cy; + this.nodes[j++] = r; + + this.nodes[j++] = (cx - r); + this.nodes[j++] = (cy + (1 + Math.sqrt(2))*r); + this.nodes[j++] = color; + this.nodes[j++] = cx; + this.nodes[j++] = cy; + this.nodes[j++] = r; } + }, - renderer.render(stage); + updateLinksBuffer: function () { + var j = 0; + this.links = []; + for (var i = 0; i < this.linkObjects.length; i++) { + var link = this.linkObjects[i]; + var x1 = this.transformX(link.x1) * this.resolution; + var y1 = this.transformY(link.y1) * this.resolution; + var x2 = this.transformX(link.x2) * this.resolution; + var y2 = this.transformY(link.y2) * this.resolution; + var color = link.color; + + this.links[j++] = x1; + this.links[j++] = y1; + this.links[j++] = color; + + this.links[j++] = x2; + this.links[j++] = y2; + this.links[j++] = color; + } + }, - img = renderer.view.toDataURL(); - textureSet[color] = PIXI.Texture.fromImage(img); - } - return textureSet[color]; - }; -})(); + resize: function (width, height) { + this._super(width, height); + this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); + }, -}, {"./vendor/pixi.js":3,"./color.js":4,"./utilities.js":5}], -3: [function(require, module, exports) { -/** - * @license - * pixi.js - v2.2.5 - * Copyright (c) 2012-2014, Mat Groves - * http://goodboydigital.com/ - * - * Compiled: 2015-01-27 - * - * pixi.js is licensed under the MIT License. - * http://www.opensource.org/licenses/mit-license.php - */ -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -(function(){ - - var root = this; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ + render: function () { + this.updateNodesBuffer(); + this.updateLinksBuffer(); + this.renderLinks(); // links have to be rendered first because of blending; + this.renderNodes(); + }, -/** - * The [pixi.js](http://www.pixijs.com/) module/namespace. - * - * @module PIXI - */ + renderLinks: function () { + var program = this.linksProgram; + this.gl.useProgram(program); -/** - * Namespace-class for [pixi.js](http://www.pixijs.com/). - * - * Contains assorted static properties and enumerations. - * - * @class PIXI - * @static - */ -var PIXI = PIXI || {}; + var linksBuffer = new Float32Array(this.links); + var buffer = this.gl.createBuffer(); -/** - * @property {Number} WEBGL_RENDERER - * @protected - * @static - */ -PIXI.WEBGL_RENDERER = 0; -/** - * @property {Number} CANVAS_RENDERER - * @protected - * @static - */ -PIXI.CANVAS_RENDERER = 1; + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer); + this.gl.bufferData(this.gl.ARRAY_BUFFER, linksBuffer, this.gl.STATIC_DRAW); -/** - * Version of pixi that is loaded. - * @property {String} VERSION - * @static - */ -PIXI.VERSION = "v2.2.5"; + var resolutionLocation = this.gl.getUniformLocation(program, 'u_resolution'); + this.gl.uniform2f(resolutionLocation, this.canvas.width, this.canvas.height); -/** - * Various blend modes supported by pixi. IMPORTANT - The WebGL renderer only supports the NORMAL, ADD, MULTIPLY and SCREEN blend modes. - * @property {Object} blendModes - * @property {Number} blendModes.NORMAL - * @property {Number} blendModes.ADD - * @property {Number} blendModes.MULTIPLY - * @property {Number} blendModes.SCREEN - * @property {Number} blendModes.OVERLAY - * @property {Number} blendModes.DARKEN - * @property {Number} blendModes.LIGHTEN - * @property {Number} blendModes.COLOR_DODGE - * @property {Number} blendModes.COLOR_BURN - * @property {Number} blendModes.HARD_LIGHT - * @property {Number} blendModes.SOFT_LIGHT - * @property {Number} blendModes.DIFFERENCE - * @property {Number} blendModes.EXCLUSION - * @property {Number} blendModes.HUE - * @property {Number} blendModes.SATURATION - * @property {Number} blendModes.COLOR - * @property {Number} blendModes.LUMINOSITY - * @static - */ -PIXI.blendModes = { - NORMAL:0, - ADD:1, - MULTIPLY:2, - SCREEN:3, - OVERLAY:4, - DARKEN:5, - LIGHTEN:6, - COLOR_DODGE:7, - COLOR_BURN:8, - HARD_LIGHT:9, - SOFT_LIGHT:10, - DIFFERENCE:11, - EXCLUSION:12, - HUE:13, - SATURATION:14, - COLOR:15, - LUMINOSITY:16 -}; + var positionLocation = this.gl.getAttribLocation(program, 'a_position'); + var colorLocation = this.gl.getAttribLocation(program, 'a_color'); + + this.gl.enableVertexAttribArray(positionLocation); + this.gl.enableVertexAttribArray(colorLocation); -/** - * The scale modes that are supported by pixi. - * - * The DEFAULT scale mode affects the default scaling mode of future operations. - * It can be re-assigned to either LINEAR or NEAREST, depending upon suitability. - * - * @property {Object} scaleModes - * @property {Number} scaleModes.DEFAULT=LINEAR - * @property {Number} scaleModes.LINEAR Smooth scaling - * @property {Number} scaleModes.NEAREST Pixelating scaling - * @static - */ -PIXI.scaleModes = { - DEFAULT:0, - LINEAR:0, - NEAREST:1 -}; + this.gl.vertexAttribPointer(positionLocation, 2, this.gl.FLOAT, false, this.LINKS_ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0); + this.gl.vertexAttribPointer(colorLocation, 1, this.gl.FLOAT, false, this.LINKS_ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8); -// used to create uids for various pixi objects.. -PIXI._UID = 0; + var lineWidthRange = this.gl.getParameter(this.gl.ALIASED_LINE_WIDTH_RANGE), // ex [1,10] + lineWidth = this.lineWidth * Math.abs(this.scale * this.resolution), + lineWidthInRange = Math.min(Math.max(lineWidth, lineWidthRange[0]), lineWidthRange[1]); -if(typeof(Float32Array) != 'undefined') -{ - PIXI.Float32Array = Float32Array; - PIXI.Uint16Array = Uint16Array; + this.gl.lineWidth(lineWidthInRange); + this.gl.drawArrays(this.gl.LINES, 0, this.links.length/this.LINKS_ATTRIBUTES); + }, - // Uint32Array and ArrayBuffer only used by WebGL renderer - // We can suppose that if WebGL is supported then typed arrays are supported too - // as they predate WebGL support for all browsers: - // see typed arrays support: http://caniuse.com/#search=TypedArrays - // see WebGL support: http://caniuse.com/#search=WebGL - PIXI.Uint32Array = Uint32Array; - PIXI.ArrayBuffer = ArrayBuffer; -} -else -{ - PIXI.Float32Array = Array; - PIXI.Uint16Array = Array; -} + renderNodes: function () { + var program = this.nodesProgram; + this.gl.useProgram(program); -// interaction frequency -PIXI.INTERACTION_FREQUENCY = 30; -PIXI.AUTO_PREVENT_DEFAULT = true; + var nodesBuffer = new Float32Array(this.nodes); + var buffer = this.gl.createBuffer(); -/** - * @property {Number} PI_2 - * @static - */ -PIXI.PI_2 = Math.PI * 2; + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer); + this.gl.bufferData(this.gl.ARRAY_BUFFER, nodesBuffer, this.gl.STATIC_DRAW); -/** - * @property {Number} RAD_TO_DEG - * @static - */ -PIXI.RAD_TO_DEG = 180 / Math.PI; + var resolutionLocation = this.gl.getUniformLocation(program, 'u_resolution'); + this.gl.uniform2f(resolutionLocation, this.canvas.width, this.canvas.height); -/** - * @property {Number} DEG_TO_RAD - * @static - */ -PIXI.DEG_TO_RAD = Math.PI / 180; + var positionLocation = this.gl.getAttribLocation(program, 'a_position'); + var colorLocation = this.gl.getAttribLocation(program, 'a_color'); + var centerLocation = this.gl.getAttribLocation(program, 'a_center'); + var radiusLocation = this.gl.getAttribLocation(program, 'a_radius'); + + this.gl.enableVertexAttribArray(positionLocation); + this.gl.enableVertexAttribArray(colorLocation); + this.gl.enableVertexAttribArray(centerLocation); + this.gl.enableVertexAttribArray(radiusLocation); -/** - * @property {String} RETINA_PREFIX - * @protected - * @static - */ -PIXI.RETINA_PREFIX = "@2x"; -//PIXI.SCALE_PREFIX "@x%%"; + this.gl.vertexAttribPointer(positionLocation, 2, this.gl.FLOAT, false, this.NODE_ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 0); + this.gl.vertexAttribPointer(colorLocation, 1, this.gl.FLOAT, false, this.NODE_ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 8); + this.gl.vertexAttribPointer(centerLocation, 2, this.gl.FLOAT, false, this.NODE_ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 12); + this.gl.vertexAttribPointer(radiusLocation, 1, this.gl.FLOAT, false, this.NODE_ATTRIBUTES * Float32Array.BYTES_PER_ELEMENT, 20); -/** - * If true the default pixi startup (console) banner message will be suppressed. - * - * @property {Boolean} dontSayHello - * @default false - * @static - */ -PIXI.dontSayHello = false; + this.gl.drawArrays(this.gl.TRIANGLES, 0, this.nodes.length/this.NODE_ATTRIBUTES); + } + }); -/** - * The default render options if none are supplied to - * {{#crossLink "WebGLRenderer"}}{{/crossLink}} or {{#crossLink "CanvasRenderer"}}{{/crossLink}}. - * - * @property {Object} defaultRenderOptions - * @property {Object} defaultRenderOptions.view=null - * @property {Boolean} defaultRenderOptions.transparent=false - * @property {Boolean} defaultRenderOptions.antialias=false - * @property {Boolean} defaultRenderOptions.preserveDrawingBuffer=false - * @property {Number} defaultRenderOptions.resolution=1 - * @property {Boolean} defaultRenderOptions.clearBeforeRender=true - * @property {Boolean} defaultRenderOptions.autoResize=false - * @static - */ -PIXI.defaultRenderOptions = { - view:null, - transparent:false, - antialias:false, - preserveDrawingBuffer:false, - resolution:1, - clearBeforeRender:true, - autoResize:false -} + if (module && module.exports) module.exports = WebGLRenderer; +})(); -PIXI.sayHello = function (type) -{ - if(PIXI.dontSayHello)return; +}, {"./shaders/link.vert":8,"./shaders/link.frag":9,"./shaders/node.vert":10,"./shaders/node.frag":11,"../renderer.js":12}], +8: [function(require, module, exports) { +module.exports = 'uniform vec2 u_resolution;\nattribute vec2 a_position;\nattribute float a_color;\nvarying vec4 color;\nvarying vec2 position;\nvarying vec2 resolution;\nvoid main() {\n vec2 clipspace = a_position / u_resolution * 2.0 - 1.0;\n gl_Position = vec4(clipspace * vec2(1, -1), 0, 1);\n float c = a_color;\n color.b = mod(c, 256.0); c = floor(c / 256.0);\n color.g = mod(c, 256.0); c = floor(c / 256.0);\n color.r = mod(c, 256.0); c = floor(c / 256.0); color /= 255.0;\n color.a = 1.0;\n}\n'; +}, {}], +9: [function(require, module, exports) { +module.exports = 'precision mediump float;\nvarying vec4 color;\nvoid main() {\n gl_FragColor = color;\n}\n'; +}, {}], +10: [function(require, module, exports) { +module.exports = 'uniform vec2 u_resolution;\nattribute vec2 a_position;\nattribute float a_color;\nattribute vec2 a_center;\nattribute float a_radius;\nvarying vec4 color;\nvarying vec2 center;\nvarying vec2 resolution;\nvarying float radius;\nvoid main() {\n vec2 clipspace = a_position / u_resolution * 2.0 - 1.0;\n gl_Position = vec4(clipspace * vec2(1, -1), 0, 1);\n float c = a_color;\n color.b = mod(c, 256.0); c = floor(c / 256.0);\n color.g = mod(c, 256.0); c = floor(c / 256.0);\n color.r = mod(c, 256.0); c = floor(c / 256.0); color /= 255.0;\n color.a = 1.0;\n radius = a_radius;\n center = a_center;\n resolution = u_resolution;\n}\n'; +}, {}], +11: [function(require, module, exports) { +module.exports = 'precision mediump float;\nvarying vec4 color;\nvarying vec2 center;\nvarying vec2 resolution;\nvarying float radius;\nvoid main() {\n vec4 color0 = vec4(0.0, 0.0, 0.0, 0.0);\n float x = gl_FragCoord.x;\n float y = resolution[1] - gl_FragCoord.y;\n float dx = center[0] - x;\n float dy = center[1] - y;\n float distance = sqrt(dx*dx + dy*dy);\n if ( distance < radius )\n gl_FragColor = color;\n else \n gl_FragColor = color0;\n}\n'; +}, {}], +12: [function(require, module, exports) { +;(function () { - if ( navigator.userAgent.toLowerCase().indexOf('chrome') > -1 ) - { - var args = [ - '%c %c %c Pixi.js ' + PIXI.VERSION + ' - ' + type + ' %c ' + ' %c ' + ' http://www.pixijs.com/ %c %c ♥%c♥%c♥ ', - 'background: #ff66a5', - 'background: #ff66a5', - 'color: #ff66a5; background: #030307;', - 'background: #ff66a5', - 'background: #ffc3dc', - 'background: #ff66a5', - 'color: #ff2424; background: #fff', - 'color: #ff2424; background: #fff', - 'color: #ff2424; background: #fff' - ]; + var Renderer = function () { + if ( !initializing && this.init ) + this.init.apply(this, arguments); + return this; + }; - console.log.apply(console, args); - } - else if (window['console']) - { - console.log('Pixi.js ' + PIXI.VERSION + ' - http://www.pixijs.com/'); + Renderer.prototype = { + init: function (o) { + this.canvas = o.canvas; + this.lineWidth = o.lineWidth || 2; + this.resolution = o.resolution || 1; + this.scale = o.scale; + this.translate = o.translate; + }, + setNodes: function (nodes) { this.nodeObjects = nodes; }, + setLinks: function (links) { this.linkObjects = links; }, + setScale: function (scale) { this.scale = scale; }, + setTranslate: function (translate) { this.translate = translate; }, + transformX: function (x) { return x * this.scale + this.translate[0]; }, + transformY: function (y) { return y * this.scale + this.translate[1]; }, + untransformX: function (x) { return (x - this.translate[0]) / this.scale; }, + untransformY: function (y) { return (y - this.translate[1]) / this.scale; }, + resize: function (width, height) { + var displayWidth = width * this.resolution; + var displayHeight = height * this.resolution; + + this.canvas.width = displayWidth; + this.canvas.height = displayHeight; } + }; - PIXI.dontSayHello = true; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * The Point object represents a location in a two-dimensional coordinate system, where x represents the horizontal axis and y represents the vertical axis. - * - * @class Point - * @constructor - * @param x {Number} position of the point on the x axis - * @param y {Number} position of the point on the y axis - */ -PIXI.Point = function(x, y) -{ - /** - * @property x - * @type Number - * @default 0 - */ - this.x = x || 0; - - /** - * @property y - * @type Number - * @default 0 - */ - this.y = y || 0; -}; + var initializing = false; -/** - * Creates a clone of this point - * - * @method clone - * @return {Point} a copy of the point - */ -PIXI.Point.prototype.clone = function() -{ - return new PIXI.Point(this.x, this.y); -}; + Renderer.extend = function (prop) { + var _super = this.prototype; -/** - * Sets the point to a new x and y position. - * If y is omitted, both x and y will be set to x. - * - * @method set - * @param [x=0] {Number} position of the point on the x axis - * @param [y=0] {Number} position of the point on the y axis - */ -PIXI.Point.prototype.set = function(x, y) -{ - this.x = x || 0; - this.y = y || ( (y !== 0) ? this.x : 0 ) ; -}; + initializing = true; + var prototype = new this(); + initializing = false; -// constructor -PIXI.Point.prototype.constructor = PIXI.Point; -/** - * @author Mat Groves http://matgroves.com/ - */ + prototype._super = this.prototype; + for (var name in prop) { + prototype[name] = typeof prop[name] == "function" && + typeof _super[name] == "function" && /\b_super\b/.test(prop[name]) ? + (function(name, fn){ + return function() { + var tmp = this._super; + + // Add a new ._super() method that is the same method + // but on the super-class + this._super = _super[name]; + + // The method only need to be bound temporarily, so we + // remove it when we're done executing + var ret = fn.apply(this, arguments); + this._super = tmp; + + return ret; + }; + })(name, prop[name]) : + prop[name]; + } -/** - * the Rectangle object is an area defined by its position, as indicated by its top-left corner point (x, y) and by its width and its height. - * - * @class Rectangle - * @constructor - * @param x {Number} The X coordinate of the upper-left corner of the rectangle - * @param y {Number} The Y coordinate of the upper-left corner of the rectangle - * @param width {Number} The overall width of this rectangle - * @param height {Number} The overall height of this rectangle - */ -PIXI.Rectangle = function(x, y, width, height) -{ - /** - * @property x - * @type Number - * @default 0 - */ - this.x = x || 0; + // The dummy class constructor + function Renderer () { + // All construction is actually done in the init method + if ( !initializing && this.init ) + this.init.apply(this, arguments); + } + + // Populate our constructed prototype object + Renderer.prototype = prototype; + + // Enforce the constructor to be what we expect + Renderer.prototype.constructor = Renderer; + + // And make this class extendable + Renderer.extend = arguments.callee; + + return Renderer; + }; - /** - * @property y - * @type Number - * @default 0 - */ - this.y = y || 0; + if (module && module.exports) module.exports = Renderer; +})(); - /** - * @property width - * @type Number - * @default 0 - */ - this.width = width || 0; +}, {}], +3: [function(require, module, exports) { +;(function () { - /** - * @property height - * @type Number - * @default 0 - */ - this.height = height || 0; -}; + var Renderer = require('../renderer.js'); + var Color = require('../../helpers/color.js'); + + var CanvasRenderer = Renderer.extend({ + init: function (o) { + this._super(o); -/** - * Creates a clone of this Rectangle - * - * @method clone - * @return {Rectangle} a copy of the rectangle - */ -PIXI.Rectangle.prototype.clone = function() -{ - return new PIXI.Rectangle(this.x, this.y, this.width, this.height); -}; + this.context = this.canvas.getContext('2d'); + }, -/** - * Checks whether the x and y coordinates given are contained within this Rectangle - * - * @method contains - * @param x {Number} The X coordinate of the point to test - * @param y {Number} The Y coordinate of the point to test - * @return {Boolean} Whether the x/y coordinates are within this Rectangle - */ -PIXI.Rectangle.prototype.contains = function(x, y) -{ - if(this.width <= 0 || this.height <= 0) - return false; + render: function () { + this.context.clearRect( 0 , 0 , this.canvas.width, this.canvas.height ); + this.renderLinks(); + this.renderNodes(); + }, - var x1 = this.x; - if(x >= x1 && x <= x1 + this.width) - { - var y1 = this.y; + renderNodes: function () { + for (var i = 0 ; i < this.nodeObjects.length; i ++) { + var node = this.nodeObjects[i]; + var cx = this.transformX(node.x) * this.resolution; + var cy = this.transformY(node.y) * this.resolution; + var r = node.r * Math.abs(this.scale * this.resolution); + + this.context.beginPath(); + this.context.arc(cx, cy, r, 0, 2 * Math.PI, false); + this.context.fillStyle = Color.toRgb(node.color); + this.context.fill(); + } + }, - if(y >= y1 && y <= y1 + this.height) - { - return true; - } + renderLinks: function () { + for (var i = 0 ; i < this.linkObjects.length; i ++) { + var link = this.linkObjects[i]; + var x1 = this.transformX(link.x1) * this.resolution; + var y1 = this.transformY(link.y1) * this.resolution; + var x2 = this.transformX(link.x2) * this.resolution; + var y2 = this.transformY(link.y2) * this.resolution; + + this.context.beginPath(); + this.context.moveTo(x1, y1); + this.context.lineTo(x2, y2); + this.context.lineWidth = this.lineWidth * Math.abs(this.scale * this.resolution); + + this.context.strokeStyle = Color.toRgb(link.color); + this.context.stroke(); + } } + }); + + if (module && module.exports) module.exports = CanvasRenderer; +})(); - return false; -}; - -// constructor -PIXI.Rectangle.prototype.constructor = PIXI.Rectangle; - -PIXI.EmptyRectangle = new PIXI.Rectangle(0,0,0,0); -/** - * @author Adrien Brault - */ - -/** - * @class Polygon - * @constructor - * @param points* {Array(Point)|Array(Number)|Point...|Number...} This can be an array of Points that form the polygon, - * a flat array of numbers that will be interpreted as [x,y, x,y, ...], or the arguments passed can be - * all the points of the polygon e.g. `new PIXI.Polygon(new PIXI.Point(), new PIXI.Point(), ...)`, or the - * arguments passed can be flat x,y values e.g. `new PIXI.Polygon(x,y, x,y, x,y, ...)` where `x` and `y` are - * Numbers. - */ -PIXI.Polygon = function(points) -{ - //if points isn't an array, use arguments as the array - if(!(points instanceof Array))points = Array.prototype.slice.call(arguments); - - //if this is a flat array of numbers, convert it to points - if(points[0] instanceof PIXI.Point) - { - var p = []; - for(var i = 0, il = points.length; i < il; i++) - { - p.push(points[i].x, points[i].y); - } - - points = p; - } - - this.closed = true; - this.points = points; -}; - -/** - * Creates a clone of this polygon - * - * @method clone - * @return {Polygon} a copy of the polygon - */ -PIXI.Polygon.prototype.clone = function() -{ - var points = this.points.slice(); - return new PIXI.Polygon(points); -}; - -/** - * Checks whether the x and y coordinates passed to this function are contained within this polygon - * - * @method contains - * @param x {Number} The X coordinate of the point to test - * @param y {Number} The Y coordinate of the point to test - * @return {Boolean} Whether the x/y coordinates are within this polygon - */ -PIXI.Polygon.prototype.contains = function(x, y) -{ - var inside = false; - - // use some raycasting to test hits - // https://github.com/substack/point-in-polygon/blob/master/index.js - var length = this.points.length / 2; - - for(var i = 0, j = length - 1; i < length; j = i++) - { - var xi = this.points[i * 2], yi = this.points[i * 2 + 1], - xj = this.points[j * 2], yj = this.points[j * 2 + 1], - intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); - - if(intersect) inside = !inside; - } - - return inside; -}; - -// constructor -PIXI.Polygon.prototype.constructor = PIXI.Polygon; - -/** - * @author Chad Engler - */ - -/** - * The Circle object can be used to specify a hit area for displayObjects - * - * @class Circle - * @constructor - * @param x {Number} The X coordinate of the center of this circle - * @param y {Number} The Y coordinate of the center of this circle - * @param radius {Number} The radius of the circle - */ -PIXI.Circle = function(x, y, radius) -{ - /** - * @property x - * @type Number - * @default 0 - */ - this.x = x || 0; - - /** - * @property y - * @type Number - * @default 0 - */ - this.y = y || 0; - - /** - * @property radius - * @type Number - * @default 0 - */ - this.radius = radius || 0; -}; - -/** - * Creates a clone of this Circle instance - * - * @method clone - * @return {Circle} a copy of the Circle - */ -PIXI.Circle.prototype.clone = function() -{ - return new PIXI.Circle(this.x, this.y, this.radius); -}; - -/** - * Checks whether the x and y coordinates given are contained within this circle - * - * @method contains - * @param x {Number} The X coordinate of the point to test - * @param y {Number} The Y coordinate of the point to test - * @return {Boolean} Whether the x/y coordinates are within this Circle - */ -PIXI.Circle.prototype.contains = function(x, y) -{ - if(this.radius <= 0) - return false; - - var dx = (this.x - x), - dy = (this.y - y), - r2 = this.radius * this.radius; - - dx *= dx; - dy *= dy; - - return (dx + dy <= r2); -}; - -/** -* Returns the framing rectangle of the circle as a PIXI.Rectangle object -* -* @method getBounds -* @return {Rectangle} the framing rectangle -*/ -PIXI.Circle.prototype.getBounds = function() -{ - return new PIXI.Rectangle(this.x - this.radius, this.y - this.radius, this.radius * 2, this.radius * 2); -}; - -// constructor -PIXI.Circle.prototype.constructor = PIXI.Circle; - -/** - * @author Chad Engler - */ - -/** - * The Ellipse object can be used to specify a hit area for displayObjects - * - * @class Ellipse - * @constructor - * @param x {Number} The X coordinate of the center of the ellipse - * @param y {Number} The Y coordinate of the center of the ellipse - * @param width {Number} The half width of this ellipse - * @param height {Number} The half height of this ellipse - */ -PIXI.Ellipse = function(x, y, width, height) -{ - /** - * @property x - * @type Number - * @default 0 - */ - this.x = x || 0; - - /** - * @property y - * @type Number - * @default 0 - */ - this.y = y || 0; - - /** - * @property width - * @type Number - * @default 0 - */ - this.width = width || 0; - - /** - * @property height - * @type Number - * @default 0 - */ - this.height = height || 0; -}; - -/** - * Creates a clone of this Ellipse instance - * - * @method clone - * @return {Ellipse} a copy of the ellipse - */ -PIXI.Ellipse.prototype.clone = function() -{ - return new PIXI.Ellipse(this.x, this.y, this.width, this.height); -}; - -/** - * Checks whether the x and y coordinates given are contained within this ellipse - * - * @method contains - * @param x {Number} The X coordinate of the point to test - * @param y {Number} The Y coordinate of the point to test - * @return {Boolean} Whether the x/y coords are within this ellipse - */ -PIXI.Ellipse.prototype.contains = function(x, y) -{ - if(this.width <= 0 || this.height <= 0) - return false; - - //normalize the coords to an ellipse with center 0,0 - var normx = ((x - this.x) / this.width), - normy = ((y - this.y) / this.height); - - normx *= normx; - normy *= normy; - - return (normx + normy <= 1); -}; - -/** -* Returns the framing rectangle of the ellipse as a PIXI.Rectangle object -* -* @method getBounds -* @return {Rectangle} the framing rectangle -*/ -PIXI.Ellipse.prototype.getBounds = function() -{ - return new PIXI.Rectangle(this.x - this.width, this.y - this.height, this.width, this.height); -}; - -// constructor -PIXI.Ellipse.prototype.constructor = PIXI.Ellipse; - -/** - * @author Mat Groves http://matgroves.com/ - */ - -/** - * The Rounded Rectangle object is an area defined by its position and has nice rounded corners, as indicated by its top-left corner point (x, y) and by its width and its height. - * - * @class RoundedRectangle - * @constructor - * @param x {Number} The X coordinate of the upper-left corner of the rounded rectangle - * @param y {Number} The Y coordinate of the upper-left corner of the rounded rectangle - * @param width {Number} The overall width of this rounded rectangle - * @param height {Number} The overall height of this rounded rectangle - * @param radius {Number} The overall radius of this corners of this rounded rectangle - */ -PIXI.RoundedRectangle = function(x, y, width, height, radius) -{ - /** - * @property x - * @type Number - * @default 0 - */ - this.x = x || 0; - - /** - * @property y - * @type Number - * @default 0 - */ - this.y = y || 0; - - /** - * @property width - * @type Number - * @default 0 - */ - this.width = width || 0; - - /** - * @property height - * @type Number - * @default 0 - */ - this.height = height || 0; - - /** - * @property radius - * @type Number - * @default 20 - */ - this.radius = radius || 20; -}; - -/** - * Creates a clone of this Rounded Rectangle - * - * @method clone - * @return {RoundedRectangle} a copy of the rounded rectangle - */ -PIXI.RoundedRectangle.prototype.clone = function() -{ - return new PIXI.RoundedRectangle(this.x, this.y, this.width, this.height, this.radius); -}; - -/** - * Checks whether the x and y coordinates given are contained within this Rounded Rectangle - * - * @method contains - * @param x {Number} The X coordinate of the point to test - * @param y {Number} The Y coordinate of the point to test - * @return {Boolean} Whether the x/y coordinates are within this Rounded Rectangle - */ -PIXI.RoundedRectangle.prototype.contains = function(x, y) -{ - if(this.width <= 0 || this.height <= 0) - return false; - - var x1 = this.x; - if(x >= x1 && x <= x1 + this.width) - { - var y1 = this.y; - - if(y >= y1 && y <= y1 + this.height) - { - return true; - } - } - - return false; -}; - -// constructor -PIXI.RoundedRectangle.prototype.constructor = PIXI.RoundedRectangle; - - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * The Matrix class is now an object, which makes it a lot faster, - * here is a representation of it : - * | a | b | tx| - * | c | d | ty| - * | 0 | 0 | 1 | - * - * @class Matrix - * @constructor - */ -PIXI.Matrix = function() -{ - /** - * @property a - * @type Number - * @default 1 - */ - this.a = 1; - - /** - * @property b - * @type Number - * @default 0 - */ - this.b = 0; - - /** - * @property c - * @type Number - * @default 0 - */ - this.c = 0; - - /** - * @property d - * @type Number - * @default 1 - */ - this.d = 1; - - /** - * @property tx - * @type Number - * @default 0 - */ - this.tx = 0; - - /** - * @property ty - * @type Number - * @default 0 - */ - this.ty = 0; -}; - -/** - * Creates a Matrix object based on the given array. The Element to Matrix mapping order is as follows: - * - * a = array[0] - * b = array[1] - * c = array[3] - * d = array[4] - * tx = array[2] - * ty = array[5] - * - * @method fromArray - * @param array {Array} The array that the matrix will be populated from. - */ -PIXI.Matrix.prototype.fromArray = function(array) -{ - this.a = array[0]; - this.b = array[1]; - this.c = array[3]; - this.d = array[4]; - this.tx = array[2]; - this.ty = array[5]; -}; - -/** - * Creates an array from the current Matrix object. - * - * @method toArray - * @param transpose {Boolean} Whether we need to transpose the matrix or not - * @return {Array} the newly created array which contains the matrix - */ -PIXI.Matrix.prototype.toArray = function(transpose) -{ - if(!this.array) this.array = new PIXI.Float32Array(9); - var array = this.array; - - if(transpose) - { - array[0] = this.a; - array[1] = this.b; - array[2] = 0; - array[3] = this.c; - array[4] = this.d; - array[5] = 0; - array[6] = this.tx; - array[7] = this.ty; - array[8] = 1; - } - else - { - array[0] = this.a; - array[1] = this.c; - array[2] = this.tx; - array[3] = this.b; - array[4] = this.d; - array[5] = this.ty; - array[6] = 0; - array[7] = 0; - array[8] = 1; - } - - return array; -}; - -/** - * Get a new position with the current transformation applied. - * Can be used to go from a child's coordinate space to the world coordinate space. (e.g. rendering) - * - * @method apply - * @param pos {Point} The origin - * @param [newPos] {Point} The point that the new position is assigned to (allowed to be same as input) - * @return {Point} The new point, transformed through this matrix - */ -PIXI.Matrix.prototype.apply = function(pos, newPos) -{ - newPos = newPos || new PIXI.Point(); - - newPos.x = this.a * pos.x + this.c * pos.y + this.tx; - newPos.y = this.b * pos.x + this.d * pos.y + this.ty; - - return newPos; -}; - -/** - * Get a new position with the inverse of the current transformation applied. - * Can be used to go from the world coordinate space to a child's coordinate space. (e.g. input) - * - * @method applyInverse - * @param pos {Point} The origin - * @param [newPos] {Point} The point that the new position is assigned to (allowed to be same as input) - * @return {Point} The new point, inverse-transformed through this matrix - */ -PIXI.Matrix.prototype.applyInverse = function(pos, newPos) -{ - newPos = newPos || new PIXI.Point(); - - var id = 1 / (this.a * this.d + this.c * -this.b); - - newPos.x = this.d * id * pos.x + -this.c * id * pos.y + (this.ty * this.c - this.tx * this.d) * id; - newPos.y = this.a * id * pos.y + -this.b * id * pos.x + (-this.ty * this.a + this.tx * this.b) * id; - - return newPos; -}; - -/** - * Translates the matrix on the x and y. - * - * @method translate - * @param {Number} x - * @param {Number} y - * @return {Matrix} This matrix. Good for chaining method calls. - **/ -PIXI.Matrix.prototype.translate = function(x, y) -{ - this.tx += x; - this.ty += y; - - return this; -}; - -/** - * Applies a scale transformation to the matrix. - * - * @method scale - * @param {Number} x The amount to scale horizontally - * @param {Number} y The amount to scale vertically - * @return {Matrix} This matrix. Good for chaining method calls. - **/ -PIXI.Matrix.prototype.scale = function(x, y) -{ - this.a *= x; - this.d *= y; - this.c *= x; - this.b *= y; - this.tx *= x; - this.ty *= y; - - return this; -}; - - -/** - * Applies a rotation transformation to the matrix. - * @method rotate - * @param {Number} angle The angle in radians. - * @return {Matrix} This matrix. Good for chaining method calls. - **/ -PIXI.Matrix.prototype.rotate = function(angle) -{ - var cos = Math.cos( angle ); - var sin = Math.sin( angle ); - - var a1 = this.a; - var c1 = this.c; - var tx1 = this.tx; - - this.a = a1 * cos-this.b * sin; - this.b = a1 * sin+this.b * cos; - this.c = c1 * cos-this.d * sin; - this.d = c1 * sin+this.d * cos; - this.tx = tx1 * cos - this.ty * sin; - this.ty = tx1 * sin + this.ty * cos; - - return this; -}; - -/** - * Appends the given Matrix to this Matrix. - * - * @method append - * @param {Matrix} matrix - * @return {Matrix} This matrix. Good for chaining method calls. - */ -PIXI.Matrix.prototype.append = function(matrix) -{ - var a1 = this.a; - var b1 = this.b; - var c1 = this.c; - var d1 = this.d; - - this.a = matrix.a * a1 + matrix.b * c1; - this.b = matrix.a * b1 + matrix.b * d1; - this.c = matrix.c * a1 + matrix.d * c1; - this.d = matrix.c * b1 + matrix.d * d1; - - this.tx = matrix.tx * a1 + matrix.ty * c1 + this.tx; - this.ty = matrix.tx * b1 + matrix.ty * d1 + this.ty; - - return this; -}; - -/** - * Resets this Matix to an identity (default) matrix. - * - * @method identity - * @return {Matrix} This matrix. Good for chaining method calls. - */ -PIXI.Matrix.prototype.identity = function() -{ - this.a = 1; - this.b = 0; - this.c = 0; - this.d = 1; - this.tx = 0; - this.ty = 0; - - return this; -}; - -PIXI.identityMatrix = new PIXI.Matrix(); - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * The base class for all objects that are rendered on the screen. - * This is an abstract class and should not be used on its own rather it should be extended. - * - * @class DisplayObject - * @constructor - */ -PIXI.DisplayObject = function() -{ - /** - * The coordinate of the object relative to the local coordinates of the parent. - * - * @property position - * @type Point - */ - this.position = new PIXI.Point(); - - /** - * The scale factor of the object. - * - * @property scale - * @type Point - */ - this.scale = new PIXI.Point(1,1);//{x:1, y:1}; - - /** - * The pivot point of the displayObject that it rotates around - * - * @property pivot - * @type Point - */ - this.pivot = new PIXI.Point(0,0); - - /** - * The rotation of the object in radians. - * - * @property rotation - * @type Number - */ - this.rotation = 0; - - /** - * The opacity of the object. - * - * @property alpha - * @type Number - */ - this.alpha = 1; - - /** - * The visibility of the object. - * - * @property visible - * @type Boolean - */ - this.visible = true; - - /** - * This is the defined area that will pick up mouse / touch events. It is null by default. - * Setting it is a neat way of optimising the hitTest function that the interactionManager will use (as it will not need to hit test all the children) - * - * @property hitArea - * @type Rectangle|Circle|Ellipse|Polygon - */ - this.hitArea = null; - - /** - * This is used to indicate if the displayObject should display a mouse hand cursor on rollover - * - * @property buttonMode - * @type Boolean - */ - this.buttonMode = false; - - /** - * Can this object be rendered - * - * @property renderable - * @type Boolean - */ - this.renderable = false; - - /** - * [read-only] The display object container that contains this display object. - * - * @property parent - * @type DisplayObjectContainer - * @readOnly - */ - this.parent = null; - - /** - * [read-only] The stage the display object is connected to, or undefined if it is not connected to the stage. - * - * @property stage - * @type Stage - * @readOnly - */ - this.stage = null; - - /** - * [read-only] The multiplied alpha of the displayObject - * - * @property worldAlpha - * @type Number - * @readOnly - */ - this.worldAlpha = 1; - - /** - * [read-only] Whether or not the object is interactive, do not toggle directly! use the `interactive` property - * - * @property _interactive - * @type Boolean - * @readOnly - * @private - */ - this._interactive = false; - - /** - * This is the cursor that will be used when the mouse is over this object. To enable this the element must have interaction = true and buttonMode = true - * - * @property defaultCursor - * @type String - * - */ - this.defaultCursor = 'pointer'; - - /** - * [read-only] Current transform of the object based on world (parent) factors - * - * @property worldTransform - * @type Matrix - * @readOnly - * @private - */ - this.worldTransform = new PIXI.Matrix(); - - /** - * cached sin rotation and cos rotation - * - * @property _sr - * @type Number - * @private - */ - this._sr = 0; - - /** - * cached sin rotation and cos rotation - * - * @property _cr - * @type Number - * @private - */ - this._cr = 1; - - /** - * The area the filter is applied to like the hitArea this is used as more of an optimisation - * rather than figuring out the dimensions of the displayObject each frame you can set this rectangle - * - * @property filterArea - * @type Rectangle - */ - this.filterArea = null;//new PIXI.Rectangle(0,0,1,1); - - /** - * The original, cached bounds of the object - * - * @property _bounds - * @type Rectangle - * @private - */ - this._bounds = new PIXI.Rectangle(0, 0, 1, 1); - - /** - * The most up-to-date bounds of the object - * - * @property _currentBounds - * @type Rectangle - * @private - */ - this._currentBounds = null; - - /** - * The original, cached mask of the object - * - * @property _currentBounds - * @type Rectangle - * @private - */ - this._mask = null; - - /** - * Cached internal flag. - * - * @property _cacheAsBitmap - * @type Boolean - * @private - */ - this._cacheAsBitmap = false; - - /** - * Cached internal flag. - * - * @property _cacheIsDirty - * @type Boolean - * @private - */ - this._cacheIsDirty = false; - - - /* - * MOUSE Callbacks - */ - - /** - * A callback that is used when the users mouse rolls over the displayObject - * @method mouseover - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the users mouse leaves the displayObject - * @method mouseout - * @param interactionData {InteractionData} - */ - - //Left button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's left button - * @method click - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the user clicks the mouse's left button down over the sprite - * @method mousedown - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * @method mouseup - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the user releases the mouse's left button that was over the displayObject but is no longer over the displayObject - * for this callback to be fired, the mouse's left button must have been pressed down over the displayObject - * @method mouseupoutside - * @param interactionData {InteractionData} - */ - - //Right button - /** - * A callback that is used when the users clicks on the displayObject with their mouse's right button - * @method rightclick - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the user clicks the mouse's right button down over the sprite - * @method rightdown - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject - * for this callback to be fired the mouse's right button must have been pressed down over the displayObject - * @method rightup - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the user releases the mouse's right button that was over the displayObject but is no longer over the displayObject - * for this callback to be fired, the mouse's right button must have been pressed down over the displayObject - * @method rightupoutside - * @param interactionData {InteractionData} - */ - - /* - * TOUCH Callbacks - */ - - /** - * A callback that is used when the users taps on the sprite with their finger - * basically a touch version of click - * @method tap - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the user touches over the displayObject - * @method touchstart - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the user releases a touch over the displayObject - * @method touchend - * @param interactionData {InteractionData} - */ - - /** - * A callback that is used when the user releases the touch that was over the displayObject - * for this callback to be fired, The touch must have started over the sprite - * @method touchendoutside - * @param interactionData {InteractionData} - */ -}; - -// constructor -PIXI.DisplayObject.prototype.constructor = PIXI.DisplayObject; - -/** - * Indicates if the sprite will have touch and mouse interactivity. It is false by default - * - * @property interactive - * @type Boolean - * @default false - */ -Object.defineProperty(PIXI.DisplayObject.prototype, 'interactive', { - get: function() { - return this._interactive; - }, - set: function(value) { - this._interactive = value; - - // TODO more to be done here.. - // need to sort out a re-crawl! - if(this.stage)this.stage.dirty = true; - } -}); - -/** - * [read-only] Indicates if the sprite is globally visible. - * - * @property worldVisible - * @type Boolean - */ -Object.defineProperty(PIXI.DisplayObject.prototype, 'worldVisible', { - get: function() { - var item = this; - - do - { - if(!item.visible)return false; - item = item.parent; - } - while(item); - - return true; - } -}); - -/** - * Sets a mask for the displayObject. A mask is an object that limits the visibility of an object to the shape of the mask applied to it. - * In PIXI a regular mask must be a PIXI.Graphics object. This allows for much faster masking in canvas as it utilises shape clipping. - * To remove a mask, set this property to null. - * - * @property mask - * @type Graphics - */ -Object.defineProperty(PIXI.DisplayObject.prototype, 'mask', { - get: function() { - return this._mask; - }, - set: function(value) { - - if(this._mask)this._mask.isMask = false; - this._mask = value; - if(this._mask)this._mask.isMask = true; - } -}); - -/** - * Sets the filters for the displayObject. - * * IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer. - * To remove filters simply set this property to 'null' - * @property filters - * @type Array(Filter) - */ -Object.defineProperty(PIXI.DisplayObject.prototype, 'filters', { - - get: function() { - return this._filters; - }, - - set: function(value) { - - if(value) - { - // now put all the passes in one place.. - var passes = []; - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = {target:this, filterPasses:passes}; - } - - this._filters = value; - } -}); - -/** - * Set if this display object is cached as a bitmap. - * This basically takes a snap shot of the display object as it is at that moment. It can provide a performance benefit for complex static displayObjects. - * To remove simply set this property to 'null' - * @property cacheAsBitmap - * @type Boolean - */ -Object.defineProperty(PIXI.DisplayObject.prototype, 'cacheAsBitmap', { - - get: function() { - return this._cacheAsBitmap; - }, - - set: function(value) { - - if(this._cacheAsBitmap === value)return; - - if(value) - { - this._generateCachedSprite(); - } - else - { - this._destroyCachedSprite(); - } - - this._cacheAsBitmap = value; - } -}); - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -PIXI.DisplayObject.prototype.updateTransform = function() -{ - // create some matrix refs for easy access - var pt = this.parent.worldTransform; - var wt = this.worldTransform; - - // temporary matrix variables - var a, b, c, d, tx, ty; - - // so if rotation is between 0 then we can simplify the multiplication process.. - if(this.rotation % PIXI.PI_2) - { - // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if(this.rotation !== this.rotationCache) - { - this.rotationCache = this.rotation; - this._sr = Math.sin(this.rotation); - this._cr = Math.cos(this.rotation); - } - - // get the matrix values of the displayobject based on its transform properties.. - a = this._cr * this.scale.x; - b = this._sr * this.scale.x; - c = -this._sr * this.scale.y; - d = this._cr * this.scale.y; - tx = this.position.x; - ty = this.position.y; - - // check for pivot.. not often used so geared towards that fact! - if(this.pivot.x || this.pivot.y) - { - tx -= this.pivot.x * a + this.pivot.y * c; - ty -= this.pivot.x * b + this.pivot.y * d; - } - - // concat the parent matrix with the objects transform. - wt.a = a * pt.a + b * pt.c; - wt.b = a * pt.b + b * pt.d; - wt.c = c * pt.a + d * pt.c; - wt.d = c * pt.b + d * pt.d; - wt.tx = tx * pt.a + ty * pt.c + pt.tx; - wt.ty = tx * pt.b + ty * pt.d + pt.ty; - - - } - else - { - // lets do the fast version as we know there is no rotation.. - a = this.scale.x; - d = this.scale.y; - - tx = this.position.x - this.pivot.x * a; - ty = this.position.y - this.pivot.y * d; - - wt.a = a * pt.a; - wt.b = a * pt.b; - wt.c = d * pt.c; - wt.d = d * pt.d; - wt.tx = tx * pt.a + ty * pt.c + pt.tx; - wt.ty = tx * pt.b + ty * pt.d + pt.ty; - } - - // multiply the alphas.. - this.worldAlpha = this.alpha * this.parent.worldAlpha; -}; - -// performance increase to avoid using call.. (10x faster) -PIXI.DisplayObject.prototype.displayObjectUpdateTransform = PIXI.DisplayObject.prototype.updateTransform; - -/** - * Retrieves the bounds of the displayObject as a rectangle object - * - * @method getBounds - * @param matrix {Matrix} - * @return {Rectangle} the rectangular bounding area - */ -PIXI.DisplayObject.prototype.getBounds = function(matrix) -{ - matrix = matrix;//just to get passed js hinting (and preserve inheritance) - return PIXI.EmptyRectangle; -}; - -/** - * Retrieves the local bounds of the displayObject as a rectangle object - * - * @method getLocalBounds - * @return {Rectangle} the rectangular bounding area - */ -PIXI.DisplayObject.prototype.getLocalBounds = function() -{ - return this.getBounds(PIXI.identityMatrix);///PIXI.EmptyRectangle(); -}; - -/** - * Sets the object's stage reference, the stage this object is connected to - * - * @method setStageReference - * @param stage {Stage} the stage that the object will have as its current stage reference - */ -PIXI.DisplayObject.prototype.setStageReference = function(stage) -{ - this.stage = stage; - if(this._interactive)this.stage.dirty = true; -}; - -/** - * Useful function that returns a texture of the displayObject object that can then be used to create sprites - * This can be quite useful if your displayObject is static / complicated and needs to be reused multiple times. - * - * @method generateTexture - * @param resolution {Number} The resolution of the texture being generated - * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values - * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used to generate the texture. - * @return {Texture} a texture of the graphics object - */ -PIXI.DisplayObject.prototype.generateTexture = function(resolution, scaleMode, renderer) -{ - var bounds = this.getLocalBounds(); - - var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0, renderer, scaleMode, resolution); - - PIXI.DisplayObject._tempMatrix.tx = -bounds.x; - PIXI.DisplayObject._tempMatrix.ty = -bounds.y; - - renderTexture.render(this, PIXI.DisplayObject._tempMatrix); - - return renderTexture; -}; - -/** - * Generates and updates the cached sprite for this object. - * - * @method updateCache - */ -PIXI.DisplayObject.prototype.updateCache = function() -{ - this._generateCachedSprite(); -}; - -/** - * Calculates the global position of the display object - * - * @method toGlobal - * @param position {Point} The world origin to calculate from - * @return {Point} A point object representing the position of this object - */ -PIXI.DisplayObject.prototype.toGlobal = function(position) -{ - // don't need to u[date the lot - this.displayObjectUpdateTransform(); - return this.worldTransform.apply(position); -}; - -/** - * Calculates the local position of the display object relative to another point - * - * @method toLocal - * @param position {Point} The world origin to calculate from - * @param [from] {DisplayObject} The DisplayObject to calculate the global position from - * @return {Point} A point object representing the position of this object - */ -PIXI.DisplayObject.prototype.toLocal = function(position, from) -{ - // - if (from) - { - position = from.toGlobal(position); - } - - // don't need to u[date the lot - this.displayObjectUpdateTransform(); - return this.worldTransform.applyInverse(position); -}; - -/** - * Internal method. - * - * @method _renderCachedSprite - * @param renderSession {Object} The render session - * @private - */ -PIXI.DisplayObject.prototype._renderCachedSprite = function(renderSession) -{ - this._cachedSprite.worldAlpha = this.worldAlpha; - - if(renderSession.gl) - { - PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); - } - else - { - PIXI.Sprite.prototype._renderCanvas.call(this._cachedSprite, renderSession); - } -}; - -/** - * Internal method. - * - * @method _generateCachedSprite - * @private - */ -PIXI.DisplayObject.prototype._generateCachedSprite = function() -{ - this._cacheAsBitmap = false; - var bounds = this.getLocalBounds(); - - if(!this._cachedSprite) - { - var renderTexture = new PIXI.RenderTexture(bounds.width | 0, bounds.height | 0);//, renderSession.renderer); - - this._cachedSprite = new PIXI.Sprite(renderTexture); - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.texture.resize(bounds.width | 0, bounds.height | 0); - } - - //REMOVE filter! - var tempFilters = this._filters; - this._filters = null; - - this._cachedSprite.filters = tempFilters; - - PIXI.DisplayObject._tempMatrix.tx = -bounds.x; - PIXI.DisplayObject._tempMatrix.ty = -bounds.y; - - this._cachedSprite.texture.render(this, PIXI.DisplayObject._tempMatrix, true); - - this._cachedSprite.anchor.x = -( bounds.x / bounds.width ); - this._cachedSprite.anchor.y = -( bounds.y / bounds.height ); - - this._filters = tempFilters; - - this._cacheAsBitmap = true; -}; - -/** -* Destroys the cached sprite. -* -* @method _destroyCachedSprite -* @private -*/ -PIXI.DisplayObject.prototype._destroyCachedSprite = function() -{ - if(!this._cachedSprite)return; - - this._cachedSprite.texture.destroy(true); - - // TODO could be object pooled! - this._cachedSprite = null; -}; - -/** -* Renders the object using the WebGL renderer -* -* @method _renderWebGL -* @param renderSession {RenderSession} -* @private -*/ -PIXI.DisplayObject.prototype._renderWebGL = function(renderSession) -{ - // OVERWRITE; - // this line is just here to pass jshinting :) - renderSession = renderSession; -}; - -/** -* Renders the object using the Canvas renderer -* -* @method _renderCanvas -* @param renderSession {RenderSession} -* @private -*/ -PIXI.DisplayObject.prototype._renderCanvas = function(renderSession) -{ - // OVERWRITE; - // this line is just here to pass jshinting :) - renderSession = renderSession; -}; - - -PIXI.DisplayObject._tempMatrix = new PIXI.Matrix(); - -/** - * The position of the displayObject on the x axis relative to the local coordinates of the parent. - * - * @property x - * @type Number - */ -Object.defineProperty(PIXI.DisplayObject.prototype, 'x', { - get: function() { - return this.position.x; - }, - set: function(value) { - this.position.x = value; - } -}); - -/** - * The position of the displayObject on the y axis relative to the local coordinates of the parent. - * - * @property y - * @type Number - */ -Object.defineProperty(PIXI.DisplayObject.prototype, 'y', { - get: function() { - return this.position.y; - }, - set: function(value) { - this.position.y = value; - } -}); - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * A DisplayObjectContainer represents a collection of display objects. - * It is the base class of all display objects that act as a container for other objects. - * - * @class DisplayObjectContainer - * @extends DisplayObject - * @constructor - */ -PIXI.DisplayObjectContainer = function() -{ - PIXI.DisplayObject.call( this ); - - /** - * [read-only] The array of children of this container. - * - * @property children - * @type Array(DisplayObject) - * @readOnly - */ - this.children = []; - - // fast access to update transform.. - -}; - -// constructor -PIXI.DisplayObjectContainer.prototype = Object.create( PIXI.DisplayObject.prototype ); -PIXI.DisplayObjectContainer.prototype.constructor = PIXI.DisplayObjectContainer; - - -/** - * The width of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @property width - * @type Number - */ -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'width', { - get: function() { - return this.scale.x * this.getLocalBounds().width; - }, - set: function(value) { - - var width = this.getLocalBounds().width; - - if(width !== 0) - { - this.scale.x = value / width; - } - else - { - this.scale.x = 1; - } - - - this._width = value; - } -}); - -/** - * The height of the displayObjectContainer, setting this will actually modify the scale to achieve the value set - * - * @property height - * @type Number - */ -Object.defineProperty(PIXI.DisplayObjectContainer.prototype, 'height', { - get: function() { - return this.scale.y * this.getLocalBounds().height; - }, - set: function(value) { - - var height = this.getLocalBounds().height; - - if(height !== 0) - { - this.scale.y = value / height ; - } - else - { - this.scale.y = 1; - } - - this._height = value; - } -}); - -/** - * Adds a child to the container. - * - * @method addChild - * @param child {DisplayObject} The DisplayObject to add to the container - * @return {DisplayObject} The child that was added. - */ -PIXI.DisplayObjectContainer.prototype.addChild = function(child) -{ - return this.addChildAt(child, this.children.length); -}; - -/** - * Adds a child to the container at a specified index. If the index is out of bounds an error will be thrown - * - * @method addChildAt - * @param child {DisplayObject} The child to add - * @param index {Number} The index to place the child in - * @return {DisplayObject} The child that was added. - */ -PIXI.DisplayObjectContainer.prototype.addChildAt = function(child, index) -{ - if(index >= 0 && index <= this.children.length) - { - if(child.parent) - { - child.parent.removeChild(child); - } - - child.parent = this; - - this.children.splice(index, 0, child); - - if(this.stage)child.setStageReference(this.stage); - - return child; - } - else - { - throw new Error(child + 'addChildAt: The index '+ index +' supplied is out of bounds ' + this.children.length); - } -}; - -/** - * Swaps the position of 2 Display Objects within this container. - * - * @method swapChildren - * @param child {DisplayObject} - * @param child2 {DisplayObject} - */ -PIXI.DisplayObjectContainer.prototype.swapChildren = function(child, child2) -{ - if(child === child2) { - return; - } - - var index1 = this.getChildIndex(child); - var index2 = this.getChildIndex(child2); - - if(index1 < 0 || index2 < 0) { - throw new Error('swapChildren: Both the supplied DisplayObjects must be a child of the caller.'); - } - - this.children[index1] = child2; - this.children[index2] = child; - -}; - -/** - * Returns the index position of a child DisplayObject instance - * - * @method getChildIndex - * @param child {DisplayObject} The DisplayObject instance to identify - * @return {Number} The index position of the child display object to identify - */ -PIXI.DisplayObjectContainer.prototype.getChildIndex = function(child) -{ - var index = this.children.indexOf(child); - if (index === -1) - { - throw new Error('The supplied DisplayObject must be a child of the caller'); - } - return index; -}; - -/** - * Changes the position of an existing child in the display object container - * - * @method setChildIndex - * @param child {DisplayObject} The child DisplayObject instance for which you want to change the index number - * @param index {Number} The resulting index number for the child display object - */ -PIXI.DisplayObjectContainer.prototype.setChildIndex = function(child, index) -{ - if (index < 0 || index >= this.children.length) - { - throw new Error('The supplied index is out of bounds'); - } - var currentIndex = this.getChildIndex(child); - this.children.splice(currentIndex, 1); //remove from old position - this.children.splice(index, 0, child); //add at new position -}; - -/** - * Returns the child at the specified index - * - * @method getChildAt - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child at the given index, if any. - */ -PIXI.DisplayObjectContainer.prototype.getChildAt = function(index) -{ - if (index < 0 || index >= this.children.length) - { - throw new Error('getChildAt: Supplied index '+ index +' does not exist in the child list, or the supplied DisplayObject must be a child of the caller'); - } - return this.children[index]; - -}; - -/** - * Removes a child from the container. - * - * @method removeChild - * @param child {DisplayObject} The DisplayObject to remove - * @return {DisplayObject} The child that was removed. - */ -PIXI.DisplayObjectContainer.prototype.removeChild = function(child) -{ - var index = this.children.indexOf( child ); - if(index === -1)return; - - return this.removeChildAt( index ); -}; - -/** - * Removes a child from the specified index position. - * - * @method removeChildAt - * @param index {Number} The index to get the child from - * @return {DisplayObject} The child that was removed. - */ -PIXI.DisplayObjectContainer.prototype.removeChildAt = function(index) -{ - var child = this.getChildAt( index ); - if(this.stage) - child.removeStageReference(); - - child.parent = undefined; - this.children.splice( index, 1 ); - return child; -}; - -/** -* Removes all children from this container that are within the begin and end indexes. -* -* @method removeChildren -* @param beginIndex {Number} The beginning position. Default value is 0. -* @param endIndex {Number} The ending position. Default value is size of the container. -*/ -PIXI.DisplayObjectContainer.prototype.removeChildren = function(beginIndex, endIndex) -{ - var begin = beginIndex || 0; - var end = typeof endIndex === 'number' ? endIndex : this.children.length; - var range = end - begin; - - if (range > 0 && range <= end) - { - var removed = this.children.splice(begin, range); - for (var i = 0; i < removed.length; i++) { - var child = removed[i]; - if(this.stage) - child.removeStageReference(); - child.parent = undefined; - } - return removed; - } - else if (range === 0 && this.children.length === 0) - { - return []; - } - else - { - throw new Error( 'removeChildren: Range Error, numeric values are outside the acceptable range' ); - } -}; - -/* - * Updates the transform on all children of this container for rendering - * - * @method updateTransform - * @private - */ -PIXI.DisplayObjectContainer.prototype.updateTransform = function() -{ - if(!this.visible)return; - - this.displayObjectUpdateTransform(); - - //PIXI.DisplayObject.prototype.updateTransform.call( this ); - - if(this._cacheAsBitmap)return; - - for(var i=0,j=this.children.length; i childMaxX ? maxX : childMaxX; - maxY = maxY > childMaxY ? maxY : childMaxY; - } - - if(!childVisible) - return PIXI.EmptyRectangle; - - var bounds = this._bounds; - - bounds.x = minX; - bounds.y = minY; - bounds.width = maxX - minX; - bounds.height = maxY - minY; - - // TODO: store a reference so that if this function gets called again in the render cycle we do not have to recalculate - //this._currentBounds = bounds; - - return bounds; -}; - -/** - * Retrieves the non-global local bounds of the displayObjectContainer as a rectangle. The calculation takes all visible children into consideration. - * - * @method getLocalBounds - * @return {Rectangle} The rectangular bounding area - */ -PIXI.DisplayObjectContainer.prototype.getLocalBounds = function() -{ - var matrixCache = this.worldTransform; - - this.worldTransform = PIXI.identityMatrix; - - for(var i=0,j=this.children.length; i maxX ? x1 : maxX; - maxX = x2 > maxX ? x2 : maxX; - maxX = x3 > maxX ? x3 : maxX; - maxX = x4 > maxX ? x4 : maxX; - - maxY = y1 > maxY ? y1 : maxY; - maxY = y2 > maxY ? y2 : maxY; - maxY = y3 > maxY ? y3 : maxY; - maxY = y4 > maxY ? y4 : maxY; - } - - var bounds = this._bounds; - - bounds.x = minX; - bounds.width = maxX - minX; - - bounds.y = minY; - bounds.height = maxY - minY; - - // store a reference so that if this function gets called again in the render cycle we do not have to recalculate - this._currentBounds = bounds; - - return bounds; -}; - -/** -* Renders the object using the WebGL renderer -* -* @method _renderWebGL -* @param renderSession {RenderSession} -* @private -*/ -PIXI.Sprite.prototype._renderWebGL = function(renderSession) -{ - // if the sprite is not visible or the alpha is 0 then no need to render this element - if(!this.visible || this.alpha <= 0)return; - - var i,j; - - // do a quick check to see if this element has a mask or a filter. - if(this._mask || this._filters) - { - var spriteBatch = renderSession.spriteBatch; - - // push filter first as we need to ensure the stencil buffer is correct for any masking - if(this._filters) - { - spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); - } - - if(this._mask) - { - spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - spriteBatch.start(); - } - - // add this sprite to the batch - spriteBatch.render(this); - - // now loop through the children and make sure they get rendered - for(i=0,j=this.children.length; i= this.textures.length) - { - this.gotoAndStop(this.textures.length - 1); - if(this.onComplete) - { - this.onComplete(); - } - } -}; - -/** - * A short hand way of creating a movieclip from an array of frame ids - * - * @static - * @method fromFrames - * @param frames {Array} the array of frames ids the movieclip will use as its texture frames - */ -PIXI.MovieClip.fromFrames = function(frames) -{ - var textures = []; - - for (var i = 0; i < frames.length; i++) - { - textures.push(new PIXI.Texture.fromFrame(frames[i])); - } - - return new PIXI.MovieClip(textures); -}; - -/** - * A short hand way of creating a movieclip from an array of image ids - * - * @static - * @method fromImages - * @param frames {Array} the array of image ids the movieclip will use as its texture frames - */ -PIXI.MovieClip.fromImages = function(images) -{ - var textures = []; - - for (var i = 0; i < images.length; i++) - { - textures.push(new PIXI.Texture.fromImage(images[i])); - } - - return new PIXI.MovieClip(textures); -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * A target and pass info object for filters. - * - * @class FilterBlock - * @constructor - */ -PIXI.FilterBlock = function() -{ - /** - * The visible state of this FilterBlock. - * - * @property visible - * @type Boolean - */ - this.visible = true; - - /** - * The renderable state of this FilterBlock. - * - * @property renderable - * @type Boolean - */ - this.renderable = true; -}; - -PIXI.FilterBlock.prototype.constructor = PIXI.FilterBlock; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - * Modified by Tom Slezakowski http://www.tomslezakowski.com @TomSlezakowski (24/03/2014) - Added dropShadowColor. - */ - -/** - * A Text Object will create a line or multiple lines of text. To split a line you can use '\n' in your text string, - * or add a wordWrap property set to true and and wordWrapWidth property with a value in the style object. - * - * @class Text - * @extends Sprite - * @constructor - * @param text {String} The copy that you would like the text to display - * @param [style] {Object} The style parameters - * @param [style.font] {String} default 'bold 20px Arial' The style and size of the font - * @param [style.fill='black'] {String|Number} A canvas fillstyle that will be used on the text e.g 'red', '#00FF00' - * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text - * @param [style.stroke] {String|Number} A canvas fillstyle that will be used on the text stroke e.g 'blue', '#FCFF00' - * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used - * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap, it needs wordWrap to be set to true - * @param [style.dropShadow=false] {Boolean} Set a drop shadow for the text - * @param [style.dropShadowColor='#000000'] {String} A fill style to be used on the dropshadow e.g 'red', '#00FF00' - * @param [style.dropShadowAngle=Math.PI/4] {Number} Set a angle of the drop shadow - * @param [style.dropShadowDistance=5] {Number} Set a distance of the drop shadow - */ -PIXI.Text = function(text, style) -{ - /** - * The canvas element that everything is drawn to - * - * @property canvas - * @type HTMLCanvasElement - */ - this.canvas = document.createElement('canvas'); - - /** - * The canvas 2d context that everything is drawn with - * @property context - * @type HTMLCanvasElement - */ - this.context = this.canvas.getContext('2d'); - - /** - * The resolution of the canvas. - * @property resolution - * @type Number - */ - this.resolution = 1; - - PIXI.Sprite.call(this, PIXI.Texture.fromCanvas(this.canvas)); - - this.setText(text); - this.setStyle(style); - -}; - -// constructor -PIXI.Text.prototype = Object.create(PIXI.Sprite.prototype); -PIXI.Text.prototype.constructor = PIXI.Text; - -/** - * The width of the Text, setting this will actually modify the scale to achieve the value set - * - * @property width - * @type Number - */ -Object.defineProperty(PIXI.Text.prototype, 'width', { - get: function() { - - if(this.dirty) - { - this.updateText(); - this.dirty = false; - } - - - return this.scale.x * this.texture.frame.width; - }, - set: function(value) { - this.scale.x = value / this.texture.frame.width; - this._width = value; - } -}); - -/** - * The height of the Text, setting this will actually modify the scale to achieve the value set - * - * @property height - * @type Number - */ -Object.defineProperty(PIXI.Text.prototype, 'height', { - get: function() { - - if(this.dirty) - { - this.updateText(); - this.dirty = false; - } - - - return this.scale.y * this.texture.frame.height; - }, - set: function(value) { - this.scale.y = value / this.texture.frame.height; - this._height = value; - } -}); - -/** - * Set the style of the text - * - * @method setStyle - * @param [style] {Object} The style parameters - * @param [style.font='bold 20pt Arial'] {String} The style and size of the font - * @param [style.fill='black'] {Object} A canvas fillstyle that will be used on the text eg 'red', '#00FF00' - * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text - * @param [style.stroke='black'] {String} A canvas fillstyle that will be used on the text stroke eg 'blue', '#FCFF00' - * @param [style.strokeThickness=0] {Number} A number that represents the thickness of the stroke. Default is 0 (no stroke) - * @param [style.wordWrap=false] {Boolean} Indicates if word wrap should be used - * @param [style.wordWrapWidth=100] {Number} The width at which text will wrap - * @param [style.dropShadow=false] {Boolean} Set a drop shadow for the text - * @param [style.dropShadowColor='#000000'] {String} A fill style to be used on the dropshadow e.g 'red', '#00FF00' - * @param [style.dropShadowAngle=Math.PI/4] {Number} Set a angle of the drop shadow - * @param [style.dropShadowDistance=5] {Number} Set a distance of the drop shadow - */ -PIXI.Text.prototype.setStyle = function(style) -{ - style = style || {}; - style.font = style.font || 'bold 20pt Arial'; - style.fill = style.fill || 'black'; - style.align = style.align || 'left'; - style.stroke = style.stroke || 'black'; //provide a default, see: https://github.com/GoodBoyDigital/pixi.js/issues/136 - style.strokeThickness = style.strokeThickness || 0; - style.wordWrap = style.wordWrap || false; - style.wordWrapWidth = style.wordWrapWidth || 100; - - style.dropShadow = style.dropShadow || false; - style.dropShadowAngle = style.dropShadowAngle || Math.PI / 6; - style.dropShadowDistance = style.dropShadowDistance || 4; - style.dropShadowColor = style.dropShadowColor || 'black'; - - this.style = style; - this.dirty = true; -}; - -/** - * Set the copy for the text object. To split a line you can use '\n'. - * - * @method setText - * @param text {String} The copy that you would like the text to display - */ -PIXI.Text.prototype.setText = function(text) -{ - this.text = text.toString() || ' '; - this.dirty = true; -}; - -/** - * Renders text and updates it when needed - * - * @method updateText - * @private - */ -PIXI.Text.prototype.updateText = function() -{ - this.texture.baseTexture.resolution = this.resolution; - - this.context.font = this.style.font; - - var outputText = this.text; - - // word wrap - // preserve original text - if(this.style.wordWrap)outputText = this.wordWrap(this.text); - - //split text into lines - var lines = outputText.split(/(?:\r\n|\r|\n)/); - - //calculate text width - var lineWidths = []; - var maxLineWidth = 0; - var fontProperties = this.determineFontProperties(this.style.font); - for (var i = 0; i < lines.length; i++) - { - var lineWidth = this.context.measureText(lines[i]).width; - lineWidths[i] = lineWidth; - maxLineWidth = Math.max(maxLineWidth, lineWidth); - } - - var width = maxLineWidth + this.style.strokeThickness; - if(this.style.dropShadow)width += this.style.dropShadowDistance; - - this.canvas.width = ( width + this.context.lineWidth ) * this.resolution; - - //calculate text height - var lineHeight = fontProperties.fontSize + this.style.strokeThickness; - - var height = lineHeight * lines.length; - if(this.style.dropShadow)height += this.style.dropShadowDistance; - - this.canvas.height = height * this.resolution; - - this.context.scale( this.resolution, this.resolution); - - if(navigator.isCocoonJS) this.context.clearRect(0,0,this.canvas.width,this.canvas.height); - - // used for debugging.. - //this.context.fillStyle ="#FF0000" - //this.context.fillRect(0, 0, this.canvas.width,this.canvas.height); - - this.context.font = this.style.font; - this.context.strokeStyle = this.style.stroke; - this.context.lineWidth = this.style.strokeThickness; - this.context.textBaseline = 'alphabetic'; - //this.context.lineJoin = 'round'; - - var linePositionX; - var linePositionY; - - if(this.style.dropShadow) - { - this.context.fillStyle = this.style.dropShadowColor; - - var xShadowOffset = Math.sin(this.style.dropShadowAngle) * this.style.dropShadowDistance; - var yShadowOffset = Math.cos(this.style.dropShadowAngle) * this.style.dropShadowDistance; - - for (i = 0; i < lines.length; i++) - { - linePositionX = this.style.strokeThickness / 2; - linePositionY = (this.style.strokeThickness / 2 + i * lineHeight) + fontProperties.ascent; - - if(this.style.align === 'right') - { - linePositionX += maxLineWidth - lineWidths[i]; - } - else if(this.style.align === 'center') - { - linePositionX += (maxLineWidth - lineWidths[i]) / 2; - } - - if(this.style.fill) - { - this.context.fillText(lines[i], linePositionX + xShadowOffset, linePositionY + yShadowOffset); - } - - // if(dropShadow) - } - } - - //set canvas text styles - this.context.fillStyle = this.style.fill; - - //draw lines line by line - for (i = 0; i < lines.length; i++) - { - linePositionX = this.style.strokeThickness / 2; - linePositionY = (this.style.strokeThickness / 2 + i * lineHeight) + fontProperties.ascent; - - if(this.style.align === 'right') - { - linePositionX += maxLineWidth - lineWidths[i]; - } - else if(this.style.align === 'center') - { - linePositionX += (maxLineWidth - lineWidths[i]) / 2; - } - - if(this.style.stroke && this.style.strokeThickness) - { - this.context.strokeText(lines[i], linePositionX, linePositionY); - } - - if(this.style.fill) - { - this.context.fillText(lines[i], linePositionX, linePositionY); - } - - // if(dropShadow) - } - - this.updateTexture(); -}; - -/** - * Updates texture size based on canvas size - * - * @method updateTexture - * @private - */ -PIXI.Text.prototype.updateTexture = function() -{ - this.texture.baseTexture.width = this.canvas.width; - this.texture.baseTexture.height = this.canvas.height; - this.texture.crop.width = this.texture.frame.width = this.canvas.width; - this.texture.crop.height = this.texture.frame.height = this.canvas.height; - - this._width = this.canvas.width; - this._height = this.canvas.height; - - // update the dirty base textures - this.texture.baseTexture.dirty(); -}; - -/** -* Renders the object using the WebGL renderer -* -* @method _renderWebGL -* @param renderSession {RenderSession} -* @private -*/ -PIXI.Text.prototype._renderWebGL = function(renderSession) -{ - if(this.dirty) - { - this.resolution = renderSession.resolution; - - this.updateText(); - this.dirty = false; - } - - PIXI.Sprite.prototype._renderWebGL.call(this, renderSession); -}; - -/** -* Renders the object using the Canvas renderer -* -* @method _renderCanvas -* @param renderSession {RenderSession} -* @private -*/ -PIXI.Text.prototype._renderCanvas = function(renderSession) -{ - if(this.dirty) - { - this.resolution = renderSession.resolution; - - this.updateText(); - this.dirty = false; - } - - PIXI.Sprite.prototype._renderCanvas.call(this, renderSession); -}; - -/** -* Calculates the ascent, descent and fontSize of a given fontStyle -* -* @method determineFontProperties -* @param fontStyle {Object} -* @private -*/ -PIXI.Text.prototype.determineFontProperties = function(fontStyle) -{ - var properties = PIXI.Text.fontPropertiesCache[fontStyle]; - - if(!properties) - { - properties = {}; - - var canvas = PIXI.Text.fontPropertiesCanvas; - var context = PIXI.Text.fontPropertiesContext; - - context.font = fontStyle; - - var width = Math.ceil(context.measureText('|Mq').width); - var baseline = Math.ceil(context.measureText('M').width); - var height = 2 * baseline; - - baseline = baseline * 1.4 | 0; - - canvas.width = width; - canvas.height = height; - - context.fillStyle = '#f00'; - context.fillRect(0, 0, width, height); - - context.font = fontStyle; - - context.textBaseline = 'alphabetic'; - context.fillStyle = '#000'; - context.fillText('|MÉq', 0, baseline); - - var imagedata = context.getImageData(0, 0, width, height).data; - var pixels = imagedata.length; - var line = width * 4; - - var i, j; - - var idx = 0; - var stop = false; - - // ascent. scan from top to bottom until we find a non red pixel - for(i = 0; i < baseline; i++) - { - for(j = 0; j < line; j += 4) - { - if(imagedata[idx + j] !== 255) - { - stop = true; - break; - } - } - if(!stop) - { - idx += line; - } - else - { - break; - } - } - - properties.ascent = baseline - i; - - idx = pixels - line; - stop = false; - - // descent. scan from bottom to top until we find a non red pixel - for(i = height; i > baseline; i--) - { - for(j = 0; j < line; j += 4) - { - if(imagedata[idx + j] !== 255) - { - stop = true; - break; - } - } - if(!stop) - { - idx -= line; - } - else - { - break; - } - } - - properties.descent = i - baseline; - //TODO might need a tweak. kind of a temp fix! - properties.descent += 6; - properties.fontSize = properties.ascent + properties.descent; - - PIXI.Text.fontPropertiesCache[fontStyle] = properties; - } - - return properties; -}; - -/** - * Applies newlines to a string to have it optimally fit into the horizontal - * bounds set by the Text object's wordWrapWidth property. - * - * @method wordWrap - * @param text {String} - * @private - */ -PIXI.Text.prototype.wordWrap = function(text) -{ - // Greedy wrapping algorithm that will wrap words as the line grows longer - // than its horizontal bounds. - var result = ''; - var lines = text.split('\n'); - for (var i = 0; i < lines.length; i++) - { - var spaceLeft = this.style.wordWrapWidth; - var words = lines[i].split(' '); - for (var j = 0; j < words.length; j++) - { - var wordWidth = this.context.measureText(words[j]).width; - var wordWidthWithSpace = wordWidth + this.context.measureText(' ').width; - if(j === 0 || wordWidthWithSpace > spaceLeft) - { - // Skip printing the newline if it's the first word of the line that is - // greater than the word wrap width. - if(j > 0) - { - result += '\n'; - } - result += words[j]; - spaceLeft = this.style.wordWrapWidth - wordWidth; - } - else - { - spaceLeft -= wordWidthWithSpace; - result += ' ' + words[j]; - } - } - - if (i < lines.length-1) - { - result += '\n'; - } - } - return result; -}; - -/** -* Returns the bounds of the Text as a rectangle. The bounds calculation takes the worldTransform into account. -* -* @method getBounds -* @param matrix {Matrix} the transformation matrix of the Text -* @return {Rectangle} the framing rectangle -*/ -PIXI.Text.prototype.getBounds = function(matrix) -{ - if(this.dirty) - { - this.updateText(); - this.dirty = false; - } - - return PIXI.Sprite.prototype.getBounds.call(this, matrix); -}; - -/** - * Destroys this text object. - * - * @method destroy - * @param destroyBaseTexture {Boolean} whether to destroy the base texture as well - */ -PIXI.Text.prototype.destroy = function(destroyBaseTexture) -{ - // make sure to reset the the context and canvas.. dont want this hanging around in memory! - this.context = null; - this.canvas = null; - - this.texture.destroy(destroyBaseTexture === undefined ? true : destroyBaseTexture); -}; - -PIXI.Text.fontPropertiesCache = {}; -PIXI.Text.fontPropertiesCanvas = document.createElement('canvas'); -PIXI.Text.fontPropertiesContext = PIXI.Text.fontPropertiesCanvas.getContext('2d'); - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * A BitmapText object will create a line or multiple lines of text using bitmap font. To split a line you can use '\n', '\r' or '\r\n' in your string. - * You can generate the fnt files using - * http://www.angelcode.com/products/bmfont/ for windows or - * http://www.bmglyph.com/ for mac. - * - * @class BitmapText - * @extends DisplayObjectContainer - * @constructor - * @param text {String} The copy that you would like the text to display - * @param style {Object} The style parameters - * @param style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously) - * @param [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text - */ -PIXI.BitmapText = function(text, style) -{ - PIXI.DisplayObjectContainer.call(this); - - /** - * [read-only] The width of the overall text, different from fontSize, - * which is defined in the style object - * - * @property textWidth - * @type Number - * @readOnly - */ - this.textWidth = 0; - - /** - * [read-only] The height of the overall text, different from fontSize, - * which is defined in the style object - * - * @property textHeight - * @type Number - * @readOnly - */ - this.textHeight = 0; - - /** - * @property _pool - * @type Array - * @private - */ - this._pool = []; - - this.setText(text); - this.setStyle(style); - this.updateText(); - - /** - * The dirty state of this object. - * @property dirty - * @type Boolean - */ - this.dirty = false; -}; - -// constructor -PIXI.BitmapText.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); -PIXI.BitmapText.prototype.constructor = PIXI.BitmapText; - -/** - * Set the text string to be rendered. - * - * @method setText - * @param text {String} The text that you would like displayed - */ -PIXI.BitmapText.prototype.setText = function(text) -{ - this.text = text || ' '; - this.dirty = true; -}; - -/** - * Set the style of the text - * style.font {String} The size (optional) and bitmap font id (required) eq 'Arial' or '20px Arial' (must have loaded previously) - * [style.align='left'] {String} Alignment for multiline text ('left', 'center' or 'right'), does not affect single lines of text - * - * @method setStyle - * @param style {Object} The style parameters, contained as properties of an object - */ -PIXI.BitmapText.prototype.setStyle = function(style) -{ - style = style || {}; - style.align = style.align || 'left'; - this.style = style; - - var font = style.font.split(' '); - this.fontName = font[font.length - 1]; - this.fontSize = font.length >= 2 ? parseInt(font[font.length - 2], 10) : PIXI.BitmapText.fonts[this.fontName].size; - - this.dirty = true; - this.tint = style.tint; -}; - -/** - * Renders text and updates it when needed - * - * @method updateText - * @private - */ -PIXI.BitmapText.prototype.updateText = function() -{ - var data = PIXI.BitmapText.fonts[this.fontName]; - var pos = new PIXI.Point(); - var prevCharCode = null; - var chars = []; - var maxLineWidth = 0; - var lineWidths = []; - var line = 0; - var scale = this.fontSize / data.size; - - for(var i = 0; i < this.text.length; i++) - { - var charCode = this.text.charCodeAt(i); - - if(/(?:\r\n|\r|\n)/.test(this.text.charAt(i))) - { - lineWidths.push(pos.x); - maxLineWidth = Math.max(maxLineWidth, pos.x); - line++; - - pos.x = 0; - pos.y += data.lineHeight; - prevCharCode = null; - continue; - } - - var charData = data.chars[charCode]; - - if(!charData) continue; - - if(prevCharCode && charData.kerning[prevCharCode]) - { - pos.x += charData.kerning[prevCharCode]; - } - - chars.push({texture:charData.texture, line: line, charCode: charCode, position: new PIXI.Point(pos.x + charData.xOffset, pos.y + charData.yOffset)}); - pos.x += charData.xAdvance; - - prevCharCode = charCode; - } - - lineWidths.push(pos.x); - maxLineWidth = Math.max(maxLineWidth, pos.x); - - var lineAlignOffsets = []; - - for(i = 0; i <= line; i++) - { - var alignOffset = 0; - if(this.style.align === 'right') - { - alignOffset = maxLineWidth - lineWidths[i]; - } - else if(this.style.align === 'center') - { - alignOffset = (maxLineWidth - lineWidths[i]) / 2; - } - lineAlignOffsets.push(alignOffset); - } - - var lenChildren = this.children.length; - var lenChars = chars.length; - var tint = this.tint || 0xFFFFFF; - - for(i = 0; i < lenChars; i++) - { - var c = i < lenChildren ? this.children[i] : this._pool.pop(); // get old child if have. if not - take from pool. - - if (c) c.setTexture(chars[i].texture); // check if got one before. - else c = new PIXI.Sprite(chars[i].texture); // if no create new one. - - c.position.x = (chars[i].position.x + lineAlignOffsets[chars[i].line]) * scale; - c.position.y = chars[i].position.y * scale; - c.scale.x = c.scale.y = scale; - c.tint = tint; - if (!c.parent) this.addChild(c); - } - - // remove unnecessary children. - // and put their into the pool. - while(this.children.length > lenChars) - { - var child = this.getChildAt(this.children.length - 1); - this._pool.push(child); - this.removeChild(child); - } - - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; -}; - -/** - * Updates the transform of this object - * - * @method updateTransform - * @private - */ -PIXI.BitmapText.prototype.updateTransform = function() -{ - if(this.dirty) - { - this.updateText(); - this.dirty = false; - } - - PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -}; - -PIXI.BitmapText.fonts = {}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * Holds all information related to an Interaction event - * - * @class InteractionData - * @constructor - */ -PIXI.InteractionData = function() -{ - /** - * This point stores the global coords of where the touch/mouse event happened - * - * @property global - * @type Point - */ - this.global = new PIXI.Point(); - - /** - * The target Sprite that was interacted with - * - * @property target - * @type Sprite - */ - this.target = null; - - /** - * When passed to an event handler, this will be the original DOM Event that was captured - * - * @property originalEvent - * @type Event - */ - this.originalEvent = null; -}; - -/** - * This will return the local coordinates of the specified displayObject for this InteractionData - * - * @method getLocalPosition - * @param displayObject {DisplayObject} The DisplayObject that you would like the local coords off - * @param [point] {Point} A Point object in which to store the value, optional (otherwise will create a new point) - * @return {Point} A point containing the coordinates of the InteractionData position relative to the DisplayObject - */ -PIXI.InteractionData.prototype.getLocalPosition = function(displayObject, point) -{ - var worldTransform = displayObject.worldTransform; - var global = this.global; - - // do a cheeky transform to get the mouse coords; - var a00 = worldTransform.a, a01 = worldTransform.c, a02 = worldTransform.tx, - a10 = worldTransform.b, a11 = worldTransform.d, a12 = worldTransform.ty, - id = 1 / (a00 * a11 + a01 * -a10); - - point = point || new PIXI.Point(); - - point.x = a11 * id * global.x + -a01 * id * global.y + (a12 * a01 - a02 * a11) * id; - point.y = a00 * id * global.y + -a10 * id * global.x + (-a12 * a00 + a02 * a10) * id; - - // set the mouse coords... - return point; -}; - -// constructor -PIXI.InteractionData.prototype.constructor = PIXI.InteractionData; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - - /** - * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive - * if its interactive parameter is set to true - * This manager also supports multitouch. - * - * @class InteractionManager - * @constructor - * @param stage {Stage} The stage to handle interactions - */ -PIXI.InteractionManager = function(stage) -{ - /** - * A reference to the stage - * - * @property stage - * @type Stage - */ - this.stage = stage; - - /** - * The mouse data - * - * @property mouse - * @type InteractionData - */ - this.mouse = new PIXI.InteractionData(); - - /** - * An object that stores current touches (InteractionData) by id reference - * - * @property touches - * @type Object - */ - this.touches = {}; - - /** - * @property tempPoint - * @type Point - * @private - */ - this.tempPoint = new PIXI.Point(); - - /** - * @property mouseoverEnabled - * @type Boolean - * @default - */ - this.mouseoverEnabled = true; - - /** - * Tiny little interactiveData pool ! - * - * @property pool - * @type Array - */ - this.pool = []; - - /** - * An array containing all the iterative items from the our interactive tree - * @property interactiveItems - * @type Array - * @private - */ - this.interactiveItems = []; - - /** - * Our canvas - * @property interactionDOMElement - * @type HTMLCanvasElement - * @private - */ - this.interactionDOMElement = null; - - //this will make it so that you don't have to call bind all the time - - /** - * @property onMouseMove - * @type Function - */ - this.onMouseMove = this.onMouseMove.bind( this ); - - /** - * @property onMouseDown - * @type Function - */ - this.onMouseDown = this.onMouseDown.bind(this); - - /** - * @property onMouseOut - * @type Function - */ - this.onMouseOut = this.onMouseOut.bind(this); - - /** - * @property onMouseUp - * @type Function - */ - this.onMouseUp = this.onMouseUp.bind(this); - - /** - * @property onTouchStart - * @type Function - */ - this.onTouchStart = this.onTouchStart.bind(this); - - /** - * @property onTouchEnd - * @type Function - */ - this.onTouchEnd = this.onTouchEnd.bind(this); - - /** - * @property onTouchMove - * @type Function - */ - this.onTouchMove = this.onTouchMove.bind(this); - - /** - * @property last - * @type Number - */ - this.last = 0; - - /** - * The css style of the cursor that is being used - * @property currentCursorStyle - * @type String - */ - this.currentCursorStyle = 'inherit'; - - /** - * Is set to true when the mouse is moved out of the canvas - * @property mouseOut - * @type Boolean - */ - this.mouseOut = false; - - /** - * @property resolution - * @type Number - */ - this.resolution = 1; - - // used for hit testing - this._tempPoint = new PIXI.Point(); -}; - -// constructor -PIXI.InteractionManager.prototype.constructor = PIXI.InteractionManager; - -/** - * Collects an interactive sprite recursively to have their interactions managed - * - * @method collectInteractiveSprite - * @param displayObject {DisplayObject} the displayObject to collect - * @param iParent {DisplayObject} the display object's parent - * @private - */ -PIXI.InteractionManager.prototype.collectInteractiveSprite = function(displayObject, iParent) -{ - var children = displayObject.children; - var length = children.length; - - // make an interaction tree... {item.__interactiveParent} - for (var i = length - 1; i >= 0; i--) - { - var child = children[i]; - - // push all interactive bits - if (child._interactive) - { - iParent.interactiveChildren = true; - //child.__iParent = iParent; - this.interactiveItems.push(child); - - if (child.children.length > 0) { - this.collectInteractiveSprite(child, child); - } - } - else - { - child.__iParent = null; - if (child.children.length > 0) - { - this.collectInteractiveSprite(child, iParent); - } - } - - } -}; - -/** - * Sets the target for event delegation - * - * @method setTarget - * @param target {WebGLRenderer|CanvasRenderer} the renderer to bind events to - * @private - */ -PIXI.InteractionManager.prototype.setTarget = function(target) -{ - this.target = target; - this.resolution = target.resolution; - - // Check if the dom element has been set. If it has don't do anything. - if (this.interactionDOMElement !== null) return; - - this.setTargetDomElement (target.view); -}; - -/** - * Sets the DOM element which will receive mouse/touch events. This is useful for when you have other DOM - * elements on top of the renderers Canvas element. With this you'll be able to delegate another DOM element - * to receive those events - * - * @method setTargetDomElement - * @param domElement {DOMElement} the DOM element which will receive mouse and touch events - * @private - */ -PIXI.InteractionManager.prototype.setTargetDomElement = function(domElement) -{ - this.removeEvents(); - - if (window.navigator.msPointerEnabled) - { - // time to remove some of that zoom in ja.. - domElement.style['-ms-content-zooming'] = 'none'; - domElement.style['-ms-touch-action'] = 'none'; - } - - this.interactionDOMElement = domElement; - - domElement.addEventListener('mousemove', this.onMouseMove, true); - domElement.addEventListener('mousedown', this.onMouseDown, true); - domElement.addEventListener('mouseout', this.onMouseOut, true); - - // aint no multi touch just yet! - domElement.addEventListener('touchstart', this.onTouchStart, true); - domElement.addEventListener('touchend', this.onTouchEnd, true); - domElement.addEventListener('touchmove', this.onTouchMove, true); - - window.addEventListener('mouseup', this.onMouseUp, true); -}; - -/** - * @method removeEvents - * @private - */ -PIXI.InteractionManager.prototype.removeEvents = function() -{ - if (!this.interactionDOMElement) return; - - this.interactionDOMElement.style['-ms-content-zooming'] = ''; - this.interactionDOMElement.style['-ms-touch-action'] = ''; - - this.interactionDOMElement.removeEventListener('mousemove', this.onMouseMove, true); - this.interactionDOMElement.removeEventListener('mousedown', this.onMouseDown, true); - this.interactionDOMElement.removeEventListener('mouseout', this.onMouseOut, true); - - // aint no multi touch just yet! - this.interactionDOMElement.removeEventListener('touchstart', this.onTouchStart, true); - this.interactionDOMElement.removeEventListener('touchend', this.onTouchEnd, true); - this.interactionDOMElement.removeEventListener('touchmove', this.onTouchMove, true); - - this.interactionDOMElement = null; - - window.removeEventListener('mouseup', this.onMouseUp, true); -}; - -/** - * updates the state of interactive objects - * - * @method update - * @private - */ -PIXI.InteractionManager.prototype.update = function() -{ - if (!this.target) return; - - // frequency of 30fps?? - var now = Date.now(); - var diff = now - this.last; - diff = (diff * PIXI.INTERACTION_FREQUENCY ) / 1000; - if (diff < 1) return; - this.last = now; - - var i = 0; - - // ok.. so mouse events?? - // yes for now :) - // OPTIMISE - how often to check?? - if (this.dirty) - { - this.rebuildInteractiveGraph(); - } - - // loop through interactive objects! - var length = this.interactiveItems.length; - var cursor = 'inherit'; - var over = false; - - for (i = 0; i < length; i++) - { - var item = this.interactiveItems[i]; - - // OPTIMISATION - only calculate every time if the mousemove function exists.. - // OK so.. does the object have any other interactive functions? - // hit-test the clip! - // if (item.mouseover || item.mouseout || item.buttonMode) - // { - // ok so there are some functions so lets hit test it.. - item.__hit = this.hitTest(item, this.mouse); - this.mouse.target = item; - // ok so deal with interactions.. - // looks like there was a hit! - if (item.__hit && !over) - { - if (item.buttonMode) cursor = item.defaultCursor; - - if (!item.interactiveChildren) - { - over = true; - } - - if (!item.__isOver) - { - if (item.mouseover) - { - item.mouseover (this.mouse); - } - item.__isOver = true; - } - } - else - { - if (item.__isOver) - { - // roll out! - if (item.mouseout) - { - item.mouseout (this.mouse); - } - item.__isOver = false; - } - } - } - - if (this.currentCursorStyle !== cursor) - { - this.currentCursorStyle = cursor; - this.interactionDOMElement.style.cursor = cursor; - } -}; - -/** - * @method rebuildInteractiveGraph - * @private - */ -PIXI.InteractionManager.prototype.rebuildInteractiveGraph = function() -{ - this.dirty = false; - - var len = this.interactiveItems.length; - - for (var i = 0; i < len; i++) { - this.interactiveItems[i].interactiveChildren = false; - } - - this.interactiveItems = []; - - if (this.stage.interactive) - { - this.interactiveItems.push(this.stage); - } - - // Go through and collect all the objects that are interactive.. - this.collectInteractiveSprite(this.stage, this.stage); -}; - -/** - * Is called when the mouse moves across the renderer element - * - * @method onMouseMove - * @param event {Event} The DOM event of the mouse moving - * @private - */ -PIXI.InteractionManager.prototype.onMouseMove = function(event) -{ - if (this.dirty) - { - this.rebuildInteractiveGraph(); - } - - this.mouse.originalEvent = event; - - // TODO optimize by not check EVERY TIME! maybe half as often? // - var rect = this.interactionDOMElement.getBoundingClientRect(); - - this.mouse.global.x = (event.clientX - rect.left) * (this.target.width / rect.width) / this.resolution; - this.mouse.global.y = (event.clientY - rect.top) * ( this.target.height / rect.height) / this.resolution; - - var length = this.interactiveItems.length; - - for (var i = 0; i < length; i++) - { - var item = this.interactiveItems[i]; - - // Call the function! - if (item.mousemove) - { - item.mousemove(this.mouse); - } - } -}; - -/** - * Is called when the mouse button is pressed down on the renderer element - * - * @method onMouseDown - * @param event {Event} The DOM event of a mouse button being pressed down - * @private - */ -PIXI.InteractionManager.prototype.onMouseDown = function(event) -{ - if (this.dirty) - { - this.rebuildInteractiveGraph(); - } - - this.mouse.originalEvent = event; - - if (PIXI.AUTO_PREVENT_DEFAULT) - { - this.mouse.originalEvent.preventDefault(); - } - - // loop through interaction tree... - // hit test each item! -> - // get interactive items under point?? - //stage.__i - var length = this.interactiveItems.length; - - var e = this.mouse.originalEvent; - var isRightButton = e.button === 2 || e.which === 3; - var downFunction = isRightButton ? 'rightdown' : 'mousedown'; - var clickFunction = isRightButton ? 'rightclick' : 'click'; - var buttonIsDown = isRightButton ? '__rightIsDown' : '__mouseIsDown'; - var isDown = isRightButton ? '__isRightDown' : '__isDown'; - - // while - // hit test - for (var i = 0; i < length; i++) - { - var item = this.interactiveItems[i]; - - if (item[downFunction] || item[clickFunction]) - { - item[buttonIsDown] = true; - item.__hit = this.hitTest(item, this.mouse); - - if (item.__hit) - { - //call the function! - if (item[downFunction]) - { - item[downFunction](this.mouse); - } - item[isDown] = true; - - // just the one! - if (!item.interactiveChildren) break; - } - } - } -}; - -/** - * Is called when the mouse is moved out of the renderer element - * - * @method onMouseOut - * @param event {Event} The DOM event of a mouse being moved out - * @private - */ -PIXI.InteractionManager.prototype.onMouseOut = function(event) -{ - if (this.dirty) - { - this.rebuildInteractiveGraph(); - } - - this.mouse.originalEvent = event; - - var length = this.interactiveItems.length; - - this.interactionDOMElement.style.cursor = 'inherit'; - - for (var i = 0; i < length; i++) - { - var item = this.interactiveItems[i]; - if (item.__isOver) - { - this.mouse.target = item; - if (item.mouseout) - { - item.mouseout(this.mouse); - } - item.__isOver = false; - } - } - - this.mouseOut = true; - - // move the mouse to an impossible position - this.mouse.global.x = -10000; - this.mouse.global.y = -10000; -}; - -/** - * Is called when the mouse button is released on the renderer element - * - * @method onMouseUp - * @param event {Event} The DOM event of a mouse button being released - * @private - */ -PIXI.InteractionManager.prototype.onMouseUp = function(event) -{ - if (this.dirty) - { - this.rebuildInteractiveGraph(); - } - - this.mouse.originalEvent = event; - - var length = this.interactiveItems.length; - var up = false; - - var e = this.mouse.originalEvent; - var isRightButton = e.button === 2 || e.which === 3; - - var upFunction = isRightButton ? 'rightup' : 'mouseup'; - var clickFunction = isRightButton ? 'rightclick' : 'click'; - var upOutsideFunction = isRightButton ? 'rightupoutside' : 'mouseupoutside'; - var isDown = isRightButton ? '__isRightDown' : '__isDown'; - - for (var i = 0; i < length; i++) - { - var item = this.interactiveItems[i]; - - if (item[clickFunction] || item[upFunction] || item[upOutsideFunction]) - { - item.__hit = this.hitTest(item, this.mouse); - - if (item.__hit && !up) - { - //call the function! - if (item[upFunction]) - { - item[upFunction](this.mouse); - } - if (item[isDown]) - { - if (item[clickFunction]) - { - item[clickFunction](this.mouse); - } - } - - if (!item.interactiveChildren) - { - up = true; - } - } - else - { - if (item[isDown]) - { - if (item[upOutsideFunction]) item[upOutsideFunction](this.mouse); - } - } - - item[isDown] = false; - } - } -}; - -/** - * Tests if the current mouse coordinates hit a sprite - * - * @method hitTest - * @param item {DisplayObject} The displayObject to test for a hit - * @param interactionData {InteractionData} The interactionData object to update in the case there is a hit - * @private - */ -PIXI.InteractionManager.prototype.hitTest = function(item, interactionData) -{ - var global = interactionData.global; - - if (!item.worldVisible) - { - return false; - } - - // map the global point to local space. - item.worldTransform.applyInverse(global, this._tempPoint); - - var x = this._tempPoint.x, - y = this._tempPoint.y, - i; - - interactionData.target = item; - - //a sprite or display object with a hit area defined - if (item.hitArea && item.hitArea.contains) - { - return item.hitArea.contains(x, y); - } - // a sprite with no hitarea defined - else if(item instanceof PIXI.Sprite) - { - var width = item.texture.frame.width; - var height = item.texture.frame.height; - var x1 = -width * item.anchor.x; - var y1; - - if (x > x1 && x < x1 + width) - { - y1 = -height * item.anchor.y; - - if (y > y1 && y < y1 + height) - { - // set the target property if a hit is true! - return true; - } - } - } - else if(item instanceof PIXI.Graphics) - { - var graphicsData = item.graphicsData; - for (i = 0; i < graphicsData.length; i++) - { - var data = graphicsData[i]; - if(!data.fill)continue; - - // only deal with fills.. - if(data.shape) - { - if(data.shape.contains(x, y)) - { - //interactionData.target = item; - return true; - } - } - } - } - - var length = item.children.length; - - for (i = 0; i < length; i++) - { - var tempItem = item.children[i]; - var hit = this.hitTest(tempItem, interactionData); - if (hit) - { - // hmm.. TODO SET CORRECT TARGET? - interactionData.target = item; - return true; - } - } - return false; -}; - -/** - * Is called when a touch is moved across the renderer element - * - * @method onTouchMove - * @param event {Event} The DOM event of a touch moving across the renderer view - * @private - */ -PIXI.InteractionManager.prototype.onTouchMove = function(event) -{ - if (this.dirty) - { - this.rebuildInteractiveGraph(); - } - - var rect = this.interactionDOMElement.getBoundingClientRect(); - var changedTouches = event.changedTouches; - var touchData; - var i = 0; - - for (i = 0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - touchData = this.touches[touchEvent.identifier]; - touchData.originalEvent = event; - - // update the touch position - touchData.global.x = ( (touchEvent.clientX - rect.left) * (this.target.width / rect.width) ) / this.resolution; - touchData.global.y = ( (touchEvent.clientY - rect.top) * (this.target.height / rect.height) ) / this.resolution; - if (navigator.isCocoonJS && !rect.left && !rect.top && !event.target.style.width && !event.target.style.height) - { - //Support for CocoonJS fullscreen scale modes - touchData.global.x = touchEvent.clientX; - touchData.global.y = touchEvent.clientY; - } - - for (var j = 0; j < this.interactiveItems.length; j++) - { - var item = this.interactiveItems[j]; - if (item.touchmove && item.__touchData && item.__touchData[touchEvent.identifier]) - { - item.touchmove(touchData); - } - } - } -}; - -/** - * Is called when a touch is started on the renderer element - * - * @method onTouchStart - * @param event {Event} The DOM event of a touch starting on the renderer view - * @private - */ -PIXI.InteractionManager.prototype.onTouchStart = function(event) -{ - if (this.dirty) - { - this.rebuildInteractiveGraph(); - } - - var rect = this.interactionDOMElement.getBoundingClientRect(); - - if (PIXI.AUTO_PREVENT_DEFAULT) - { - event.preventDefault(); - } - - var changedTouches = event.changedTouches; - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - - var touchData = this.pool.pop(); - if (!touchData) - { - touchData = new PIXI.InteractionData(); - } - - touchData.originalEvent = event; - - this.touches[touchEvent.identifier] = touchData; - touchData.global.x = ( (touchEvent.clientX - rect.left) * (this.target.width / rect.width) ) / this.resolution; - touchData.global.y = ( (touchEvent.clientY - rect.top) * (this.target.height / rect.height) ) / this.resolution; - if (navigator.isCocoonJS && !rect.left && !rect.top && !event.target.style.width && !event.target.style.height) - { - //Support for CocoonJS fullscreen scale modes - touchData.global.x = touchEvent.clientX; - touchData.global.y = touchEvent.clientY; - } - - var length = this.interactiveItems.length; - - for (var j = 0; j < length; j++) - { - var item = this.interactiveItems[j]; - - if (item.touchstart || item.tap) - { - item.__hit = this.hitTest(item, touchData); - - if (item.__hit) - { - //call the function! - if (item.touchstart)item.touchstart(touchData); - item.__isDown = true; - item.__touchData = item.__touchData || {}; - item.__touchData[touchEvent.identifier] = touchData; - - if (!item.interactiveChildren) break; - } - } - } - } -}; - -/** - * Is called when a touch is ended on the renderer element - * - * @method onTouchEnd - * @param event {Event} The DOM event of a touch ending on the renderer view - * @private - */ -PIXI.InteractionManager.prototype.onTouchEnd = function(event) -{ - if (this.dirty) - { - this.rebuildInteractiveGraph(); - } - - var rect = this.interactionDOMElement.getBoundingClientRect(); - var changedTouches = event.changedTouches; - - for (var i=0; i < changedTouches.length; i++) - { - var touchEvent = changedTouches[i]; - var touchData = this.touches[touchEvent.identifier]; - var up = false; - touchData.global.x = ( (touchEvent.clientX - rect.left) * (this.target.width / rect.width) ) / this.resolution; - touchData.global.y = ( (touchEvent.clientY - rect.top) * (this.target.height / rect.height) ) / this.resolution; - if (navigator.isCocoonJS && !rect.left && !rect.top && !event.target.style.width && !event.target.style.height) - { - //Support for CocoonJS fullscreen scale modes - touchData.global.x = touchEvent.clientX; - touchData.global.y = touchEvent.clientY; - } - - var length = this.interactiveItems.length; - for (var j = 0; j < length; j++) - { - var item = this.interactiveItems[j]; - - if (item.__touchData && item.__touchData[touchEvent.identifier]) - { - - item.__hit = this.hitTest(item, item.__touchData[touchEvent.identifier]); - - // so this one WAS down... - touchData.originalEvent = event; - // hitTest?? - - if (item.touchend || item.tap) - { - if (item.__hit && !up) - { - if (item.touchend) - { - item.touchend(touchData); - } - if (item.__isDown && item.tap) - { - item.tap(touchData); - } - if (!item.interactiveChildren) - { - up = true; - } - } - else - { - if (item.__isDown && item.touchendoutside) - { - item.touchendoutside(touchData); - } - } - - item.__isDown = false; - } - - item.__touchData[touchEvent.identifier] = null; - } - } - // remove the touch.. - this.pool.push(touchData); - this.touches[touchEvent.identifier] = null; - } -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * A Stage represents the root of the display tree. Everything connected to the stage is rendered - * - * @class Stage - * @extends DisplayObjectContainer - * @constructor - * @param backgroundColor {Number} the background color of the stage, you have to pass this in is in hex format - * like: 0xFFFFFF for white - * - * Creating a stage is a mandatory process when you use Pixi, which is as simple as this : - * var stage = new PIXI.Stage(0xFFFFFF); - * where the parameter given is the background colour of the stage, in hex - * you will use this stage instance to add your sprites to it and therefore to the renderer - * Here is how to add a sprite to the stage : - * stage.addChild(sprite); - */ -PIXI.Stage = function(backgroundColor) -{ - PIXI.DisplayObjectContainer.call( this ); - - /** - * [read-only] Current transform of the object based on world (parent) factors - * - * @property worldTransform - * @type Matrix - * @readOnly - * @private - */ - this.worldTransform = new PIXI.Matrix(); - - /** - * Whether or not the stage is interactive - * - * @property interactive - * @type Boolean - */ - this.interactive = true; - - /** - * The interaction manage for this stage, manages all interactive activity on the stage - * - * @property interactionManager - * @type InteractionManager - */ - this.interactionManager = new PIXI.InteractionManager(this); - - /** - * Whether the stage is dirty and needs to have interactions updated - * - * @property dirty - * @type Boolean - * @private - */ - this.dirty = true; - - //the stage is its own stage - this.stage = this; - - //optimize hit detection a bit - this.stage.hitArea = new PIXI.Rectangle(0, 0, 100000, 100000); - - this.setBackgroundColor(backgroundColor); -}; - -// constructor -PIXI.Stage.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Stage.prototype.constructor = PIXI.Stage; - -/** - * Sets another DOM element which can receive mouse/touch interactions instead of the default Canvas element. - * This is useful for when you have other DOM elements on top of the Canvas element. - * - * @method setInteractionDelegate - * @param domElement {DOMElement} This new domElement which will receive mouse/touch events - */ -PIXI.Stage.prototype.setInteractionDelegate = function(domElement) -{ - this.interactionManager.setTargetDomElement( domElement ); -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -PIXI.Stage.prototype.updateTransform = function() -{ - this.worldAlpha = 1; - - for(var i=0,j=this.children.length; i> 16 & 0xFF) / 255, ( hex >> 8 & 0xFF) / 255, (hex & 0xFF)/ 255]; -}; - -/** - * Converts a color as an [R, G, B] array to a hex number - * - * @method rgb2hex - * @param rgb {Array} - */ -PIXI.rgb2hex = function(rgb) { - return ((rgb[0]*255 << 16) + (rgb[1]*255 << 8) + rgb[2]*255); -}; - -/** - * A polyfill for Function.prototype.bind - * - * @method bind - */ -if (typeof Function.prototype.bind !== 'function') { - Function.prototype.bind = (function () { - return function (thisArg) { - var target = this, i = arguments.length - 1, boundArgs = []; - if (i > 0) - { - boundArgs.length = i; - while (i--) boundArgs[i] = arguments[i + 1]; - } - - if (typeof target !== 'function') throw new TypeError(); - - function bound() { - var i = arguments.length, args = new Array(i); - while (i--) args[i] = arguments[i]; - args = boundArgs.concat(args); - return target.apply(this instanceof bound ? this : thisArg, args); - } - - bound.prototype = (function F(proto) { - if (proto) F.prototype = proto; - if (!(this instanceof F)) return new F(); - })(target.prototype); - - return bound; - }; - })(); -} - -/** - * A wrapper for ajax requests to be handled cross browser - * - * @class AjaxRequest - * @constructor - */ -PIXI.AjaxRequest = function() -{ - var activexmodes = ['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.3.0', 'Microsoft.XMLHTTP']; //activeX versions to check for in IE - - if (window.ActiveXObject) - { //Test for support for ActiveXObject in IE first (as XMLHttpRequest in IE7 is broken) - for (var i=0; i 0 && (number & (number - 1)) === 0) // see: http://goo.gl/D9kPj - return number; - else - { - var result = 1; - while (result < number) result <<= 1; - return result; - } -}; - -PIXI.isPowerOfTwo = function(width, height) -{ - return (width > 0 && (width & (width - 1)) === 0 && height > 0 && (height & (height - 1)) === 0); - -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - * @author Chad Engler https://github.com/englercj @Rolnaaba - */ - -/** - * Originally based on https://github.com/mrdoob/eventtarget.js/ from mr Doob. - * Currently takes inspiration from the nodejs EventEmitter, EventEmitter3, and smokesignals - */ - -/** - * Mixins event emitter functionality to a class - * - * @class EventTarget - * @example - * function MyEmitter() {} - * - * PIXI.EventTarget.mixin(MyEmitter.prototype); - * - * var em = new MyEmitter(); - * em.emit('eventName', 'some data', 'some more data', {}, null, ...); - */ -PIXI.EventTarget = { - /** - * Backward compat from when this used to be a function - */ - call: function callCompat(obj) { - if(obj) { - obj = obj.prototype || obj; - PIXI.EventTarget.mixin(obj); - } - }, - - /** - * Mixes in the properties of the EventTarget prototype onto another object - * - * @method mixin - * @param object {Object} The obj to mix into - */ - mixin: function mixin(obj) { - /** - * Return a list of assigned event listeners. - * - * @method listeners - * @param eventName {String} The events that should be listed. - * @return {Array} An array of listener functions - */ - obj.listeners = function listeners(eventName) { - this._listeners = this._listeners || {}; - - return this._listeners[eventName] ? this._listeners[eventName].slice() : []; - }; - - /** - * Emit an event to all registered event listeners. - * - * @method emit - * @alias dispatchEvent - * @param eventName {String} The name of the event. - * @return {Boolean} Indication if we've emitted an event. - */ - obj.emit = obj.dispatchEvent = function emit(eventName, data) { - this._listeners = this._listeners || {}; - - //backwards compat with old method ".emit({ type: 'something' })" - if(typeof eventName === 'object') { - data = eventName; - eventName = eventName.type; - } - - //ensure we are using a real pixi event - if(!data || data.__isEventObject !== true) { - data = new PIXI.Event(this, eventName, data); - } - - //iterate the listeners - if(this._listeners && this._listeners[eventName]) { - var listeners = this._listeners[eventName].slice(0), - length = listeners.length, - fn = listeners[0], - i; - - for(i = 0; i < length; fn = listeners[++i]) { - //call the event listener - fn.call(this, data); - - //if "stopImmediatePropagation" is called, stop calling sibling events - if(data.stoppedImmediate) { - return this; - } - } - - //if "stopPropagation" is called then don't bubble the event - if(data.stopped) { - return this; - } - } - - //bubble this event up the scene graph - if(this.parent && this.parent.emit) { - this.parent.emit.call(this.parent, eventName, data); - } - - return this; - }; - - /** - * Register a new EventListener for the given event. - * - * @method on - * @alias addEventListener - * @param eventName {String} Name of the event. - * @param callback {Functon} fn Callback function. - */ - obj.on = obj.addEventListener = function on(eventName, fn) { - this._listeners = this._listeners || {}; - - (this._listeners[eventName] = this._listeners[eventName] || []) - .push(fn); - - return this; - }; - - /** - * Add an EventListener that's only called once. - * - * @method once - * @param eventName {String} Name of the event. - * @param callback {Function} Callback function. - */ - obj.once = function once(eventName, fn) { - this._listeners = this._listeners || {}; - - var self = this; - function onceHandlerWrapper() { - fn.apply(self.off(eventName, onceHandlerWrapper), arguments); - } - onceHandlerWrapper._originalHandler = fn; - - return this.on(eventName, onceHandlerWrapper); - }; - - /** - * Remove event listeners. - * - * @method off - * @alias removeEventListener - * @param eventName {String} The event we want to remove. - * @param callback {Function} The listener that we need to find. - */ - obj.off = obj.removeEventListener = function off(eventName, fn) { - this._listeners = this._listeners || {}; - - if(!this._listeners[eventName]) - return this; - - var list = this._listeners[eventName], - i = fn ? list.length : 0; - - while(i-- > 0) { - if(list[i] === fn || list[i]._originalHandler === fn) { - list.splice(i, 1); - } - } - - if(list.length === 0) { - delete this._listeners[eventName]; - } - - return this; - }; - - /** - * Remove all listeners or only the listeners for the specified event. - * - * @method removeAllListeners - * @param eventName {String} The event you want to remove all listeners for. - */ - obj.removeAllListeners = function removeAllListeners(eventName) { - this._listeners = this._listeners || {}; - - if(!this._listeners[eventName]) - return this; - - delete this._listeners[eventName]; - - return this; - }; - } -}; - -/** - * Creates an homogenous object for tracking events so users can know what to expect. - * - * @class Event - * @extends Object - * @constructor - * @param target {Object} The target object that the event is called on - * @param name {String} The string name of the event that was triggered - * @param data {Object} Arbitrary event data to pass along - */ -PIXI.Event = function(target, name, data) { - //for duck typing in the ".on()" function - this.__isEventObject = true; - - /** - * Tracks the state of bubbling propagation. Do not - * set this directly, instead use `event.stopPropagation()` - * - * @property stopped - * @type Boolean - * @private - * @readOnly - */ - this.stopped = false; - - /** - * Tracks the state of sibling listener propagation. Do not - * set this directly, instead use `event.stopImmediatePropagation()` - * - * @property stoppedImmediate - * @type Boolean - * @private - * @readOnly - */ - this.stoppedImmediate = false; - - /** - * The original target the event triggered on. - * - * @property target - * @type Object - * @readOnly - */ - this.target = target; - - /** - * The string name of the event that this represents. - * - * @property type - * @type String - * @readOnly - */ - this.type = name; - - /** - * The data that was passed in with this event. - * - * @property data - * @type Object - * @readOnly - */ - this.data = data; - - //backwards compat with older version of events - this.content = data; - - /** - * The timestamp when the event occurred. - * - * @property timeStamp - * @type Number - * @readOnly - */ - this.timeStamp = Date.now(); -}; - -/** - * Stops the propagation of events up the scene graph (prevents bubbling). - * - * @method stopPropagation - */ -PIXI.Event.prototype.stopPropagation = function stopPropagation() { - this.stopped = true; -}; - -/** - * Stops the propagation of events to sibling listeners (no longer calls any listeners). - * - * @method stopImmediatePropagation - */ -PIXI.Event.prototype.stopImmediatePropagation = function stopImmediatePropagation() { - this.stoppedImmediate = true; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * This helper function will automatically detect which renderer you should be using. - * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by - * the browser then this function will return a canvas renderer - * - * @method autoDetectRenderer - * @for PIXI - * @static - * @param width=800 {Number} the width of the renderers view - * @param height=600 {Number} the height of the renderers view - * - * @param [options] {Object} The optional renderer parameters - * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional - * @param [options.transparent=false] {Boolean} If the render view is transparent, default false - * @param [options.antialias=false] {Boolean} sets antialias (only applicable in chrome at the moment) - * @param [options.preserveDrawingBuffer=false] {Boolean} enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context - * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 - * - */ -PIXI.autoDetectRenderer = function(width, height, options) -{ - if(!width)width = 800; - if(!height)height = 600; - - // BORROWED from Mr Doob (mrdoob.com) - var webgl = ( function () { try { - var canvas = document.createElement( 'canvas' ); - return !! window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ); - } catch( e ) { - return false; - } - } )(); - - if( webgl ) - { - return new PIXI.WebGLRenderer(width, height, options); - } - - return new PIXI.CanvasRenderer(width, height, options); -}; - -/** - * This helper function will automatically detect which renderer you should be using. - * This function is very similar to the autoDetectRenderer function except that is will return a canvas renderer for android. - * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. - * This function will likely change and update as webGL performance improves on these devices. - * - * @method autoDetectRecommendedRenderer - * @for PIXI - * @static - * @param width=800 {Number} the width of the renderers view - * @param height=600 {Number} the height of the renderers view - * - * @param [options] {Object} The optional renderer parameters - * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional - * @param [options.transparent=false] {Boolean} If the render view is transparent, default false - * @param [options.antialias=false] {Boolean} sets antialias (only applicable in chrome at the moment) - * @param [options.preserveDrawingBuffer=false] {Boolean} enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context - * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 - * - */ -PIXI.autoDetectRecommendedRenderer = function(width, height, options) -{ - if(!width)width = 800; - if(!height)height = 600; - - // BORROWED from Mr Doob (mrdoob.com) - var webgl = ( function () { try { - var canvas = document.createElement( 'canvas' ); - return !! window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ); - } catch( e ) { - return false; - } - } )(); - - var isAndroid = /Android/i.test(navigator.userAgent); - - if( webgl && !isAndroid) - { - return new PIXI.WebGLRenderer(width, height, options); - } - - return new PIXI.CanvasRenderer(width, height, options); -}; - -/* - PolyK library - url: http://polyk.ivank.net - Released under MIT licence. - - Copyright (c) 2012 Ivan Kuckir - - 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. - - This is an amazing lib! - - Slightly modified by Mat Groves (matgroves.com); -*/ - -/** - * Based on the Polyk library http://polyk.ivank.net released under MIT licence. - * This is an amazing lib! - * Slightly modified by Mat Groves (matgroves.com); - * @class PolyK - */ -PIXI.PolyK = {}; - -/** - * Triangulates shapes for webGL graphic fills. - * - * @method Triangulate - */ -PIXI.PolyK.Triangulate = function(p) -{ - var sign = true; - - var n = p.length >> 1; - if(n < 3) return []; - - var tgs = []; - var avl = []; - for(var i = 0; i < n; i++) avl.push(i); - - i = 0; - var al = n; - while(al > 3) - { - var i0 = avl[(i+0)%al]; - var i1 = avl[(i+1)%al]; - var i2 = avl[(i+2)%al]; - - var ax = p[2*i0], ay = p[2*i0+1]; - var bx = p[2*i1], by = p[2*i1+1]; - var cx = p[2*i2], cy = p[2*i2+1]; - - var earFound = false; - if(PIXI.PolyK._convex(ax, ay, bx, by, cx, cy, sign)) - { - earFound = true; - for(var j = 0; j < al; j++) - { - var vi = avl[j]; - if(vi === i0 || vi === i1 || vi === i2) continue; - - if(PIXI.PolyK._PointInTriangle(p[2*vi], p[2*vi+1], ax, ay, bx, by, cx, cy)) { - earFound = false; - break; - } - } - } - - if(earFound) - { - tgs.push(i0, i1, i2); - avl.splice((i+1)%al, 1); - al--; - i = 0; - } - else if(i++ > 3*al) - { - // need to flip flip reverse it! - // reset! - if(sign) - { - tgs = []; - avl = []; - for(i = 0; i < n; i++) avl.push(i); - - i = 0; - al = n; - - sign = false; - } - else - { - // window.console.log("PIXI Warning: shape too complex to fill"); - return null; - } - } - } - - tgs.push(avl[0], avl[1], avl[2]); - return tgs; -}; - -/** - * Checks whether a point is within a triangle - * - * @method _PointInTriangle - * @param px {Number} x coordinate of the point to test - * @param py {Number} y coordinate of the point to test - * @param ax {Number} x coordinate of the a point of the triangle - * @param ay {Number} y coordinate of the a point of the triangle - * @param bx {Number} x coordinate of the b point of the triangle - * @param by {Number} y coordinate of the b point of the triangle - * @param cx {Number} x coordinate of the c point of the triangle - * @param cy {Number} y coordinate of the c point of the triangle - * @private - * @return {Boolean} - */ -PIXI.PolyK._PointInTriangle = function(px, py, ax, ay, bx, by, cx, cy) -{ - var v0x = cx-ax; - var v0y = cy-ay; - var v1x = bx-ax; - var v1y = by-ay; - var v2x = px-ax; - var v2y = py-ay; - - var dot00 = v0x*v0x+v0y*v0y; - var dot01 = v0x*v1x+v0y*v1y; - var dot02 = v0x*v2x+v0y*v2y; - var dot11 = v1x*v1x+v1y*v1y; - var dot12 = v1x*v2x+v1y*v2y; - - var invDenom = 1 / (dot00 * dot11 - dot01 * dot01); - var u = (dot11 * dot02 - dot01 * dot12) * invDenom; - var v = (dot00 * dot12 - dot01 * dot02) * invDenom; - - // Check if point is in triangle - return (u >= 0) && (v >= 0) && (u + v < 1); -}; - -/** - * Checks whether a shape is convex - * - * @method _convex - * @private - * @return {Boolean} - */ -PIXI.PolyK._convex = function(ax, ay, bx, by, cx, cy, sign) -{ - return ((ay-by)*(cx-bx) + (bx-ax)*(cy-by) >= 0) === sign; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** -* @method initDefaultShaders -* @static -* @private -*/ -PIXI.initDefaultShaders = function() -{ -}; - -/** -* @method CompileVertexShader -* @static -* @param gl {WebGLContext} the current WebGL drawing context -* @param shaderSrc {Array} -* @return {Any} -*/ -PIXI.CompileVertexShader = function(gl, shaderSrc) -{ - return PIXI._CompileShader(gl, shaderSrc, gl.VERTEX_SHADER); -}; - -/** -* @method CompileFragmentShader -* @static -* @param gl {WebGLContext} the current WebGL drawing context -* @param shaderSrc {Array} -* @return {Any} -*/ -PIXI.CompileFragmentShader = function(gl, shaderSrc) -{ - return PIXI._CompileShader(gl, shaderSrc, gl.FRAGMENT_SHADER); -}; - -/** -* @method _CompileShader -* @static -* @private -* @param gl {WebGLContext} the current WebGL drawing context -* @param shaderSrc {Array} -* @param shaderType {Number} -* @return {Any} -*/ -PIXI._CompileShader = function(gl, shaderSrc, shaderType) -{ - var src = shaderSrc.join("\n"); - var shader = gl.createShader(shaderType); - gl.shaderSource(shader, src); - gl.compileShader(shader); - - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) - { - window.console.log(gl.getShaderInfoLog(shader)); - return null; - } - - return shader; -}; - -/** -* @method compileProgram -* @static -* @param gl {WebGLContext} the current WebGL drawing context -* @param vertexSrc {Array} -* @param fragmentSrc {Array} -* @return {Any} -*/ -PIXI.compileProgram = function(gl, vertexSrc, fragmentSrc) -{ - var fragmentShader = PIXI.CompileFragmentShader(gl, fragmentSrc); - var vertexShader = PIXI.CompileVertexShader(gl, vertexSrc); - - var shaderProgram = gl.createProgram(); - - gl.attachShader(shaderProgram, vertexShader); - gl.attachShader(shaderProgram, fragmentShader); - gl.linkProgram(shaderProgram); - - if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) - { - window.console.log("Could not initialise shaders"); - } - - return shaderProgram; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - * @author Richard Davey http://www.photonstorm.com @photonstorm - */ - -/** -* @class PixiShader -* @constructor -* @param gl {WebGLContext} the current WebGL drawing context -*/ -PIXI.PixiShader = function(gl) -{ - /** - * @property _UID - * @type Number - * @private - */ - this._UID = PIXI._UID++; - - /** - * @property gl - * @type WebGLContext - */ - this.gl = gl; - - /** - * The WebGL program. - * @property program - * @type Any - */ - this.program = null; - - /** - * The fragment shader. - * @property fragmentSrc - * @type Array - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @property textureCount - * @type Number - */ - this.textureCount = 0; - - /** - * A local flag - * @property firstRun - * @type Boolean - * @private - */ - this.firstRun = true; - - /** - * A dirty flag - * @property dirty - * @type Boolean - */ - this.dirty = true; - - /** - * Uniform attributes cache. - * @property attributes - * @type Array - * @private - */ - this.attributes = []; - - this.init(); -}; - -PIXI.PixiShader.prototype.constructor = PIXI.PixiShader; - -/** -* Initialises the shader. -* -* @method init -*/ -PIXI.PixiShader.prototype.init = function() -{ - var gl = this.gl; - - var program = PIXI.compileProgram(gl, this.vertexSrc || PIXI.PixiShader.defaultVertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its something to do with the current state of the gl context. - // I'm convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if(this.colorAttribute === -1) - { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - // add those custom shaders! - for (var key in this.uniforms) - { - // get the uniform locations.. - this.uniforms[key].uniformLocation = gl.getUniformLocation(program, key); - } - - this.initUniforms(); - - this.program = program; -}; - -/** -* Initialises the shader uniform values. -* -* Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/ -* http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf -* -* @method initUniforms -*/ -PIXI.PixiShader.prototype.initUniforms = function() -{ - this.textureCount = 1; - var gl = this.gl; - var uniform; - - for (var key in this.uniforms) - { - uniform = this.uniforms[key]; - - var type = uniform.type; - - if (type === 'sampler2D') - { - uniform._init = false; - - if (uniform.value !== null) - { - this.initSampler2D(uniform); - } - } - else if (type === 'mat2' || type === 'mat3' || type === 'mat4') - { - // These require special handling - uniform.glMatrix = true; - uniform.glValueLength = 1; - - if (type === 'mat2') - { - uniform.glFunc = gl.uniformMatrix2fv; - } - else if (type === 'mat3') - { - uniform.glFunc = gl.uniformMatrix3fv; - } - else if (type === 'mat4') - { - uniform.glFunc = gl.uniformMatrix4fv; - } - } - else - { - // GL function reference - uniform.glFunc = gl['uniform' + type]; - - if (type === '2f' || type === '2i') - { - uniform.glValueLength = 2; - } - else if (type === '3f' || type === '3i') - { - uniform.glValueLength = 3; - } - else if (type === '4f' || type === '4i') - { - uniform.glValueLength = 4; - } - else - { - uniform.glValueLength = 1; - } - } - } - -}; - -/** -* Initialises a Sampler2D uniform (which may only be available later on after initUniforms once the texture has loaded) -* -* @method initSampler2D -*/ -PIXI.PixiShader.prototype.initSampler2D = function(uniform) -{ - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - return; - } - - var gl = this.gl; - - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // Extended texture data - if (uniform.textureData) - { - var data = uniform.textureData; - - // GLTexture = mag linear, min linear_mipmap_linear, wrap repeat + gl.generateMipmap(gl.TEXTURE_2D); - // GLTextureLinear = mag/min linear, wrap clamp - // GLTextureNearestRepeat = mag/min NEAREST, wrap repeat - // GLTextureNearest = mag/min nearest, wrap clamp - // AudioTexture = whatever + luminance + width 512, height 2, border 0 - // KeyTexture = whatever + luminance + width 256, height 2, border 0 - - // magFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST - // wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT - - var magFilter = (data.magFilter) ? data.magFilter : gl.LINEAR; - var minFilter = (data.minFilter) ? data.minFilter : gl.LINEAR; - var wrapS = (data.wrapS) ? data.wrapS : gl.CLAMP_TO_EDGE; - var wrapT = (data.wrapT) ? data.wrapT : gl.CLAMP_TO_EDGE; - var format = (data.luminance) ? gl.LUMINANCE : gl.RGBA; - - if (data.repeat) - { - wrapS = gl.REPEAT; - wrapT = gl.REPEAT; - } - - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !!data.flipY); - - if (data.width) - { - var width = (data.width) ? data.width : 512; - var height = (data.height) ? data.height : 2; - var border = (data.border) ? data.border : 0; - - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, ArrayBufferView? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, border, format, gl.UNSIGNED_BYTE, null); - } - else - { - // void texImage2D(GLenum target, GLint level, GLenum internalformat, GLenum format, GLenum type, ImageData? pixels); - gl.texImage2D(gl.TEXTURE_2D, 0, format, gl.RGBA, gl.UNSIGNED_BYTE, uniform.value.baseTexture.source); - } - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, magFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, minFilter); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapS); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapT); - } - - gl.uniform1i(uniform.uniformLocation, this.textureCount); - - uniform._init = true; - - this.textureCount++; - -}; - -/** -* Updates the shader uniform values. -* -* @method syncUniforms -*/ -PIXI.PixiShader.prototype.syncUniforms = function() -{ - this.textureCount = 1; - var uniform; - var gl = this.gl; - - // This would probably be faster in an array and it would guarantee key order - for (var key in this.uniforms) - { - uniform = this.uniforms[key]; - - if (uniform.glValueLength === 1) - { - if (uniform.glMatrix === true) - { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.transpose, uniform.value); - } - else - { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value); - } - } - else if (uniform.glValueLength === 2) - { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y); - } - else if (uniform.glValueLength === 3) - { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z); - } - else if (uniform.glValueLength === 4) - { - uniform.glFunc.call(gl, uniform.uniformLocation, uniform.value.x, uniform.value.y, uniform.value.z, uniform.value.w); - } - else if (uniform.type === 'sampler2D') - { - if (uniform._init) - { - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - if(uniform.value.baseTexture._dirty[gl.id]) - { - PIXI.instances[gl.id].updateTexture(uniform.value.baseTexture); - } - else - { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - } - - // gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id] || PIXI.createWebGLTexture( uniform.value.baseTexture, gl)); - gl.uniform1i(uniform.uniformLocation, this.textureCount); - this.textureCount++; - } - else - { - this.initSampler2D(uniform); - } - } - } - -}; - -/** -* Destroys the shader. -* -* @method destroy -*/ -PIXI.PixiShader.prototype.destroy = function() -{ - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** -* The Default Vertex shader source. -* -* @property defaultVertexSrc -* @type String -*/ -PIXI.PixiShader.defaultVertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'attribute vec4 aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', - '}' -]; -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** -* @class PixiFastShader -* @constructor -* @param gl {WebGLContext} the current WebGL drawing context -*/ -PIXI.PixiFastShader = function(gl) -{ - /** - * @property _UID - * @type Number - * @private - */ - this._UID = PIXI._UID++; - - /** - * @property gl - * @type WebGLContext - */ - this.gl = gl; - - /** - * The WebGL program. - * @property program - * @type Any - */ - this.program = null; - - /** - * The fragment shader. - * @property fragmentSrc - * @type Array - */ - this.fragmentSrc = [ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]; - - /** - * The vertex shader. - * @property vertexSrc - * @type Array - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aPositionCoord;', - 'attribute vec2 aScale;', - 'attribute float aRotation;', - 'attribute vec2 aTextureCoord;', - 'attribute float aColor;', - - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform mat3 uMatrix;', - - 'varying vec2 vTextureCoord;', - 'varying float vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', - - 'void main(void) {', - ' vec2 v;', - ' vec2 sv = aVertexPosition * aScale;', - ' v.x = (sv.x) * cos(aRotation) - (sv.y) * sin(aRotation);', - ' v.y = (sv.x) * sin(aRotation) + (sv.y) * cos(aRotation);', - ' v = ( uMatrix * vec3(v + aPositionCoord , 1.0) ).xy ;', - ' gl_Position = vec4( ( v / projectionVector) + center , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vec3 color = mod(vec3(aColor.y/65536.0, aColor.y/256.0, aColor.y), 256.0) / 256.0;', - ' vColor = aColor;', - '}' - ]; - - /** - * A local texture counter for multi-texture shaders. - * @property textureCount - * @type Number - */ - this.textureCount = 0; - - this.init(); -}; - -PIXI.PixiFastShader.prototype.constructor = PIXI.PixiFastShader; - -/** -* Initialises the shader. -* -* @method init -*/ -PIXI.PixiFastShader.prototype.init = function() -{ - var gl = this.gl; - - var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.dimensions = gl.getUniformLocation(program, 'dimensions'); - this.uMatrix = gl.getUniformLocation(program, 'uMatrix'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aPositionCoord = gl.getAttribLocation(program, 'aPositionCoord'); - - this.aScale = gl.getAttribLocation(program, 'aScale'); - this.aRotation = gl.getAttribLocation(program, 'aRotation'); - - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - // Begin worst hack eva // - - // WHY??? ONLY on my chrome pixel the line above returns -1 when using filters? - // maybe its somthing to do with the current state of the gl context. - // Im convinced this is a bug in the chrome browser as there is NO reason why this should be returning -1 especially as it only manifests on my chrome pixel - // If theres any webGL people that know why could happen please help :) - if(this.colorAttribute === -1) - { - this.colorAttribute = 2; - } - - this.attributes = [this.aVertexPosition, this.aPositionCoord, this.aScale, this.aRotation, this.aTextureCoord, this.colorAttribute]; - - // End worst hack eva // - - this.program = program; -}; - -/** -* Destroys the shader. -* -* @method destroy -*/ -PIXI.PixiFastShader.prototype.destroy = function() -{ - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** -* @class StripShader -* @constructor -* @param gl {WebGLContext} the current WebGL drawing context -*/ -PIXI.StripShader = function(gl) -{ - /** - * @property _UID - * @type Number - * @private - */ - this._UID = PIXI._UID++; - - /** - * @property gl - * @type WebGLContext - */ - this.gl = gl; - - /** - * The WebGL program. - * @property program - * @type Any - */ - this.program = null; - - /** - * The fragment shader. - * @property fragmentSrc - * @type Array - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec2 vTextureCoord;', - // 'varying float vColor;', - 'uniform float alpha;', - 'uniform sampler2D uSampler;', - - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.x, vTextureCoord.y)) * alpha;', - // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//gl_FragColor * alpha;', - '}' - ]; - - /** - * The vertex shader. - * @property vertexSrc - * @type Array - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - // 'uniform float alpha;', - // 'uniform vec3 tint;', - 'varying vec2 vTextureCoord;', - // 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, v.y / -projectionVector.y + 1.0 , 0.0, 1.0);', - ' vTextureCoord = aTextureCoord;', - // ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); -}; - -PIXI.StripShader.prototype.constructor = PIXI.StripShader; - -/** -* Initialises the shader. -* -* @method init -*/ -PIXI.StripShader.prototype.init = function() -{ - var gl = this.gl; - - var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.uSampler = gl.getUniformLocation(program, 'uSampler'); - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - //this.dimensions = gl.getUniformLocation(this.program, 'dimensions'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.aTextureCoord = gl.getAttribLocation(program, 'aTextureCoord'); - - this.attributes = [this.aVertexPosition, this.aTextureCoord]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** -* Destroys the shader. -* -* @method destroy -*/ -PIXI.StripShader.prototype.destroy = function() -{ - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** -* @class PrimitiveShader -* @constructor -* @param gl {WebGLContext} the current WebGL drawing context -*/ -PIXI.PrimitiveShader = function(gl) -{ - /** - * @property _UID - * @type Number - * @private - */ - this._UID = PIXI._UID++; - - /** - * @property gl - * @type WebGLContext - */ - this.gl = gl; - - /** - * The WebGL program. - * @property program - * @type Any - */ - this.program = null; - - /** - * The fragment shader. - * @property fragmentSrc - * @type Array - */ - this.fragmentSrc = [ - 'precision mediump float;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @property vertexSrc - * @type Array - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); -}; - -PIXI.PrimitiveShader.prototype.constructor = PIXI.PrimitiveShader; - -/** -* Initialises the shader. -* -* @method init -*/ -PIXI.PrimitiveShader.prototype.init = function() -{ - var gl = this.gl; - - var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** -* Destroys the shader. -* -* @method destroy -*/ -PIXI.PrimitiveShader.prototype.destroy = function() -{ - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attributes = null; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** -* @class ComplexPrimitiveShader -* @constructor -* @param gl {WebGLContext} the current WebGL drawing context -*/ -PIXI.ComplexPrimitiveShader = function(gl) -{ - /** - * @property _UID - * @type Number - * @private - */ - this._UID = PIXI._UID++; - - /** - * @property gl - * @type WebGLContext - */ - this.gl = gl; - - /** - * The WebGL program. - * @property program - * @type Any - */ - this.program = null; - - /** - * The fragment shader. - * @property fragmentSrc - * @type Array - */ - this.fragmentSrc = [ - - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void) {', - ' gl_FragColor = vColor;', - '}' - ]; - - /** - * The vertex shader. - * @property vertexSrc - * @type Array - */ - this.vertexSrc = [ - 'attribute vec2 aVertexPosition;', - //'attribute vec4 aColor;', - 'uniform mat3 translationMatrix;', - 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - 'uniform float flipY;', - 'varying vec4 vColor;', - - 'void main(void) {', - ' vec3 v = translationMatrix * vec3(aVertexPosition , 1.0);', - ' v -= offsetVector.xyx;', - ' gl_Position = vec4( v.x / projectionVector.x -1.0, (v.y / projectionVector.y * -flipY) + flipY , 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ]; - - this.init(); -}; - -PIXI.ComplexPrimitiveShader.prototype.constructor = PIXI.ComplexPrimitiveShader; - -/** -* Initialises the shader. -* -* @method init -*/ -PIXI.ComplexPrimitiveShader.prototype.init = function() -{ - var gl = this.gl; - - var program = PIXI.compileProgram(gl, this.vertexSrc, this.fragmentSrc); - gl.useProgram(program); - - // get and store the uniforms for the shader - this.projectionVector = gl.getUniformLocation(program, 'projectionVector'); - this.offsetVector = gl.getUniformLocation(program, 'offsetVector'); - this.tintColor = gl.getUniformLocation(program, 'tint'); - this.color = gl.getUniformLocation(program, 'color'); - this.flipY = gl.getUniformLocation(program, 'flipY'); - - // get and store the attributes - this.aVertexPosition = gl.getAttribLocation(program, 'aVertexPosition'); - // this.colorAttribute = gl.getAttribLocation(program, 'aColor'); - - this.attributes = [this.aVertexPosition, this.colorAttribute]; - - this.translationMatrix = gl.getUniformLocation(program, 'translationMatrix'); - this.alpha = gl.getUniformLocation(program, 'alpha'); - - this.program = program; -}; - -/** -* Destroys the shader. -* -* @method destroy -*/ -PIXI.ComplexPrimitiveShader.prototype.destroy = function() -{ - this.gl.deleteProgram( this.program ); - this.uniforms = null; - this.gl = null; - - this.attribute = null; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * A set of functions used by the webGL renderer to draw the primitive graphics data - * - * @class WebGLGraphics - * @private - * @static - */ -PIXI.WebGLGraphics = function() -{ -}; - -/** - * Renders the graphics object - * - * @static - * @private - * @method renderGraphics - * @param graphics {Graphics} - * @param renderSession {Object} - */ -PIXI.WebGLGraphics.renderGraphics = function(graphics, renderSession)//projection, offset) -{ - var gl = renderSession.gl; - var projection = renderSession.projection, - offset = renderSession.offset, - shader = renderSession.shaderManager.primitiveShader, - webGLData; - - if(graphics.dirty) - { - PIXI.WebGLGraphics.updateGraphics(graphics, gl); - } - - var webGL = graphics._webGL[gl.id]; - - // This could be speeded up for sure! - - for (var i = 0; i < webGL.data.length; i++) - { - if(webGL.data[i].mode === 1) - { - webGLData = webGL.data[i]; - - renderSession.stencilManager.pushStencil(graphics, webGLData, renderSession); - - // render quad.. - gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); - - renderSession.stencilManager.popStencil(graphics, webGLData, renderSession); - } - else - { - webGLData = webGL.data[i]; - - - renderSession.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; - gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); - - gl.uniform1f(shader.flipY, 1); - - gl.uniform2f(shader.projectionVector, projection.x, -projection.y); - gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); - - gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); - - gl.uniform1f(shader.alpha, graphics.worldAlpha); - - - gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); - - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); - gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); - - // set the index buffer! - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); - gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); - } - } -}; - -/** - * Updates the graphics object - * - * @static - * @private - * @method updateGraphics - * @param graphicsData {Graphics} The graphics object to update - * @param gl {WebGLContext} the current WebGL drawing context - */ -PIXI.WebGLGraphics.updateGraphics = function(graphics, gl) -{ - // get the contexts graphics object - var webGL = graphics._webGL[gl.id]; - // if the graphics object does not exist in the webGL context time to create it! - if(!webGL)webGL = graphics._webGL[gl.id] = {lastIndex:0, data:[], gl:gl}; - - // flag the graphics as not dirty as we are about to update it... - graphics.dirty = false; - - var i; - - // if the user cleared the graphics object we will need to clear every object - if(graphics.clearDirty) - { - graphics.clearDirty = false; - - // lop through and return all the webGLDatas to the object pool so than can be reused later on - for (i = 0; i < webGL.data.length; i++) - { - var graphicsData = webGL.data[i]; - graphicsData.reset(); - PIXI.WebGLGraphics.graphicsDataPool.push( graphicsData ); - } - - // clear the array and reset the index.. - webGL.data = []; - webGL.lastIndex = 0; - } - - var webGLData; - - // loop through the graphics datas and construct each one.. - // if the object is a complex fill then the new stencil buffer technique will be used - // other wise graphics objects will be pushed into a batch.. - for (i = webGL.lastIndex; i < graphics.graphicsData.length; i++) - { - var data = graphics.graphicsData[i]; - - if(data.type === PIXI.Graphics.POLY) - { - // need to add the points the the graphics object.. - data.points = data.shape.points.slice(); - if(data.shape.closed) - { - // close the poly if the value is true! - if(data.points[0] !== data.points[data.points.length-2] || data.points[1] !== data.points[data.points.length-1]) - { - data.points.push(data.points[0], data.points[1]); - } - } - - // MAKE SURE WE HAVE THE CORRECT TYPE.. - if(data.fill) - { - if(data.points.length >= 6) - { - if(data.points.length < 6 * 2) - { - webGLData = PIXI.WebGLGraphics.switchMode(webGL, 0); - - var canDrawUsingSimple = PIXI.WebGLGraphics.buildPoly(data, webGLData); - // console.log(canDrawUsingSimple); - - if(!canDrawUsingSimple) - { - // console.log("<>>>") - webGLData = PIXI.WebGLGraphics.switchMode(webGL, 1); - PIXI.WebGLGraphics.buildComplexPoly(data, webGLData); - } - - } - else - { - webGLData = PIXI.WebGLGraphics.switchMode(webGL, 1); - PIXI.WebGLGraphics.buildComplexPoly(data, webGLData); - } - } - } - - if(data.lineWidth > 0) - { - webGLData = PIXI.WebGLGraphics.switchMode(webGL, 0); - PIXI.WebGLGraphics.buildLine(data, webGLData); - - } - } - else - { - webGLData = PIXI.WebGLGraphics.switchMode(webGL, 0); - - if(data.type === PIXI.Graphics.RECT) - { - PIXI.WebGLGraphics.buildRectangle(data, webGLData); - } - else if(data.type === PIXI.Graphics.CIRC || data.type === PIXI.Graphics.ELIP) - { - PIXI.WebGLGraphics.buildCircle(data, webGLData); - } - else if(data.type === PIXI.Graphics.RREC) - { - PIXI.WebGLGraphics.buildRoundedRectangle(data, webGLData); - } - } - - webGL.lastIndex++; - } - - // upload all the dirty data... - for (i = 0; i < webGL.data.length; i++) - { - webGLData = webGL.data[i]; - if(webGLData.dirty)webGLData.upload(); - } -}; - -/** - * @static - * @private - * @method switchMode - * @param webGL {WebGLContext} - * @param type {Number} - */ -PIXI.WebGLGraphics.switchMode = function(webGL, type) -{ - var webGLData; - - if(!webGL.data.length) - { - webGLData = PIXI.WebGLGraphics.graphicsDataPool.pop() || new PIXI.WebGLGraphicsData(webGL.gl); - webGLData.mode = type; - webGL.data.push(webGLData); - } - else - { - webGLData = webGL.data[webGL.data.length-1]; - - if(webGLData.mode !== type || type === 1) - { - webGLData = PIXI.WebGLGraphics.graphicsDataPool.pop() || new PIXI.WebGLGraphicsData(webGL.gl); - webGLData.mode = type; - webGL.data.push(webGLData); - } - } - - webGLData.dirty = true; - - return webGLData; -}; - -/** - * Builds a rectangle to draw - * - * @static - * @private - * @method buildRectangle - * @param graphicsData {Graphics} The graphics object containing all the necessary properties - * @param webGLData {Object} - */ -PIXI.WebGLGraphics.buildRectangle = function(graphicsData, webGLData) -{ - // --- // - // need to convert points to a nice regular data - // - var rectData = graphicsData.shape; - var x = rectData.x; - var y = rectData.y; - var width = rectData.width; - var height = rectData.height; - - if(graphicsData.fill) - { - var color = PIXI.hex2rgb(graphicsData.fillColor); - var alpha = graphicsData.fillAlpha; - - var r = color[0] * alpha; - var g = color[1] * alpha; - var b = color[2] * alpha; - - var verts = webGLData.points; - var indices = webGLData.indices; - - var vertPos = verts.length/6; - - // start - verts.push(x, y); - verts.push(r, g, b, alpha); - - verts.push(x + width, y); - verts.push(r, g, b, alpha); - - verts.push(x , y + height); - verts.push(r, g, b, alpha); - - verts.push(x + width, y + height); - verts.push(r, g, b, alpha); - - // insert 2 dead triangles.. - indices.push(vertPos, vertPos, vertPos+1, vertPos+2, vertPos+3, vertPos+3); - } - - if(graphicsData.lineWidth) - { - var tempPoints = graphicsData.points; - - graphicsData.points = [x, y, - x + width, y, - x + width, y + height, - x, y + height, - x, y]; - - - PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); - - graphicsData.points = tempPoints; - } -}; - -/** - * Builds a rounded rectangle to draw - * - * @static - * @private - * @method buildRoundedRectangle - * @param graphicsData {Graphics} The graphics object containing all the necessary properties - * @param webGLData {Object} - */ -PIXI.WebGLGraphics.buildRoundedRectangle = function(graphicsData, webGLData) -{ - var rrectData = graphicsData.shape; - var x = rrectData.x; - var y = rrectData.y; - var width = rrectData.width; - var height = rrectData.height; - - var radius = rrectData.radius; - - var recPoints = []; - recPoints.push(x, y + radius); - recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x, y + height - radius, x, y + height, x + radius, y + height)); - recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x + width - radius, y + height, x + width, y + height, x + width, y + height - radius)); - recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x + width, y + radius, x + width, y, x + width - radius, y)); - recPoints = recPoints.concat(PIXI.WebGLGraphics.quadraticBezierCurve(x + radius, y, x, y, x, y + radius)); - - if (graphicsData.fill) { - var color = PIXI.hex2rgb(graphicsData.fillColor); - var alpha = graphicsData.fillAlpha; - - var r = color[0] * alpha; - var g = color[1] * alpha; - var b = color[2] * alpha; - - var verts = webGLData.points; - var indices = webGLData.indices; - - var vecPos = verts.length/6; - - var triangles = PIXI.PolyK.Triangulate(recPoints); - - // - - var i = 0; - for (i = 0; i < triangles.length; i+=3) - { - indices.push(triangles[i] + vecPos); - indices.push(triangles[i] + vecPos); - indices.push(triangles[i+1] + vecPos); - indices.push(triangles[i+2] + vecPos); - indices.push(triangles[i+2] + vecPos); - } - - - for (i = 0; i < recPoints.length; i++) - { - verts.push(recPoints[i], recPoints[++i], r, g, b, alpha); - } - } - - if (graphicsData.lineWidth) { - var tempPoints = graphicsData.points; - - graphicsData.points = recPoints; - - PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); - - graphicsData.points = tempPoints; - } -}; - -/** - * Calculate the points for a quadratic bezier curve. (helper function..) - * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c - * - * @static - * @private - * @method quadraticBezierCurve - * @param fromX {Number} Origin point x - * @param fromY {Number} Origin point x - * @param cpX {Number} Control point x - * @param cpY {Number} Control point y - * @param toX {Number} Destination point x - * @param toY {Number} Destination point y - * @return {Array(Number)} - */ -PIXI.WebGLGraphics.quadraticBezierCurve = function(fromX, fromY, cpX, cpY, toX, toY) { - - var xa, - ya, - xb, - yb, - x, - y, - n = 20, - points = []; - - function getPt(n1 , n2, perc) { - var diff = n2 - n1; - - return n1 + ( diff * perc ); - } - - var j = 0; - for (var i = 0; i <= n; i++ ) - { - j = i / n; - - // The Green Line - xa = getPt( fromX , cpX , j ); - ya = getPt( fromY , cpY , j ); - xb = getPt( cpX , toX , j ); - yb = getPt( cpY , toY , j ); - - // The Black Dot - x = getPt( xa , xb , j ); - y = getPt( ya , yb , j ); - - points.push(x, y); - } - return points; -}; - -/** - * Builds a circle to draw - * - * @static - * @private - * @method buildCircle - * @param graphicsData {Graphics} The graphics object to draw - * @param webGLData {Object} - */ -PIXI.WebGLGraphics.buildCircle = function(graphicsData, webGLData) -{ - // need to convert points to a nice regular data - var circleData = graphicsData.shape; - var x = circleData.x; - var y = circleData.y; - var width; - var height; - - // TODO - bit hacky?? - if(graphicsData.type === PIXI.Graphics.CIRC) - { - width = circleData.radius; - height = circleData.radius; - } - else - { - width = circleData.width; - height = circleData.height; - } - - var totalSegs = 40; - var seg = (Math.PI * 2) / totalSegs ; - - var i = 0; - - if(graphicsData.fill) - { - var color = PIXI.hex2rgb(graphicsData.fillColor); - var alpha = graphicsData.fillAlpha; - - var r = color[0] * alpha; - var g = color[1] * alpha; - var b = color[2] * alpha; - - var verts = webGLData.points; - var indices = webGLData.indices; - - var vecPos = verts.length/6; - - indices.push(vecPos); - - for (i = 0; i < totalSegs + 1 ; i++) - { - verts.push(x,y, r, g, b, alpha); - - verts.push(x + Math.sin(seg * i) * width, - y + Math.cos(seg * i) * height, - r, g, b, alpha); - - indices.push(vecPos++, vecPos++); - } - - indices.push(vecPos-1); - } - - if(graphicsData.lineWidth) - { - var tempPoints = graphicsData.points; - - graphicsData.points = []; - - for (i = 0; i < totalSegs + 1; i++) - { - graphicsData.points.push(x + Math.sin(seg * i) * width, - y + Math.cos(seg * i) * height); - } - - PIXI.WebGLGraphics.buildLine(graphicsData, webGLData); - - graphicsData.points = tempPoints; - } -}; - -/** - * Builds a line to draw - * - * @static - * @private - * @method buildLine - * @param graphicsData {Graphics} The graphics object containing all the necessary properties - * @param webGLData {Object} - */ -PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData) -{ - // TODO OPTIMISE! - var i = 0; - var points = graphicsData.points; - if(points.length === 0)return; - - // if the line width is an odd number add 0.5 to align to a whole pixel - if(graphicsData.lineWidth%2) - { - for (i = 0; i < points.length; i++) { - points[i] += 0.5; - } - } - - // get first and last point.. figure out the middle! - var firstPoint = new PIXI.Point( points[0], points[1] ); - var lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] ); - - // if the first point is the last point - gonna have issues :) - if(firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y) - { - // need to clone as we are going to slightly modify the shape.. - points = points.slice(); - - points.pop(); - points.pop(); - - lastPoint = new PIXI.Point( points[points.length - 2], points[points.length - 1] ); - - var midPointX = lastPoint.x + (firstPoint.x - lastPoint.x) *0.5; - var midPointY = lastPoint.y + (firstPoint.y - lastPoint.y) *0.5; - - points.unshift(midPointX, midPointY); - points.push(midPointX, midPointY); - } - - var verts = webGLData.points; - var indices = webGLData.indices; - var length = points.length / 2; - var indexCount = points.length; - var indexStart = verts.length/6; - - // DRAW the Line - var width = graphicsData.lineWidth / 2; - - // sort color - var color = PIXI.hex2rgb(graphicsData.lineColor); - var alpha = graphicsData.lineAlpha; - var r = color[0] * alpha; - var g = color[1] * alpha; - var b = color[2] * alpha; - - var px, py, p1x, p1y, p2x, p2y, p3x, p3y; - var perpx, perpy, perp2x, perp2y, perp3x, perp3y; - var a1, b1, c1, a2, b2, c2; - var denom, pdist, dist; - - p1x = points[0]; - p1y = points[1]; - - p2x = points[2]; - p2y = points[3]; - - perpx = -(p1y - p2y); - perpy = p1x - p2x; - - dist = Math.sqrt(perpx*perpx + perpy*perpy); - - perpx /= dist; - perpy /= dist; - perpx *= width; - perpy *= width; - - // start - verts.push(p1x - perpx , p1y - perpy, - r, g, b, alpha); - - verts.push(p1x + perpx , p1y + perpy, - r, g, b, alpha); - - for (i = 1; i < length-1; i++) - { - p1x = points[(i-1)*2]; - p1y = points[(i-1)*2 + 1]; - - p2x = points[(i)*2]; - p2y = points[(i)*2 + 1]; - - p3x = points[(i+1)*2]; - p3y = points[(i+1)*2 + 1]; - - perpx = -(p1y - p2y); - perpy = p1x - p2x; - - dist = Math.sqrt(perpx*perpx + perpy*perpy); - perpx /= dist; - perpy /= dist; - perpx *= width; - perpy *= width; - - perp2x = -(p2y - p3y); - perp2y = p2x - p3x; - - dist = Math.sqrt(perp2x*perp2x + perp2y*perp2y); - perp2x /= dist; - perp2y /= dist; - perp2x *= width; - perp2y *= width; - - a1 = (-perpy + p1y) - (-perpy + p2y); - b1 = (-perpx + p2x) - (-perpx + p1x); - c1 = (-perpx + p1x) * (-perpy + p2y) - (-perpx + p2x) * (-perpy + p1y); - a2 = (-perp2y + p3y) - (-perp2y + p2y); - b2 = (-perp2x + p2x) - (-perp2x + p3x); - c2 = (-perp2x + p3x) * (-perp2y + p2y) - (-perp2x + p2x) * (-perp2y + p3y); - - denom = a1*b2 - a2*b1; - - if(Math.abs(denom) < 0.1 ) - { - - denom+=10.1; - verts.push(p2x - perpx , p2y - perpy, - r, g, b, alpha); - - verts.push(p2x + perpx , p2y + perpy, - r, g, b, alpha); - - continue; - } - - px = (b1*c2 - b2*c1)/denom; - py = (a2*c1 - a1*c2)/denom; - - - pdist = (px -p2x) * (px -p2x) + (py -p2y) + (py -p2y); - - - if(pdist > 140 * 140) - { - perp3x = perpx - perp2x; - perp3y = perpy - perp2y; - - dist = Math.sqrt(perp3x*perp3x + perp3y*perp3y); - perp3x /= dist; - perp3y /= dist; - perp3x *= width; - perp3y *= width; - - verts.push(p2x - perp3x, p2y -perp3y); - verts.push(r, g, b, alpha); - - verts.push(p2x + perp3x, p2y +perp3y); - verts.push(r, g, b, alpha); - - verts.push(p2x - perp3x, p2y -perp3y); - verts.push(r, g, b, alpha); - - indexCount++; - } - else - { - - verts.push(px , py); - verts.push(r, g, b, alpha); - - verts.push(p2x - (px-p2x), p2y - (py - p2y)); - verts.push(r, g, b, alpha); - } - } - - p1x = points[(length-2)*2]; - p1y = points[(length-2)*2 + 1]; - - p2x = points[(length-1)*2]; - p2y = points[(length-1)*2 + 1]; - - perpx = -(p1y - p2y); - perpy = p1x - p2x; - - dist = Math.sqrt(perpx*perpx + perpy*perpy); - perpx /= dist; - perpy /= dist; - perpx *= width; - perpy *= width; - - verts.push(p2x - perpx , p2y - perpy); - verts.push(r, g, b, alpha); - - verts.push(p2x + perpx , p2y + perpy); - verts.push(r, g, b, alpha); - - indices.push(indexStart); - - for (i = 0; i < indexCount; i++) - { - indices.push(indexStart++); - } - - indices.push(indexStart-1); -}; - -/** - * Builds a complex polygon to draw - * - * @static - * @private - * @method buildComplexPoly - * @param graphicsData {Graphics} The graphics object containing all the necessary properties - * @param webGLData {Object} - */ -PIXI.WebGLGraphics.buildComplexPoly = function(graphicsData, webGLData) -{ - //TODO - no need to copy this as it gets turned into a FLoat32Array anyways.. - var points = graphicsData.points.slice(); - if(points.length < 6)return; - - // get first and last point.. figure out the middle! - var indices = webGLData.indices; - webGLData.points = points; - webGLData.alpha = graphicsData.fillAlpha; - webGLData.color = PIXI.hex2rgb(graphicsData.fillColor); - - /* - calclate the bounds.. - */ - var minX = Infinity; - var maxX = -Infinity; - - var minY = Infinity; - var maxY = -Infinity; - - var x,y; - - // get size.. - for (var i = 0; i < points.length; i+=2) - { - x = points[i]; - y = points[i+1]; - - minX = x < minX ? x : minX; - maxX = x > maxX ? x : maxX; - - minY = y < minY ? y : minY; - maxY = y > maxY ? y : maxY; - } - - // add a quad to the end cos there is no point making another buffer! - points.push(minX, minY, - maxX, minY, - maxX, maxY, - minX, maxY); - - // push a quad onto the end.. - - //TODO - this aint needed! - var length = points.length / 2; - for (i = 0; i < length; i++) - { - indices.push( i ); - } - -}; - -/** - * Builds a polygon to draw - * - * @static - * @private - * @method buildPoly - * @param graphicsData {Graphics} The graphics object containing all the necessary properties - * @param webGLData {Object} - */ -PIXI.WebGLGraphics.buildPoly = function(graphicsData, webGLData) -{ - var points = graphicsData.points; - - if(points.length < 6)return; - // get first and last point.. figure out the middle! - var verts = webGLData.points; - var indices = webGLData.indices; - - var length = points.length / 2; - - // sort color - var color = PIXI.hex2rgb(graphicsData.fillColor); - var alpha = graphicsData.fillAlpha; - var r = color[0] * alpha; - var g = color[1] * alpha; - var b = color[2] * alpha; - - var triangles = PIXI.PolyK.Triangulate(points); - - if(!triangles)return false; - - var vertPos = verts.length / 6; - - var i = 0; - - for (i = 0; i < triangles.length; i+=3) - { - indices.push(triangles[i] + vertPos); - indices.push(triangles[i] + vertPos); - indices.push(triangles[i+1] + vertPos); - indices.push(triangles[i+2] +vertPos); - indices.push(triangles[i+2] + vertPos); - } - - for (i = 0; i < length; i++) - { - verts.push(points[i * 2], points[i * 2 + 1], - r, g, b, alpha); - } - - return true; -}; - -PIXI.WebGLGraphics.graphicsDataPool = []; - -/** - * @class WebGLGraphicsData - * @private - * @static - */ -PIXI.WebGLGraphicsData = function(gl) -{ - this.gl = gl; - - //TODO does this need to be split before uploding?? - this.color = [0,0,0]; // color split! - this.points = []; - this.indices = []; - this.buffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - this.mode = 1; - this.alpha = 1; - this.dirty = true; -}; - -/** - * @method reset - */ -PIXI.WebGLGraphicsData.prototype.reset = function() -{ - this.points = []; - this.indices = []; -}; - -/** - * @method upload - */ -PIXI.WebGLGraphicsData.prototype.upload = function() -{ - var gl = this.gl; - -// this.lastIndex = graphics.graphicsData.length; - this.glPoints = new PIXI.Float32Array(this.points); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer); - gl.bufferData(gl.ARRAY_BUFFER, this.glPoints, gl.STATIC_DRAW); - - this.glIndicies = new PIXI.Uint16Array(this.indices); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.glIndicies, gl.STATIC_DRAW); - - this.dirty = false; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -PIXI.glContexts = []; // this is where we store the webGL contexts for easy access. -PIXI.instances = []; - -/** - * The WebGLRenderer draws the stage and all its content onto a webGL enabled canvas. This renderer - * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. - * So no need for Sprite Batches or Sprite Clouds. - * Don't forget to add the view to your DOM or you will not see anything :) - * - * @class WebGLRenderer - * @constructor - * @param [width=0] {Number} the width of the canvas view - * @param [height=0] {Number} the height of the canvas view - * @param [options] {Object} The optional renderer parameters - * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional - * @param [options.transparent=false] {Boolean} If the render view is transparent, default false - * @param [options.autoResize=false] {Boolean} If the render view is automatically resized, default false - * @param [options.antialias=false] {Boolean} sets antialias (only applicable in chrome at the moment) - * @param [options.preserveDrawingBuffer=false] {Boolean} enables drawing buffer preservation, enable this if you need to call toDataUrl on the webgl context - * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 - */ -PIXI.WebGLRenderer = function(width, height, options) -{ - if(options) - { - for (var i in PIXI.defaultRenderOptions) - { - if (typeof options[i] === 'undefined') options[i] = PIXI.defaultRenderOptions[i]; - } - } - else - { - options = PIXI.defaultRenderOptions; - } - - if(!PIXI.defaultRenderer) - { - PIXI.sayHello('webGL'); - PIXI.defaultRenderer = this; - } - - /** - * @property type - * @type Number - */ - this.type = PIXI.WEBGL_RENDERER; - - /** - * The resolution of the renderer - * - * @property resolution - * @type Number - * @default 1 - */ - this.resolution = options.resolution; - - // do a catch.. only 1 webGL renderer.. - - /** - * Whether the render view is transparent - * - * @property transparent - * @type Boolean - */ - this.transparent = options.transparent; - - /** - * Whether the render view should be resized automatically - * - * @property autoResize - * @type Boolean - */ - this.autoResize = options.autoResize || false; - - /** - * The value of the preserveDrawingBuffer flag affects whether or not the contents of the stencil buffer is retained after rendering. - * - * @property preserveDrawingBuffer - * @type Boolean - */ - this.preserveDrawingBuffer = options.preserveDrawingBuffer; - - /** - * This sets if the WebGLRenderer will clear the context texture or not before the new render pass. If true: - * If the Stage is NOT transparent, Pixi will clear to alpha (0, 0, 0, 0). - * If the Stage is transparent, Pixi will clear to the target Stage's background color. - * Disable this by setting this to false. For example: if your game has a canvas filling background image, you often don't need this set. - * - * @property clearBeforeRender - * @type Boolean - * @default - */ - this.clearBeforeRender = options.clearBeforeRender; - - /** - * The width of the canvas view - * - * @property width - * @type Number - * @default 800 - */ - this.width = width || 800; - - /** - * The height of the canvas view - * - * @property height - * @type Number - * @default 600 - */ - this.height = height || 600; - - /** - * The canvas element that everything is drawn to - * - * @property view - * @type HTMLCanvasElement - */ - this.view = options.view || document.createElement( 'canvas' ); - - // deal with losing context.. - - /** - * @property contextLostBound - * @type Function - */ - this.contextLostBound = this.handleContextLost.bind(this); - - /** - * @property contextRestoredBound - * @type Function - */ - this.contextRestoredBound = this.handleContextRestored.bind(this); - - this.view.addEventListener('webglcontextlost', this.contextLostBound, false); - this.view.addEventListener('webglcontextrestored', this.contextRestoredBound, false); - - /** - * @property _contextOptions - * @type Object - * @private - */ - this._contextOptions = { - alpha: this.transparent, - antialias: options.antialias, // SPEED UP?? - premultipliedAlpha:this.transparent && this.transparent !== 'notMultiplied', - stencil:true, - preserveDrawingBuffer: options.preserveDrawingBuffer - }; - - /** - * @property projection - * @type Point - */ - this.projection = new PIXI.Point(); - - /** - * @property offset - * @type Point - */ - this.offset = new PIXI.Point(0, 0); - - // time to create the render managers! each one focuses on managing a state in webGL - - /** - * Deals with managing the shader programs and their attribs - * @property shaderManager - * @type WebGLShaderManager - */ - this.shaderManager = new PIXI.WebGLShaderManager(); - - /** - * Manages the rendering of sprites - * @property spriteBatch - * @type WebGLSpriteBatch - */ - this.spriteBatch = new PIXI.WebGLSpriteBatch(); - - /** - * Manages the masks using the stencil buffer - * @property maskManager - * @type WebGLMaskManager - */ - this.maskManager = new PIXI.WebGLMaskManager(); - - /** - * Manages the filters - * @property filterManager - * @type WebGLFilterManager - */ - this.filterManager = new PIXI.WebGLFilterManager(); - - /** - * Manages the stencil buffer - * @property stencilManager - * @type WebGLStencilManager - */ - this.stencilManager = new PIXI.WebGLStencilManager(); - - /** - * Manages the blendModes - * @property blendModeManager - * @type WebGLBlendModeManager - */ - this.blendModeManager = new PIXI.WebGLBlendModeManager(); - - /** - * TODO remove - * @property renderSession - * @type Object - */ - this.renderSession = {}; - this.renderSession.gl = this.gl; - this.renderSession.drawCount = 0; - this.renderSession.shaderManager = this.shaderManager; - this.renderSession.maskManager = this.maskManager; - this.renderSession.filterManager = this.filterManager; - this.renderSession.blendModeManager = this.blendModeManager; - this.renderSession.spriteBatch = this.spriteBatch; - this.renderSession.stencilManager = this.stencilManager; - this.renderSession.renderer = this; - this.renderSession.resolution = this.resolution; - - // time init the context.. - this.initContext(); - - // map some webGL blend modes.. - this.mapBlendModes(); -}; - -// constructor -PIXI.WebGLRenderer.prototype.constructor = PIXI.WebGLRenderer; - -/** -* @method initContext -*/ -PIXI.WebGLRenderer.prototype.initContext = function() -{ - var gl = this.view.getContext('webgl', this._contextOptions) || this.view.getContext('experimental-webgl', this._contextOptions); - this.gl = gl; - - if (!gl) { - // fail, not able to get a context - throw new Error('This browser does not support webGL. Try using the canvas renderer'); - } - - this.glContextId = gl.id = PIXI.WebGLRenderer.glContextId ++; - - PIXI.glContexts[this.glContextId] = gl; - - PIXI.instances[this.glContextId] = this; - - // set up the default pixi settings.. - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.enable(gl.BLEND); - - // need to set the context for all the managers... - this.shaderManager.setContext(gl); - this.spriteBatch.setContext(gl); - this.maskManager.setContext(gl); - this.filterManager.setContext(gl); - this.blendModeManager.setContext(gl); - this.stencilManager.setContext(gl); - - this.renderSession.gl = this.gl; - - // now resize and we are good to go! - this.resize(this.width, this.height); -}; - -/** - * Renders the stage to its webGL view - * - * @method render - * @param stage {Stage} the Stage element to be rendered - */ -PIXI.WebGLRenderer.prototype.render = function(stage) -{ - // no point rendering if our context has been blown up! - if(this.contextLost)return; - - // if rendering a new stage clear the batches.. - if(this.__stage !== stage) - { - if(stage.interactive)stage.interactionManager.removeEvents(); - - // TODO make this work - // dont think this is needed any more? - this.__stage = stage; - } - - // update the scene graph - stage.updateTransform(); - - var gl = this.gl; - - // interaction - if(stage._interactive) - { - //need to add some events! - if(!stage._interactiveEventsAdded) - { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } - else - { - if(stage._interactiveEventsAdded) - { - stage._interactiveEventsAdded = false; - stage.interactionManager.setTarget(this); - } - } - - // -- Does this need to be set every frame? -- // - gl.viewport(0, 0, this.width, this.height); - - // make sure we are bound to the main frame buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - if (this.clearBeforeRender) - { - if(this.transparent) - { - gl.clearColor(0, 0, 0, 0); - } - else - { - gl.clearColor(stage.backgroundColorSplit[0],stage.backgroundColorSplit[1],stage.backgroundColorSplit[2], 1); - } - - gl.clear (gl.COLOR_BUFFER_BIT); - } - - this.renderDisplayObject( stage, this.projection ); -}; - -/** - * Renders a Display Object. - * - * @method renderDisplayObject - * @param displayObject {DisplayObject} The DisplayObject to render - * @param projection {Point} The projection - * @param buffer {Array} a standard WebGL buffer - */ -PIXI.WebGLRenderer.prototype.renderDisplayObject = function(displayObject, projection, buffer) -{ - this.renderSession.blendModeManager.setBlendMode(PIXI.blendModes.NORMAL); - - // reset the render session data.. - this.renderSession.drawCount = 0; - - // make sure to flip the Y if using a render texture.. - this.renderSession.flipY = buffer ? -1 : 1; - - // set the default projection - this.renderSession.projection = projection; - - //set the default offset - this.renderSession.offset = this.offset; - - // start the sprite batch - this.spriteBatch.begin(this.renderSession); - - // start the filter manager - this.filterManager.begin(this.renderSession, buffer); - - // render the scene! - displayObject._renderWebGL(this.renderSession); - - // finish the sprite batch - this.spriteBatch.end(); -}; - -/** - * Resizes the webGL view to the specified width and height. - * - * @method resize - * @param width {Number} the new width of the webGL view - * @param height {Number} the new height of the webGL view - */ -PIXI.WebGLRenderer.prototype.resize = function(width, height) -{ - this.width = width * this.resolution; - this.height = height * this.resolution; - - this.view.width = this.width; - this.view.height = this.height; - - if (this.autoResize) { - this.view.style.width = this.width / this.resolution + 'px'; - this.view.style.height = this.height / this.resolution + 'px'; - } - - this.gl.viewport(0, 0, this.width, this.height); - - this.projection.x = this.width / 2 / this.resolution; - this.projection.y = -this.height / 2 / this.resolution; -}; - -/** - * Updates and Creates a WebGL texture for the renderers context. - * - * @method updateTexture - * @param texture {Texture} the texture to update - */ -PIXI.WebGLRenderer.prototype.updateTexture = function(texture) -{ - if(!texture.hasLoaded )return; - - var gl = this.gl; - - if(!texture._glTextures[gl.id])texture._glTextures[gl.id] = gl.createTexture(); - - gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); - - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultipliedAlpha); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.source); - - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); - - - if(texture.mipmap && PIXI.isPowerOfTwo(texture.width, texture.height)) - { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR_MIPMAP_LINEAR : gl.NEAREST_MIPMAP_NEAREST); - gl.generateMipmap(gl.TEXTURE_2D); - } - else - { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, texture.scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); - } - - // reguler... - if(!texture._powerOf2) - { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - } - else - { - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); - } - - texture._dirty[gl.id] = false; - - return texture._glTextures[gl.id]; -}; - -/** - * Handles a lost webgl context - * - * @method handleContextLost - * @param event {Event} - * @private - */ -PIXI.WebGLRenderer.prototype.handleContextLost = function(event) -{ - event.preventDefault(); - this.contextLost = true; -}; - -/** - * Handles a restored webgl context - * - * @method handleContextRestored - * @param event {Event} - * @private - */ -PIXI.WebGLRenderer.prototype.handleContextRestored = function() -{ - this.initContext(); - - // empty all the ol gl textures as they are useless now - for(var key in PIXI.TextureCache) - { - var texture = PIXI.TextureCache[key].baseTexture; - texture._glTextures = []; - } - - this.contextLost = false; -}; - -/** - * Removes everything from the renderer (event listeners, spritebatch, etc...) - * - * @method destroy - */ -PIXI.WebGLRenderer.prototype.destroy = function() -{ - // remove listeners - this.view.removeEventListener('webglcontextlost', this.contextLostBound); - this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); - - PIXI.glContexts[this.glContextId] = null; - - this.projection = null; - this.offset = null; - - // time to create the render managers! each one focuses on managine a state in webGL - this.shaderManager.destroy(); - this.spriteBatch.destroy(); - this.maskManager.destroy(); - this.filterManager.destroy(); - - this.shaderManager = null; - this.spriteBatch = null; - this.maskManager = null; - this.filterManager = null; - - this.gl = null; - this.renderSession = null; -}; - -/** - * Maps Pixi blend modes to WebGL blend modes. - * - * @method mapBlendModes - */ -PIXI.WebGLRenderer.prototype.mapBlendModes = function() -{ - var gl = this.gl; - - if(!PIXI.blendModesWebGL) - { - PIXI.blendModesWebGL = []; - - PIXI.blendModesWebGL[PIXI.blendModes.NORMAL] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.ADD] = [gl.SRC_ALPHA, gl.DST_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.MULTIPLY] = [gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.SCREEN] = [gl.SRC_ALPHA, gl.ONE]; - PIXI.blendModesWebGL[PIXI.blendModes.OVERLAY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.DARKEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.LIGHTEN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.COLOR_DODGE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.COLOR_BURN] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.HARD_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.SOFT_LIGHT] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.DIFFERENCE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.EXCLUSION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.HUE] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.SATURATION] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - PIXI.blendModesWebGL[PIXI.blendModes.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; - } -}; - -PIXI.WebGLRenderer.glContextId = 0; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** -* @class WebGLBlendModeManager -* @constructor -* @param gl {WebGLContext} the current WebGL drawing context -*/ -PIXI.WebGLBlendModeManager = function() -{ - /** - * @property currentBlendMode - * @type Number - */ - this.currentBlendMode = 99999; -}; - -PIXI.WebGLBlendModeManager.prototype.constructor = PIXI.WebGLBlendModeManager; - -/** - * Sets the WebGL Context. - * - * @method setContext - * @param gl {WebGLContext} the current WebGL drawing context - */ -PIXI.WebGLBlendModeManager.prototype.setContext = function(gl) -{ - this.gl = gl; -}; - -/** -* Sets-up the given blendMode from WebGL's point of view. -* -* @method setBlendMode -* @param blendMode {Number} the blendMode, should be a Pixi const, such as PIXI.BlendModes.ADD -*/ -PIXI.WebGLBlendModeManager.prototype.setBlendMode = function(blendMode) -{ - if(this.currentBlendMode === blendMode)return false; - - this.currentBlendMode = blendMode; - - var blendModeWebGL = PIXI.blendModesWebGL[this.currentBlendMode]; - this.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); - - return true; -}; - -/** -* Destroys this object. -* -* @method destroy -*/ -PIXI.WebGLBlendModeManager.prototype.destroy = function() -{ - this.gl = null; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** -* @class WebGLMaskManager -* @constructor -* @private -*/ -PIXI.WebGLMaskManager = function() -{ -}; - -PIXI.WebGLMaskManager.prototype.constructor = PIXI.WebGLMaskManager; - -/** -* Sets the drawing context to the one given in parameter. -* -* @method setContext -* @param gl {WebGLContext} the current WebGL drawing context -*/ -PIXI.WebGLMaskManager.prototype.setContext = function(gl) -{ - this.gl = gl; -}; - -/** -* Applies the Mask and adds it to the current filter stack. -* -* @method pushMask -* @param maskData {Array} -* @param renderSession {Object} -*/ -PIXI.WebGLMaskManager.prototype.pushMask = function(maskData, renderSession) -{ - var gl = renderSession.gl; - - if(maskData.dirty) - { - PIXI.WebGLGraphics.updateGraphics(maskData, gl); - } - - if(!maskData._webGL[gl.id].data.length)return; - - renderSession.stencilManager.pushStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); -}; - -/** -* Removes the last filter from the filter stack and doesn't return it. -* -* @method popMask -* @param maskData {Array} -* @param renderSession {Object} an object containing all the useful parameters -*/ -PIXI.WebGLMaskManager.prototype.popMask = function(maskData, renderSession) -{ - var gl = this.gl; - renderSession.stencilManager.popStencil(maskData, maskData._webGL[gl.id].data[0], renderSession); -}; - -/** -* Destroys the mask stack. -* -* @method destroy -*/ -PIXI.WebGLMaskManager.prototype.destroy = function() -{ - this.gl = null; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** -* @class WebGLStencilManager -* @constructor -* @private -*/ -PIXI.WebGLStencilManager = function() -{ - this.stencilStack = []; - this.reverse = true; - this.count = 0; -}; - -/** -* Sets the drawing context to the one given in parameter. -* -* @method setContext -* @param gl {WebGLContext} the current WebGL drawing context -*/ -PIXI.WebGLStencilManager.prototype.setContext = function(gl) -{ - this.gl = gl; -}; - -/** -* Applies the Mask and adds it to the current filter stack. -* -* @method pushMask -* @param graphics {Graphics} -* @param webGLData {Array} -* @param renderSession {Object} -*/ -PIXI.WebGLStencilManager.prototype.pushStencil = function(graphics, webGLData, renderSession) -{ - var gl = this.gl; - this.bindGraphics(graphics, webGLData, renderSession); - - if(this.stencilStack.length === 0) - { - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.STENCIL_BUFFER_BIT); - this.reverse = true; - this.count = 0; - } - - this.stencilStack.push(webGLData); - - var level = this.count; - - gl.colorMask(false, false, false, false); - - gl.stencilFunc(gl.ALWAYS,0,0xFF); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.INVERT); - - // draw the triangle strip! - - if(webGLData.mode === 1) - { - gl.drawElements(gl.TRIANGLE_FAN, webGLData.indices.length - 4, gl.UNSIGNED_SHORT, 0 ); - - if(this.reverse) - { - gl.stencilFunc(gl.EQUAL, 0xFF - level, 0xFF); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); - } - else - { - gl.stencilFunc(gl.EQUAL,level, 0xFF); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); - } - - // draw a quad to increment.. - gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); - - if(this.reverse) - { - gl.stencilFunc(gl.EQUAL,0xFF-(level+1), 0xFF); - } - else - { - gl.stencilFunc(gl.EQUAL,level+1, 0xFF); - } - - this.reverse = !this.reverse; - } - else - { - if(!this.reverse) - { - gl.stencilFunc(gl.EQUAL, 0xFF - level, 0xFF); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); - } - else - { - gl.stencilFunc(gl.EQUAL,level, 0xFF); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); - } - - gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); - - if(!this.reverse) - { - gl.stencilFunc(gl.EQUAL,0xFF-(level+1), 0xFF); - } - else - { - gl.stencilFunc(gl.EQUAL,level+1, 0xFF); - } - } - - gl.colorMask(true, true, true, true); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - - this.count++; -}; - -/** - * TODO this does not belong here! - * - * @method bindGraphics - * @param graphics {Graphics} - * @param webGLData {Array} - * @param renderSession {Object} - */ -PIXI.WebGLStencilManager.prototype.bindGraphics = function(graphics, webGLData, renderSession) -{ - //if(this._currentGraphics === graphics)return; - this._currentGraphics = graphics; - - var gl = this.gl; - - // bind the graphics object.. - var projection = renderSession.projection, - offset = renderSession.offset, - shader;// = renderSession.shaderManager.primitiveShader; - - if(webGLData.mode === 1) - { - shader = renderSession.shaderManager.complexPrimitiveShader; - - renderSession.shaderManager.setShader( shader ); - - gl.uniform1f(shader.flipY, renderSession.flipY); - - gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); - - gl.uniform2f(shader.projectionVector, projection.x, -projection.y); - gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); - - gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); - gl.uniform3fv(shader.color, webGLData.color); - - gl.uniform1f(shader.alpha, graphics.worldAlpha * webGLData.alpha); - - gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); - - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 2, 0); - - - // now do the rest.. - // set the index buffer! - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); - } - else - { - //renderSession.shaderManager.activatePrimitiveShader(); - shader = renderSession.shaderManager.primitiveShader; - renderSession.shaderManager.setShader( shader ); - - gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); - - gl.uniform1f(shader.flipY, renderSession.flipY); - gl.uniform2f(shader.projectionVector, projection.x, -projection.y); - gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); - - gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); - - gl.uniform1f(shader.alpha, graphics.worldAlpha); - - gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); - - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); - gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); - - // set the index buffer! - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); - } -}; - -/** - * @method popStencil - * @param graphics {Graphics} - * @param webGLData {Array} - * @param renderSession {Object} - */ -PIXI.WebGLStencilManager.prototype.popStencil = function(graphics, webGLData, renderSession) -{ - var gl = this.gl; - this.stencilStack.pop(); - - this.count--; - - if(this.stencilStack.length === 0) - { - // the stack is empty! - gl.disable(gl.STENCIL_TEST); - - } - else - { - - var level = this.count; - - this.bindGraphics(graphics, webGLData, renderSession); - - gl.colorMask(false, false, false, false); - - if(webGLData.mode === 1) - { - this.reverse = !this.reverse; - - if(this.reverse) - { - gl.stencilFunc(gl.EQUAL, 0xFF - (level+1), 0xFF); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); - } - else - { - gl.stencilFunc(gl.EQUAL,level+1, 0xFF); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); - } - - // draw a quad to increment.. - gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); - - gl.stencilFunc(gl.ALWAYS,0,0xFF); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.INVERT); - - // draw the triangle strip! - gl.drawElements(gl.TRIANGLE_FAN, webGLData.indices.length - 4, gl.UNSIGNED_SHORT, 0 ); - - if(!this.reverse) - { - gl.stencilFunc(gl.EQUAL,0xFF-(level), 0xFF); - } - else - { - gl.stencilFunc(gl.EQUAL,level, 0xFF); - } - - } - else - { - // console.log("<<>>") - if(!this.reverse) - { - gl.stencilFunc(gl.EQUAL, 0xFF - (level+1), 0xFF); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); - } - else - { - gl.stencilFunc(gl.EQUAL,level+1, 0xFF); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); - } - - gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); - - if(!this.reverse) - { - gl.stencilFunc(gl.EQUAL,0xFF-(level), 0xFF); - } - else - { - gl.stencilFunc(gl.EQUAL,level, 0xFF); - } - } - - gl.colorMask(true, true, true, true); - gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); - - - } -}; - -/** -* Destroys the mask stack. -* -* @method destroy -*/ -PIXI.WebGLStencilManager.prototype.destroy = function() -{ - this.stencilStack = null; - this.gl = null; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** -* @class WebGLShaderManager -* @constructor -* @private -*/ -PIXI.WebGLShaderManager = function() -{ - /** - * @property maxAttibs - * @type Number - */ - this.maxAttibs = 10; - - /** - * @property attribState - * @type Array - */ - this.attribState = []; - - /** - * @property tempAttribState - * @type Array - */ - this.tempAttribState = []; - - for (var i = 0; i < this.maxAttibs; i++) - { - this.attribState[i] = false; - } - - /** - * @property stack - * @type Array - */ - this.stack = []; - -}; - -PIXI.WebGLShaderManager.prototype.constructor = PIXI.WebGLShaderManager; - -/** -* Initialises the context and the properties. -* -* @method setContext -* @param gl {WebGLContext} the current WebGL drawing context -*/ -PIXI.WebGLShaderManager.prototype.setContext = function(gl) -{ - this.gl = gl; - - // the next one is used for rendering primitives - this.primitiveShader = new PIXI.PrimitiveShader(gl); - - // the next one is used for rendering triangle strips - this.complexPrimitiveShader = new PIXI.ComplexPrimitiveShader(gl); - - // this shader is used for the default sprite rendering - this.defaultShader = new PIXI.PixiShader(gl); - - // this shader is used for the fast sprite rendering - this.fastShader = new PIXI.PixiFastShader(gl); - - // the next one is used for rendering triangle strips - this.stripShader = new PIXI.StripShader(gl); - this.setShader(this.defaultShader); -}; - -/** -* Takes the attributes given in parameters. -* -* @method setAttribs -* @param attribs {Array} attribs -*/ -PIXI.WebGLShaderManager.prototype.setAttribs = function(attribs) -{ - // reset temp state - var i; - - for (i = 0; i < this.tempAttribState.length; i++) - { - this.tempAttribState[i] = false; - } - - // set the new attribs - for (i = 0; i < attribs.length; i++) - { - var attribId = attribs[i]; - this.tempAttribState[attribId] = true; - } - - var gl = this.gl; - - for (i = 0; i < this.attribState.length; i++) - { - if(this.attribState[i] !== this.tempAttribState[i]) - { - this.attribState[i] = this.tempAttribState[i]; - - if(this.tempAttribState[i]) - { - gl.enableVertexAttribArray(i); - } - else - { - gl.disableVertexAttribArray(i); - } - } - } -}; - -/** -* Sets the current shader. -* -* @method setShader -* @param shader {Any} -*/ -PIXI.WebGLShaderManager.prototype.setShader = function(shader) -{ - if(this._currentId === shader._UID)return false; - - this._currentId = shader._UID; - - this.currentShader = shader; - - this.gl.useProgram(shader.program); - this.setAttribs(shader.attributes); - - return true; -}; - -/** -* Destroys this object. -* -* @method destroy -*/ -PIXI.WebGLShaderManager.prototype.destroy = function() -{ - this.attribState = null; - - this.tempAttribState = null; - - this.primitiveShader.destroy(); - - this.complexPrimitiveShader.destroy(); - - this.defaultShader.destroy(); - - this.fastShader.destroy(); - - this.stripShader.destroy(); - - this.gl = null; -}; - -/** - * @author Mat Groves - * - * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ - * for creating the original pixi version! - * Also a thanks to https://github.com/bchevalier for tweaking the tint and alpha so that they now share 4 bytes on the vertex buffer - * - * Heavily inspired by LibGDX's WebGLSpriteBatch: - * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java - */ - - /** - * - * @class WebGLSpriteBatch - * @private - * @constructor - */ -PIXI.WebGLSpriteBatch = function() -{ - /** - * @property vertSize - * @type Number - */ - this.vertSize = 5; - - /** - * The number of images in the SpriteBatch before it flushes - * @property size - * @type Number - */ - this.size = 2000;//Math.pow(2, 16) / this.vertSize; - - //the total number of bytes in our batch - var numVerts = this.size * 4 * 4 * this.vertSize; - //the total number of indices in our batch - var numIndices = this.size * 6; - - /** - * Holds the vertices - * - * @property vertices - * @type ArrayBuffer - */ - this.vertices = new PIXI.ArrayBuffer(numVerts); - - /** - * View on the vertices as a Float32Array - * - * @property positions - * @type Float32Array - */ - this.positions = new PIXI.Float32Array(this.vertices); - - /** - * View on the vertices as a Uint32Array - * - * @property colors - * @type Uint32Array - */ - this.colors = new PIXI.Uint32Array(this.vertices); - - /** - * Holds the indices - * - * @property indices - * @type Uint16Array - */ - this.indices = new PIXI.Uint16Array(numIndices); - - /** - * @property lastIndexCount - * @type Number - */ - this.lastIndexCount = 0; - - for (var i=0, j=0; i < numIndices; i += 6, j += 4) - { - this.indices[i + 0] = j + 0; - this.indices[i + 1] = j + 1; - this.indices[i + 2] = j + 2; - this.indices[i + 3] = j + 0; - this.indices[i + 4] = j + 2; - this.indices[i + 5] = j + 3; - } - - /** - * @property drawing - * @type Boolean - */ - this.drawing = false; - - /** - * @property currentBatchSize - * @type Number - */ - this.currentBatchSize = 0; - - /** - * @property currentBaseTexture - * @type BaseTexture - */ - this.currentBaseTexture = null; - - /** - * @property dirty - * @type Boolean - */ - this.dirty = true; - - /** - * @property textures - * @type Array - */ - this.textures = []; - - /** - * @property blendModes - * @type Array - */ - this.blendModes = []; - - /** - * @property shaders - * @type Array - */ - this.shaders = []; - - /** - * @property sprites - * @type Array - */ - this.sprites = []; - - /** - * @property defaultShader - * @type AbstractFilter - */ - this.defaultShader = new PIXI.AbstractFilter([ - 'precision lowp float;', - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - 'uniform sampler2D uSampler;', - 'void main(void) {', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ]); -}; - -/** -* @method setContext -* @param gl {WebGLContext} the current WebGL drawing context -*/ -PIXI.WebGLSpriteBatch.prototype.setContext = function(gl) -{ - this.gl = gl; - - // create a couple of buffers - this.vertexBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // 65535 is max index, so 65535 / 6 = 10922. - - //upload the index data - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); - - this.currentBlendMode = 99999; - - var shader = new PIXI.PixiShader(gl); - - shader.fragmentSrc = this.defaultShader.fragmentSrc; - shader.uniforms = {}; - shader.init(); - - this.defaultShader.shaders[gl.id] = shader; -}; - -/** -* @method begin -* @param renderSession {Object} The RenderSession object -*/ -PIXI.WebGLSpriteBatch.prototype.begin = function(renderSession) -{ - this.renderSession = renderSession; - this.shader = this.renderSession.shaderManager.defaultShader; - - this.start(); -}; - -/** -* @method end -*/ -PIXI.WebGLSpriteBatch.prototype.end = function() -{ - this.flush(); -}; - -/** -* @method render -* @param sprite {Sprite} the sprite to render when using this spritebatch -*/ -PIXI.WebGLSpriteBatch.prototype.render = function(sprite) -{ - var texture = sprite.texture; - - //TODO set blend modes.. - // check texture.. - if(this.currentBatchSize >= this.size) - { - this.flush(); - this.currentBaseTexture = texture.baseTexture; - } - - // get the uvs for the texture - var uvs = texture._uvs; - // if the uvs have not updated then no point rendering just yet! - if(!uvs)return; - - // TODO trim?? - var aX = sprite.anchor.x; - var aY = sprite.anchor.y; - - var w0, w1, h0, h1; - - if (texture.trim) - { - // if the sprite is trimmed then we need to add the extra space before transforming the sprite coords.. - var trim = texture.trim; - - w1 = trim.x - aX * trim.width; - w0 = w1 + texture.crop.width; - - h1 = trim.y - aY * trim.height; - h0 = h1 + texture.crop.height; - - } - else - { - w0 = (texture.frame.width ) * (1-aX); - w1 = (texture.frame.width ) * -aX; - - h0 = texture.frame.height * (1-aY); - h1 = texture.frame.height * -aY; - } - - var index = this.currentBatchSize * 4 * this.vertSize; - - var resolution = texture.baseTexture.resolution; - - var worldTransform = sprite.worldTransform; - - var a = worldTransform.a / resolution; - var b = worldTransform.b / resolution; - var c = worldTransform.c / resolution; - var d = worldTransform.d / resolution; - var tx = worldTransform.tx; - var ty = worldTransform.ty; - - var colors = this.colors; - var positions = this.positions; - - if(this.renderSession.roundPixels) - { - // xy - positions[index] = a * w1 + c * h1 + tx | 0; - positions[index+1] = d * h1 + b * w1 + ty | 0; - - // xy - positions[index+5] = a * w0 + c * h1 + tx | 0; - positions[index+6] = d * h1 + b * w0 + ty | 0; - - // xy - positions[index+10] = a * w0 + c * h0 + tx | 0; - positions[index+11] = d * h0 + b * w0 + ty | 0; - - // xy - positions[index+15] = a * w1 + c * h0 + tx | 0; - positions[index+16] = d * h0 + b * w1 + ty | 0; - } - else - { - // xy - positions[index] = a * w1 + c * h1 + tx; - positions[index+1] = d * h1 + b * w1 + ty; - - // xy - positions[index+5] = a * w0 + c * h1 + tx; - positions[index+6] = d * h1 + b * w0 + ty; - - // xy - positions[index+10] = a * w0 + c * h0 + tx; - positions[index+11] = d * h0 + b * w0 + ty; - - // xy - positions[index+15] = a * w1 + c * h0 + tx; - positions[index+16] = d * h0 + b * w1 + ty; - } - - // uv - positions[index+2] = uvs.x0; - positions[index+3] = uvs.y0; - - // uv - positions[index+7] = uvs.x1; - positions[index+8] = uvs.y1; - - // uv - positions[index+12] = uvs.x2; - positions[index+13] = uvs.y2; - - // uv - positions[index+17] = uvs.x3; - positions[index+18] = uvs.y3; - - // color and alpha - var tint = sprite.tint; - colors[index+4] = colors[index+9] = colors[index+14] = colors[index+19] = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16) + (sprite.worldAlpha * 255 << 24); - - // increment the batchsize - this.sprites[this.currentBatchSize++] = sprite; - - -}; - -/** -* Renders a TilingSprite using the spriteBatch. -* -* @method renderTilingSprite -* @param sprite {TilingSprite} the tilingSprite to render -*/ -PIXI.WebGLSpriteBatch.prototype.renderTilingSprite = function(tilingSprite) -{ - var texture = tilingSprite.tilingTexture; - - // check texture.. - if(this.currentBatchSize >= this.size) - { - //return; - this.flush(); - this.currentBaseTexture = texture.baseTexture; - } - - // set the textures uvs temporarily - // TODO create a separate texture so that we can tile part of a texture - - if(!tilingSprite._uvs)tilingSprite._uvs = new PIXI.TextureUvs(); - - var uvs = tilingSprite._uvs; - - tilingSprite.tilePosition.x %= texture.baseTexture.width * tilingSprite.tileScaleOffset.x; - tilingSprite.tilePosition.y %= texture.baseTexture.height * tilingSprite.tileScaleOffset.y; - - var offsetX = tilingSprite.tilePosition.x/(texture.baseTexture.width*tilingSprite.tileScaleOffset.x); - var offsetY = tilingSprite.tilePosition.y/(texture.baseTexture.height*tilingSprite.tileScaleOffset.y); - - var scaleX = (tilingSprite.width / texture.baseTexture.width) / (tilingSprite.tileScale.x * tilingSprite.tileScaleOffset.x); - var scaleY = (tilingSprite.height / texture.baseTexture.height) / (tilingSprite.tileScale.y * tilingSprite.tileScaleOffset.y); - - uvs.x0 = 0 - offsetX; - uvs.y0 = 0 - offsetY; - - uvs.x1 = (1 * scaleX) - offsetX; - uvs.y1 = 0 - offsetY; - - uvs.x2 = (1 * scaleX) - offsetX; - uvs.y2 = (1 * scaleY) - offsetY; - - uvs.x3 = 0 - offsetX; - uvs.y3 = (1 * scaleY) - offsetY; - - // get the tilingSprites current alpha and tint and combining them into a single color - var tint = tilingSprite.tint; - var color = (tint >> 16) + (tint & 0xff00) + ((tint & 0xff) << 16) + (tilingSprite.alpha * 255 << 24); - - var positions = this.positions; - var colors = this.colors; - - var width = tilingSprite.width; - var height = tilingSprite.height; - - // TODO trim?? - var aX = tilingSprite.anchor.x; - var aY = tilingSprite.anchor.y; - var w0 = width * (1-aX); - var w1 = width * -aX; - - var h0 = height * (1-aY); - var h1 = height * -aY; - - var index = this.currentBatchSize * 4 * this.vertSize; - - var resolution = texture.baseTexture.resolution; - - var worldTransform = tilingSprite.worldTransform; - - var a = worldTransform.a / resolution;//[0]; - var b = worldTransform.b / resolution;//[3]; - var c = worldTransform.c / resolution;//[1]; - var d = worldTransform.d / resolution;//[4]; - var tx = worldTransform.tx;//[2]; - var ty = worldTransform.ty;//[5]; - - // xy - positions[index++] = a * w1 + c * h1 + tx; - positions[index++] = d * h1 + b * w1 + ty; - // uv - positions[index++] = uvs.x0; - positions[index++] = uvs.y0; - // color - colors[index++] = color; - - // xy - positions[index++] = (a * w0 + c * h1 + tx); - positions[index++] = d * h1 + b * w0 + ty; - // uv - positions[index++] = uvs.x1; - positions[index++] = uvs.y1; - // color - colors[index++] = color; - - // xy - positions[index++] = a * w0 + c * h0 + tx; - positions[index++] = d * h0 + b * w0 + ty; - // uv - positions[index++] = uvs.x2; - positions[index++] = uvs.y2; - // color - colors[index++] = color; - - // xy - positions[index++] = a * w1 + c * h0 + tx; - positions[index++] = d * h0 + b * w1 + ty; - // uv - positions[index++] = uvs.x3; - positions[index++] = uvs.y3; - // color - colors[index++] = color; - - // increment the batchsize - this.sprites[this.currentBatchSize++] = tilingSprite; -}; - -/** -* Renders the content and empties the current batch. -* -* @method flush -*/ -PIXI.WebGLSpriteBatch.prototype.flush = function() -{ - // If the batch is length 0 then return as there is nothing to draw - if (this.currentBatchSize===0)return; - - var gl = this.gl; - var shader; - - if(this.dirty) - { - this.dirty = false; - // bind the main texture - gl.activeTexture(gl.TEXTURE0); - - // bind the buffers - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - shader = this.defaultShader.shaders[gl.id]; - - // this is the same for each shader? - var stride = this.vertSize * 4; - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, stride, 2 * 4); - - // color attributes will be interpreted as unsigned bytes and normalized - gl.vertexAttribPointer(shader.colorAttribute, 4, gl.UNSIGNED_BYTE, true, stride, 4 * 4); - } - - // upload the verts to the buffer - if(this.currentBatchSize > ( this.size * 0.5 ) ) - { - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); - } - else - { - var view = this.positions.subarray(0, this.currentBatchSize * 4 * this.vertSize); - gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); - } - - var nextTexture, nextBlendMode, nextShader; - var batchSize = 0; - var start = 0; - - var currentBaseTexture = null; - var currentBlendMode = this.renderSession.blendModeManager.currentBlendMode; - var currentShader = null; - - var blendSwap = false; - var shaderSwap = false; - var sprite; - - for (var i = 0, j = this.currentBatchSize; i < j; i++) { - - sprite = this.sprites[i]; - - nextTexture = sprite.texture.baseTexture; - nextBlendMode = sprite.blendMode; - nextShader = sprite.shader || this.defaultShader; - - blendSwap = currentBlendMode !== nextBlendMode; - shaderSwap = currentShader !== nextShader; // should I use _UIDS??? - - if(currentBaseTexture !== nextTexture || blendSwap || shaderSwap) - { - this.renderBatch(currentBaseTexture, batchSize, start); - - start = i; - batchSize = 0; - currentBaseTexture = nextTexture; - - if( blendSwap ) - { - currentBlendMode = nextBlendMode; - this.renderSession.blendModeManager.setBlendMode( currentBlendMode ); - } - - if( shaderSwap ) - { - currentShader = nextShader; - - shader = currentShader.shaders[gl.id]; - - if(!shader) - { - shader = new PIXI.PixiShader(gl); - - shader.fragmentSrc =currentShader.fragmentSrc; - shader.uniforms =currentShader.uniforms; - shader.init(); - - currentShader.shaders[gl.id] = shader; - } - - // set shader function??? - this.renderSession.shaderManager.setShader(shader); - - if(shader.dirty)shader.syncUniforms(); - - // both thease only need to be set if they are changing.. - // set the projection - var projection = this.renderSession.projection; - gl.uniform2f(shader.projectionVector, projection.x, projection.y); - - // TODO - this is temprorary! - var offsetVector = this.renderSession.offset; - gl.uniform2f(shader.offsetVector, offsetVector.x, offsetVector.y); - - // set the pointers - } - } - - batchSize++; - } - - this.renderBatch(currentBaseTexture, batchSize, start); - - // then reset the batch! - this.currentBatchSize = 0; -}; - -/** -* @method renderBatch -* @param texture {Texture} -* @param size {Number} -* @param startIndex {Number} -*/ -PIXI.WebGLSpriteBatch.prototype.renderBatch = function(texture, size, startIndex) -{ - if(size === 0)return; - - var gl = this.gl; - - // check if a texture is dirty.. - if(texture._dirty[gl.id]) - { - this.renderSession.renderer.updateTexture(texture); - } - else - { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); - } - - // now draw those suckas! - gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); - - // increment the draw count - this.renderSession.drawCount++; -}; - -/** -* @method stop -*/ -PIXI.WebGLSpriteBatch.prototype.stop = function() -{ - this.flush(); - this.dirty = true; -}; - -/** -* @method start -*/ -PIXI.WebGLSpriteBatch.prototype.start = function() -{ - this.dirty = true; -}; - -/** -* Destroys the SpriteBatch. -* -* @method destroy -*/ -PIXI.WebGLSpriteBatch.prototype.destroy = function() -{ - this.vertices = null; - this.indices = null; - - this.gl.deleteBuffer( this.vertexBuffer ); - this.gl.deleteBuffer( this.indexBuffer ); - - this.currentBaseTexture = null; - - this.gl = null; -}; -/** - * @author Mat Groves - * - * Big thanks to the very clever Matt DesLauriers https://github.com/mattdesl/ - * for creating the original pixi version! - * - * Heavily inspired by LibGDX's WebGLSpriteBatch: - * https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/WebGLSpriteBatch.java - */ - -/** -* @class WebGLFastSpriteBatch -* @constructor -*/ -PIXI.WebGLFastSpriteBatch = function(gl) -{ - /** - * @property vertSize - * @type Number - */ - this.vertSize = 10; - - /** - * @property maxSize - * @type Number - */ - this.maxSize = 6000;//Math.pow(2, 16) / this.vertSize; - - /** - * @property size - * @type Number - */ - this.size = this.maxSize; - - //the total number of floats in our batch - var numVerts = this.size * 4 * this.vertSize; - - //the total number of indices in our batch - var numIndices = this.maxSize * 6; - - /** - * Vertex data - * @property vertices - * @type Float32Array - */ - this.vertices = new PIXI.Float32Array(numVerts); - - /** - * Index data - * @property indices - * @type Uint16Array - */ - this.indices = new PIXI.Uint16Array(numIndices); - - /** - * @property vertexBuffer - * @type Object - */ - this.vertexBuffer = null; - - /** - * @property indexBuffer - * @type Object - */ - this.indexBuffer = null; - - /** - * @property lastIndexCount - * @type Number - */ - this.lastIndexCount = 0; - - for (var i=0, j=0; i < numIndices; i += 6, j += 4) - { - this.indices[i + 0] = j + 0; - this.indices[i + 1] = j + 1; - this.indices[i + 2] = j + 2; - this.indices[i + 3] = j + 0; - this.indices[i + 4] = j + 2; - this.indices[i + 5] = j + 3; - } - - /** - * @property drawing - * @type Boolean - */ - this.drawing = false; - - /** - * @property currentBatchSize - * @type Number - */ - this.currentBatchSize = 0; - - /** - * @property currentBaseTexture - * @type BaseTexture - */ - this.currentBaseTexture = null; - - /** - * @property currentBlendMode - * @type Number - */ - this.currentBlendMode = 0; - - /** - * @property renderSession - * @type Object - */ - this.renderSession = null; - - /** - * @property shader - * @type Object - */ - this.shader = null; - - /** - * @property matrix - * @type Matrix - */ - this.matrix = null; - - this.setContext(gl); -}; - -PIXI.WebGLFastSpriteBatch.prototype.constructor = PIXI.WebGLFastSpriteBatch; - -/** - * Sets the WebGL Context. - * - * @method setContext - * @param gl {WebGLContext} the current WebGL drawing context - */ -PIXI.WebGLFastSpriteBatch.prototype.setContext = function(gl) -{ - this.gl = gl; - - // create a couple of buffers - this.vertexBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // 65535 is max index, so 65535 / 6 = 10922. - - //upload the index data - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); -}; - -/** - * @method begin - * @param spriteBatch {WebGLSpriteBatch} - * @param renderSession {Object} - */ -PIXI.WebGLFastSpriteBatch.prototype.begin = function(spriteBatch, renderSession) -{ - this.renderSession = renderSession; - this.shader = this.renderSession.shaderManager.fastShader; - - this.matrix = spriteBatch.worldTransform.toArray(true); - - this.start(); -}; - -/** - * @method end - */ -PIXI.WebGLFastSpriteBatch.prototype.end = function() -{ - this.flush(); -}; - -/** - * @method render - * @param spriteBatch {WebGLSpriteBatch} - */ -PIXI.WebGLFastSpriteBatch.prototype.render = function(spriteBatch) -{ - var children = spriteBatch.children; - var sprite = children[0]; - - // if the uvs have not updated then no point rendering just yet! - - // check texture. - if(!sprite.texture._uvs)return; - - this.currentBaseTexture = sprite.texture.baseTexture; - - // check blend mode - if(sprite.blendMode !== this.renderSession.blendModeManager.currentBlendMode) - { - this.flush(); - this.renderSession.blendModeManager.setBlendMode(sprite.blendMode); - } - - for(var i=0,j= children.length; i= this.size) - { - this.flush(); - } -}; - -/** - * @method flush - */ -PIXI.WebGLFastSpriteBatch.prototype.flush = function() -{ - // If the batch is length 0 then return as there is nothing to draw - if (this.currentBatchSize===0)return; - - var gl = this.gl; - - // bind the current texture - - if(!this.currentBaseTexture._glTextures[gl.id])this.renderSession.renderer.updateTexture(this.currentBaseTexture, gl); - - gl.bindTexture(gl.TEXTURE_2D, this.currentBaseTexture._glTextures[gl.id]); - - // upload the verts to the buffer - - if(this.currentBatchSize > ( this.size * 0.5 ) ) - { - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); - } - else - { - var view = this.vertices.subarray(0, this.currentBatchSize * 4 * this.vertSize); - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, view); - } - - // now draw those suckas! - gl.drawElements(gl.TRIANGLES, this.currentBatchSize * 6, gl.UNSIGNED_SHORT, 0); - - // then reset the batch! - this.currentBatchSize = 0; - - // increment the draw count - this.renderSession.drawCount++; -}; - - -/** - * @method stop - */ -PIXI.WebGLFastSpriteBatch.prototype.stop = function() -{ - this.flush(); -}; - -/** - * @method start - */ -PIXI.WebGLFastSpriteBatch.prototype.start = function() -{ - var gl = this.gl; - - // bind the main texture - gl.activeTexture(gl.TEXTURE0); - - // bind the buffers - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // set the projection - var projection = this.renderSession.projection; - gl.uniform2f(this.shader.projectionVector, projection.x, projection.y); - - // set the matrix - gl.uniformMatrix3fv(this.shader.uMatrix, false, this.matrix); - - // set the pointers - var stride = this.vertSize * 4; - - gl.vertexAttribPointer(this.shader.aVertexPosition, 2, gl.FLOAT, false, stride, 0); - gl.vertexAttribPointer(this.shader.aPositionCoord, 2, gl.FLOAT, false, stride, 2 * 4); - gl.vertexAttribPointer(this.shader.aScale, 2, gl.FLOAT, false, stride, 4 * 4); - gl.vertexAttribPointer(this.shader.aRotation, 1, gl.FLOAT, false, stride, 6 * 4); - gl.vertexAttribPointer(this.shader.aTextureCoord, 2, gl.FLOAT, false, stride, 7 * 4); - gl.vertexAttribPointer(this.shader.colorAttribute, 1, gl.FLOAT, false, stride, 9 * 4); - -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** -* @class WebGLFilterManager -* @constructor -*/ -PIXI.WebGLFilterManager = function() -{ - /** - * @property filterStack - * @type Array - */ - this.filterStack = []; - - /** - * @property offsetX - * @type Number - */ - this.offsetX = 0; - - /** - * @property offsetY - * @type Number - */ - this.offsetY = 0; -}; - -PIXI.WebGLFilterManager.prototype.constructor = PIXI.WebGLFilterManager; - -/** -* Initialises the context and the properties. -* -* @method setContext -* @param gl {WebGLContext} the current WebGL drawing context -*/ -PIXI.WebGLFilterManager.prototype.setContext = function(gl) -{ - this.gl = gl; - this.texturePool = []; - - this.initShaderBuffers(); -}; - -/** -* @method begin -* @param renderSession {RenderSession} -* @param buffer {ArrayBuffer} -*/ -PIXI.WebGLFilterManager.prototype.begin = function(renderSession, buffer) -{ - this.renderSession = renderSession; - this.defaultShader = renderSession.shaderManager.defaultShader; - - var projection = this.renderSession.projection; - this.width = projection.x * 2; - this.height = -projection.y * 2; - this.buffer = buffer; -}; - -/** -* Applies the filter and adds it to the current filter stack. -* -* @method pushFilter -* @param filterBlock {Object} the filter that will be pushed to the current filter stack -*/ -PIXI.WebGLFilterManager.prototype.pushFilter = function(filterBlock) -{ - var gl = this.gl; - - var projection = this.renderSession.projection; - var offset = this.renderSession.offset; - - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); - - var filter = filterBlock.filterPasses[0]; - - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; - - var texture = this.texturePool.pop(); - if(!texture) - { - texture = new PIXI.FilterTexture(this.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } - - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; - - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; - - // cap filter to screen size.. - if(filterArea.x < 0)filterArea.x = 0; - if(filterArea.width > this.width)filterArea.width = this.width; - if(filterArea.y < 0)filterArea.y = 0; - if(filterArea.height > this.height)filterArea.height = this.height; - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderSession.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; - -}; - -/** -* Removes the last filter from the filter stack and doesn't return it. -* -* @method popFilter -*/ -PIXI.WebGLFilterManager.prototype.popFilter = function() -{ - var gl = this.gl; - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderSession.projection; - var offset = this.renderSession.offset; - - if(filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if(!outputTexture)outputTexture = new PIXI.FilterTexture(this.gl, this.width, this.height); - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if(this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderSession.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - this.texturePool.push(texture); - filterBlock._glFilterTexture = null; -}; - - -/** -* Applies the filter to the specified area. -* -* @method applyFilterPass -* @param filter {AbstractFilter} the filter that needs to be applied -* @param filterArea {Texture} TODO - might need an update -* @param width {Number} the horizontal range of the filter -* @param height {Number} the vertical range of the filter -*/ -PIXI.WebGLFilterManager.prototype.applyFilterPass = function(filter, filterArea, width, height) -{ - // use program - var gl = this.gl; - var shader = filter.shaders[gl.id]; - - if(!shader) - { - shader = new PIXI.PixiShader(gl); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderSession.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if(filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.colorAttribute, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - - this.renderSession.drawCount++; -}; - -/** -* Initialises the shader buffers. -* -* @method initShaderBuffers -*/ -PIXI.WebGLFilterManager.prototype.initShaderBuffers = function() -{ - var gl = this.gl; - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new PIXI.Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new PIXI.Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new PIXI.Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - -}; - -/** -* Destroys the filter and removes it from the filter stack. -* -* @method destroy -*/ -PIXI.WebGLFilterManager.prototype.destroy = function() -{ - var gl = this.gl; - - this.filterStack = null; - - this.offsetX = 0; - this.offsetY = 0; - - // destroy textures - for (var i = 0; i < this.texturePool.length; i++) { - this.texturePool[i].destroy(); - } - - this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** -* @class FilterTexture -* @constructor -* @param gl {WebGLContext} the current WebGL drawing context -* @param width {Number} the horizontal range of the filter -* @param height {Number} the vertical range of the filter -* @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values -*/ -PIXI.FilterTexture = function(gl, width, height, scaleMode) -{ - /** - * @property gl - * @type WebGLContext - */ - this.gl = gl; - - // next time to create a frame buffer and texture - - /** - * @property frameBuffer - * @type Any - */ - this.frameBuffer = gl.createFramebuffer(); - - /** - * @property texture - * @type Any - */ - this.texture = gl.createTexture(); - - /** - * @property scaleMode - * @type Number - */ - scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; - - gl.bindTexture(gl.TEXTURE_2D, this.texture); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, scaleMode === PIXI.scaleModes.LINEAR ? gl.LINEAR : gl.NEAREST); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer ); - - gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer ); - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture, 0); - - // required for masking a mask?? - this.renderBuffer = gl.createRenderbuffer(); - gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBuffer); - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, this.renderBuffer); - - this.resize(width, height); -}; - -PIXI.FilterTexture.prototype.constructor = PIXI.FilterTexture; - -/** -* Clears the filter texture. -* -* @method clear -*/ -PIXI.FilterTexture.prototype.clear = function() -{ - var gl = this.gl; - - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); -}; - -/** - * Resizes the texture to the specified width and height - * - * @method resize - * @param width {Number} the new width of the texture - * @param height {Number} the new height of the texture - */ -PIXI.FilterTexture.prototype.resize = function(width, height) -{ - if(this.width === width && this.height === height) return; - - this.width = width; - this.height = height; - - var gl = this.gl; - - gl.bindTexture(gl.TEXTURE_2D, this.texture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - // update the stencil buffer width and height - gl.bindRenderbuffer(gl.RENDERBUFFER, this.renderBuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); -}; - -/** -* Destroys the filter texture. -* -* @method destroy -*/ -PIXI.FilterTexture.prototype.destroy = function() -{ - var gl = this.gl; - gl.deleteFramebuffer( this.frameBuffer ); - gl.deleteTexture( this.texture ); - - this.frameBuffer = null; - this.texture = null; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * Creates a Canvas element of the given size. - * - * @class CanvasBuffer - * @constructor - * @param width {Number} the width for the newly created canvas - * @param height {Number} the height for the newly created canvas - */ -PIXI.CanvasBuffer = function(width, height) -{ - /** - * The width of the Canvas in pixels. - * - * @property width - * @type Number - */ - this.width = width; - - /** - * The height of the Canvas in pixels. - * - * @property height - * @type Number - */ - this.height = height; - - /** - * The Canvas object that belongs to this CanvasBuffer. - * - * @property canvas - * @type HTMLCanvasElement - */ - this.canvas = document.createElement("canvas"); - - /** - * A CanvasRenderingContext2D object representing a two-dimensional rendering context. - * - * @property context - * @type CanvasRenderingContext2D - */ - this.context = this.canvas.getContext("2d"); - - this.canvas.width = width; - this.canvas.height = height; -}; - -PIXI.CanvasBuffer.prototype.constructor = PIXI.CanvasBuffer; - -/** - * Clears the canvas that was created by the CanvasBuffer class. - * - * @method clear - * @private - */ -PIXI.CanvasBuffer.prototype.clear = function() -{ - this.context.setTransform(1, 0, 0, 1, 0, 0); - this.context.clearRect(0,0, this.width, this.height); -}; - -/** - * Resizes the canvas to the specified width and height. - * - * @method resize - * @param width {Number} the new width of the canvas - * @param height {Number} the new height of the canvas - */ -PIXI.CanvasBuffer.prototype.resize = function(width, height) -{ - this.width = this.canvas.width = width; - this.height = this.canvas.height = height; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * A set of functions used to handle masking. - * - * @class CanvasMaskManager - * @constructor - */ -PIXI.CanvasMaskManager = function() -{ -}; - -PIXI.CanvasMaskManager.prototype.constructor = PIXI.CanvasMaskManager; - -/** - * This method adds it to the current stack of masks. - * - * @method pushMask - * @param maskData {Object} the maskData that will be pushed - * @param renderSession {Object} The renderSession whose context will be used for this mask manager. - */ -PIXI.CanvasMaskManager.prototype.pushMask = function(maskData, renderSession) -{ - var context = renderSession.context; - - context.save(); - - var cacheAlpha = maskData.alpha; - var transform = maskData.worldTransform; - - var resolution = renderSession.resolution; - - context.setTransform(transform.a * resolution, - transform.b * resolution, - transform.c * resolution, - transform.d * resolution, - transform.tx * resolution, - transform.ty * resolution); - - PIXI.CanvasGraphics.renderGraphicsMask(maskData, context); - - context.clip(); - - maskData.worldAlpha = cacheAlpha; -}; - -/** - * Restores the current drawing context to the state it was before the mask was applied. - * - * @method popMask - * @param renderSession {Object} The renderSession whose context will be used for this mask manager. - */ -PIXI.CanvasMaskManager.prototype.popMask = function(renderSession) -{ - renderSession.context.restore(); -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * Utility methods for Sprite/Texture tinting. - * - * @class CanvasTinter - * @static - */ -PIXI.CanvasTinter = function() -{ -}; - -/** - * Basically this method just needs a sprite and a color and tints the sprite with the given color. - * - * @method getTintedTexture - * @static - * @param sprite {Sprite} the sprite to tint - * @param color {Number} the color to use to tint the sprite with - * @return {HTMLCanvasElement} The tinted canvas - */ -PIXI.CanvasTinter.getTintedTexture = function(sprite, color) -{ - var texture = sprite.texture; - - color = PIXI.CanvasTinter.roundColor(color); - - var stringColor = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); - - texture.tintCache = texture.tintCache || {}; - - if(texture.tintCache[stringColor]) return texture.tintCache[stringColor]; - - // clone texture.. - var canvas = PIXI.CanvasTinter.canvas || document.createElement("canvas"); - - //PIXI.CanvasTinter.tintWithPerPixel(texture, stringColor, canvas); - PIXI.CanvasTinter.tintMethod(texture, color, canvas); - - if(PIXI.CanvasTinter.convertTintToImage) - { - // is this better? - var tintImage = new Image(); - tintImage.src = canvas.toDataURL(); - - texture.tintCache[stringColor] = tintImage; - } - else - { - texture.tintCache[stringColor] = canvas; - // if we are not converting the texture to an image then we need to lose the reference to the canvas - PIXI.CanvasTinter.canvas = null; - } - - return canvas; -}; - -/** - * Tint a texture using the "multiply" operation. - * - * @method tintWithMultiply - * @static - * @param texture {Texture} the texture to tint - * @param color {Number} the color to use to tint the sprite with - * @param canvas {HTMLCanvasElement} the current canvas - */ -PIXI.CanvasTinter.tintWithMultiply = function(texture, color, canvas) -{ - var context = canvas.getContext( "2d" ); - - var crop = texture.crop; - - canvas.width = crop.width; - canvas.height = crop.height; - - context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); - - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = "multiply"; - - context.drawImage(texture.baseTexture.source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height); - - context.globalCompositeOperation = "destination-atop"; - - context.drawImage(texture.baseTexture.source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height); -}; - -/** - * Tint a texture using the "overlay" operation. - * - * @method tintWithOverlay - * @static - * @param texture {Texture} the texture to tint - * @param color {Number} the color to use to tint the sprite with - * @param canvas {HTMLCanvasElement} the current canvas - */ -PIXI.CanvasTinter.tintWithOverlay = function(texture, color, canvas) -{ - var context = canvas.getContext( "2d" ); - - var crop = texture.crop; - - canvas.width = crop.width; - canvas.height = crop.height; - - context.globalCompositeOperation = "copy"; - context.fillStyle = "#" + ("00000" + ( color | 0).toString(16)).substr(-6); - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = "destination-atop"; - context.drawImage(texture.baseTexture.source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height); - - //context.globalCompositeOperation = "copy"; -}; - -/** - * Tint a texture pixel per pixel. - * - * @method tintPerPixel - * @static - * @param texture {Texture} the texture to tint - * @param color {Number} the color to use to tint the sprite with - * @param canvas {HTMLCanvasElement} the current canvas - */ -PIXI.CanvasTinter.tintWithPerPixel = function(texture, color, canvas) -{ - var context = canvas.getContext( "2d" ); - - var crop = texture.crop; - - canvas.width = crop.width; - canvas.height = crop.height; - - context.globalCompositeOperation = "copy"; - context.drawImage(texture.baseTexture.source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height); - - var rgbValues = PIXI.hex2rgb(color); - var r = rgbValues[0], g = rgbValues[1], b = rgbValues[2]; - - var pixelData = context.getImageData(0, 0, crop.width, crop.height); - - var pixels = pixelData.data; - - for (var i = 0; i < pixels.length; i += 4) - { - pixels[i+0] *= r; - pixels[i+1] *= g; - pixels[i+2] *= b; - } - - context.putImageData(pixelData, 0, 0); -}; - -/** - * Rounds the specified color according to the PIXI.CanvasTinter.cacheStepsPerColorChannel. - * - * @method roundColor - * @static - * @param color {number} the color to round, should be a hex color - */ -PIXI.CanvasTinter.roundColor = function(color) -{ - var step = PIXI.CanvasTinter.cacheStepsPerColorChannel; - - var rgbValues = PIXI.hex2rgb(color); - - rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); - rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); - rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); - - return PIXI.rgb2hex(rgbValues); -}; - -/** - * Number of steps which will be used as a cap when rounding colors. - * - * @property cacheStepsPerColorChannel - * @type Number - * @static - */ -PIXI.CanvasTinter.cacheStepsPerColorChannel = 8; - -/** - * Tint cache boolean flag. - * - * @property convertTintToImage - * @type Boolean - * @static - */ -PIXI.CanvasTinter.convertTintToImage = false; - -/** - * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. - * - * @property canUseMultiply - * @type Boolean - * @static - */ -PIXI.CanvasTinter.canUseMultiply = PIXI.canUseNewCanvasBlendModes(); - -/** - * The tinting method that will be used. - * - * @method tintMethod - * @static - */ -PIXI.CanvasTinter.tintMethod = PIXI.CanvasTinter.canUseMultiply ? PIXI.CanvasTinter.tintWithMultiply : PIXI.CanvasTinter.tintWithPerPixel; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * The CanvasRenderer draws the Stage and all its content onto a 2d canvas. This renderer should be used for browsers that do not support webGL. - * Don't forget to add the CanvasRenderer.view to your DOM or you will not see anything :) - * - * @class CanvasRenderer - * @constructor - * @param [width=800] {Number} the width of the canvas view - * @param [height=600] {Number} the height of the canvas view - * @param [options] {Object} The optional renderer parameters - * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional - * @param [options.transparent=false] {Boolean} If the render view is transparent, default false - * @param [options.autoResize=false] {Boolean} If the render view is automatically resized, default false - * @param [options.resolution=1] {Number} the resolution of the renderer retina would be 2 - * @param [options.clearBeforeRender=true] {Boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - */ -PIXI.CanvasRenderer = function(width, height, options) -{ - if(options) - { - for (var i in PIXI.defaultRenderOptions) - { - if (typeof options[i] === "undefined") options[i] = PIXI.defaultRenderOptions[i]; - } - } - else - { - options = PIXI.defaultRenderOptions; - } - - if(!PIXI.defaultRenderer) - { - PIXI.sayHello("Canvas"); - PIXI.defaultRenderer = this; - } - - /** - * The renderer type. - * - * @property type - * @type Number - */ - this.type = PIXI.CANVAS_RENDERER; - - /** - * The resolution of the canvas. - * - * @property resolution - * @type Number - */ - this.resolution = options.resolution; - - /** - * This sets if the CanvasRenderer will clear the canvas or not before the new render pass. - * If the Stage is NOT transparent Pixi will use a canvas sized fillRect operation every frame to set the canvas background color. - * If the Stage is transparent Pixi will use clearRect to clear the canvas every frame. - * Disable this by setting this to false. For example if your game has a canvas filling background image you often don't need this set. - * - * @property clearBeforeRender - * @type Boolean - * @default - */ - this.clearBeforeRender = options.clearBeforeRender; - - /** - * Whether the render view is transparent - * - * @property transparent - * @type Boolean - */ - this.transparent = options.transparent; - - /** - * Whether the render view should be resized automatically - * - * @property autoResize - * @type Boolean - */ - this.autoResize = options.autoResize || false; - - - /** - * The width of the canvas view - * - * @property width - * @type Number - * @default 800 - */ - this.width = width || 800; - - /** - * The height of the canvas view - * - * @property height - * @type Number - * @default 600 - */ - this.height = height || 600; - - this.width *= this.resolution; - this.height *= this.resolution; - - /** - * The canvas element that everything is drawn to. - * - * @property view - * @type HTMLCanvasElement - */ - this.view = options.view || document.createElement( "canvas" ); - - /** - * The canvas 2d context that everything is drawn with - * @property context - * @type CanvasRenderingContext2D - */ - this.context = this.view.getContext( "2d", { alpha: this.transparent } ); - - /** - * Boolean flag controlling canvas refresh. - * - * @property refresh - * @type Boolean - */ - this.refresh = true; - - this.view.width = this.width * this.resolution; - this.view.height = this.height * this.resolution; - - /** - * Internal var. - * - * @property count - * @type Number - */ - this.count = 0; - - /** - * Instance of a PIXI.CanvasMaskManager, handles masking when using the canvas renderer - * @property CanvasMaskManager - * @type CanvasMaskManager - */ - this.maskManager = new PIXI.CanvasMaskManager(); - - /** - * The render session is just a bunch of parameter used for rendering - * @property renderSession - * @type Object - */ - this.renderSession = { - context: this.context, - maskManager: this.maskManager, - scaleMode: null, - smoothProperty: null, - /** - * If true Pixi will Math.floor() x/y values when rendering, stopping pixel interpolation. - * Handy for crisp pixel art and speed on legacy devices. - * - */ - roundPixels: false - }; - - this.mapBlendModes(); - - this.resize(width, height); - - if("imageSmoothingEnabled" in this.context) - this.renderSession.smoothProperty = "imageSmoothingEnabled"; - else if("webkitImageSmoothingEnabled" in this.context) - this.renderSession.smoothProperty = "webkitImageSmoothingEnabled"; - else if("mozImageSmoothingEnabled" in this.context) - this.renderSession.smoothProperty = "mozImageSmoothingEnabled"; - else if("oImageSmoothingEnabled" in this.context) - this.renderSession.smoothProperty = "oImageSmoothingEnabled"; - else if ("msImageSmoothingEnabled" in this.context) - this.renderSession.smoothProperty = "msImageSmoothingEnabled"; -}; - -// constructor -PIXI.CanvasRenderer.prototype.constructor = PIXI.CanvasRenderer; - -/** - * Renders the Stage to this canvas view - * - * @method render - * @param stage {Stage} the Stage element to be rendered - */ -PIXI.CanvasRenderer.prototype.render = function(stage) -{ - stage.updateTransform(); - - this.context.setTransform(1,0,0,1,0,0); - - this.context.globalAlpha = 1; - - this.renderSession.currentBlendMode = PIXI.blendModes.NORMAL; - this.context.globalCompositeOperation = PIXI.blendModesCanvas[PIXI.blendModes.NORMAL]; - - if (navigator.isCocoonJS && this.view.screencanvas) { - this.context.fillStyle = "black"; - this.context.clear(); - } - - if (this.clearBeforeRender) - { - if (this.transparent) - { - this.context.clearRect(0, 0, this.width, this.height); - } - else - { - this.context.fillStyle = stage.backgroundColorString; - this.context.fillRect(0, 0, this.width , this.height); - } - } - - this.renderDisplayObject(stage); - - // run interaction! - if(stage.interactive) - { - //need to add some events! - if(!stage._interactiveEventsAdded) - { - stage._interactiveEventsAdded = true; - stage.interactionManager.setTarget(this); - } - } -}; - -/** - * Removes everything from the renderer and optionally removes the Canvas DOM element. - * - * @method destroy - * @param [removeView=true] {boolean} Removes the Canvas element from the DOM. - */ -PIXI.CanvasRenderer.prototype.destroy = function(removeView) -{ - if (typeof removeView === "undefined") { removeView = true; } - - if (removeView && this.view.parent) - { - this.view.parent.removeChild(this.view); - } - - this.view = null; - this.context = null; - this.maskManager = null; - this.renderSession = null; - -}; - -/** - * Resizes the canvas view to the specified width and height - * - * @method resize - * @param width {Number} the new width of the canvas view - * @param height {Number} the new height of the canvas view - */ -PIXI.CanvasRenderer.prototype.resize = function(width, height) -{ - this.width = width * this.resolution; - this.height = height * this.resolution; - - this.view.width = this.width; - this.view.height = this.height; - - if (this.autoResize) { - this.view.style.width = this.width / this.resolution + "px"; - this.view.style.height = this.height / this.resolution + "px"; - } -}; - -/** - * Renders a display object - * - * @method renderDisplayObject - * @param displayObject {DisplayObject} The displayObject to render - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas - * @private - */ -PIXI.CanvasRenderer.prototype.renderDisplayObject = function(displayObject, context) -{ - this.renderSession.context = context || this.context; - this.renderSession.resolution = this.resolution; - displayObject._renderCanvas(this.renderSession); -}; - -/** - * Maps Pixi blend modes to canvas blend modes. - * - * @method mapBlendModes - * @private - */ -PIXI.CanvasRenderer.prototype.mapBlendModes = function() -{ - if(!PIXI.blendModesCanvas) - { - PIXI.blendModesCanvas = []; - - if(PIXI.canUseNewCanvasBlendModes()) - { - PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.ADD] = "lighter"; //IS THIS OK??? - PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "multiply"; - PIXI.blendModesCanvas[PIXI.blendModes.SCREEN] = "screen"; - PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY] = "overlay"; - PIXI.blendModesCanvas[PIXI.blendModes.DARKEN] = "darken"; - PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN] = "lighten"; - PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "color-dodge"; - PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "color-burn"; - PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "hard-light"; - PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "soft-light"; - PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "difference"; - PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "exclusion"; - PIXI.blendModesCanvas[PIXI.blendModes.HUE] = "hue"; - PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "saturation"; - PIXI.blendModesCanvas[PIXI.blendModes.COLOR] = "color"; - PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "luminosity"; - } - else - { - // this means that the browser does not support the cool new blend modes in canvas "cough" ie "cough" - PIXI.blendModesCanvas[PIXI.blendModes.NORMAL] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.ADD] = "lighter"; //IS THIS OK??? - PIXI.blendModesCanvas[PIXI.blendModes.MULTIPLY] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.SCREEN] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.OVERLAY] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.DARKEN] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.LIGHTEN] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.COLOR_DODGE] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.COLOR_BURN] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.HARD_LIGHT] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.SOFT_LIGHT] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.DIFFERENCE] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.EXCLUSION] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.HUE] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.SATURATION] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.COLOR] = "source-over"; - PIXI.blendModesCanvas[PIXI.blendModes.LUMINOSITY] = "source-over"; - } - } -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - - -/** - * A set of functions used by the canvas renderer to draw the primitive graphics data. - * - * @class CanvasGraphics - * @static - */ -PIXI.CanvasGraphics = function() -{ -}; - -/* - * Renders a PIXI.Graphics object to a canvas. - * - * @method renderGraphics - * @static - * @param graphics {Graphics} the actual graphics object to render - * @param context {CanvasRenderingContext2D} the 2d drawing method of the canvas - */ -PIXI.CanvasGraphics.renderGraphics = function(graphics, context) -{ - var worldAlpha = graphics.worldAlpha; - - if(graphics.dirty) - { - this.updateGraphicsTint(graphics); - graphics.dirty = false; - } - - - for (var i = 0; i < graphics.graphicsData.length; i++) - { - var data = graphics.graphicsData[i]; - var shape = data.shape; - - var fillColor = data._fillTint; - var lineColor = data._lineTint; - - context.lineWidth = data.lineWidth; - - if(data.type === PIXI.Graphics.POLY) - { - context.beginPath(); - - var points = shape.points; - - context.moveTo(points[0], points[1]); - - for (var j=1; j < points.length/2; j++) - { - context.lineTo(points[j * 2], points[j * 2 + 1]); - } - - if(shape.closed) - { - context.lineTo(points[0], points[1]); - } - - // if the first and last point are the same close the path - much neater :) - if(points[0] === points[points.length-2] && points[1] === points[points.length-1]) - { - context.closePath(); - } - - if(data.fill) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - } - if(data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - else if(data.type === PIXI.Graphics.RECT) - { - - if(data.fillColor || data.fillColor === 0) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fillRect(shape.x, shape.y, shape.width, shape.height); - - } - if(data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.strokeRect(shape.x, shape.y, shape.width, shape.height); - } - } - else if(data.type === PIXI.Graphics.CIRC) - { - // TODO - need to be Undefined! - context.beginPath(); - context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); - context.closePath(); - - if(data.fill) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - } - if(data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - else if(data.type === PIXI.Graphics.ELIP) - { - // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - - var w = shape.width * 2; - var h = shape.height * 2; - - var x = shape.x - w/2; - var y = shape.y - h/2; - - context.beginPath(); - - var kappa = 0.5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle - - context.moveTo(x, ym); - context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - - context.closePath(); - - if(data.fill) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - } - if(data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - else if (data.type === PIXI.Graphics.RREC) - { - var rx = shape.x; - var ry = shape.y; - var width = shape.width; - var height = shape.height; - var radius = shape.radius; - - var maxRadius = Math.min(width, height) / 2 | 0; - radius = radius > maxRadius ? maxRadius : radius; - - context.beginPath(); - context.moveTo(rx, ry + radius); - context.lineTo(rx, ry + height - radius); - context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); - context.lineTo(rx + width - radius, ry + height); - context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); - context.lineTo(rx + width, ry + radius); - context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); - context.lineTo(rx + radius, ry); - context.quadraticCurveTo(rx, ry, rx, ry + radius); - context.closePath(); - - if(data.fillColor || data.fillColor === 0) - { - context.globalAlpha = data.fillAlpha * worldAlpha; - context.fillStyle = '#' + ('00000' + ( fillColor | 0).toString(16)).substr(-6); - context.fill(); - - } - if(data.lineWidth) - { - context.globalAlpha = data.lineAlpha * worldAlpha; - context.strokeStyle = '#' + ('00000' + ( lineColor | 0).toString(16)).substr(-6); - context.stroke(); - } - } - } -}; - -/* - * Renders a graphics mask - * - * @static - * @private - * @method renderGraphicsMask - * @param graphics {Graphics} the graphics which will be used as a mask - * @param context {CanvasRenderingContext2D} the context 2d method of the canvas - */ -PIXI.CanvasGraphics.renderGraphicsMask = function(graphics, context) -{ - var len = graphics.graphicsData.length; - - if(len === 0) return; - - if(len > 1) - { - len = 1; - window.console.log('Pixi.js warning: masks in canvas can only mask using the first path in the graphics object'); - } - - for (var i = 0; i < 1; i++) - { - var data = graphics.graphicsData[i]; - var shape = data.shape; - - if(data.type === PIXI.Graphics.POLY) - { - context.beginPath(); - - var points = shape.points; - - context.moveTo(points[0], points[1]); - - for (var j=1; j < points.length/2; j++) - { - context.lineTo(points[j * 2], points[j * 2 + 1]); - } - - // if the first and last point are the same close the path - much neater :) - if(points[0] === points[points.length-2] && points[1] === points[points.length-1]) - { - context.closePath(); - } - - } - else if(data.type === PIXI.Graphics.RECT) - { - context.beginPath(); - context.rect(shape.x, shape.y, shape.width, shape.height); - context.closePath(); - } - else if(data.type === PIXI.Graphics.CIRC) - { - // TODO - need to be Undefined! - context.beginPath(); - context.arc(shape.x, shape.y, shape.radius,0,2*Math.PI); - context.closePath(); - } - else if(data.type === PIXI.Graphics.ELIP) - { - - // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas - - var w = shape.width * 2; - var h = shape.height * 2; - - var x = shape.x - w/2; - var y = shape.y - h/2; - - context.beginPath(); - - var kappa = 0.5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle - - context.moveTo(x, ym); - context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - context.closePath(); - } - else if (data.type === PIXI.Graphics.RREC) - { - - var pts = shape.points; - var rx = pts[0]; - var ry = pts[1]; - var width = pts[2]; - var height = pts[3]; - var radius = pts[4]; - - var maxRadius = Math.min(width, height) / 2 | 0; - radius = radius > maxRadius ? maxRadius : radius; - - context.beginPath(); - context.moveTo(rx, ry + radius); - context.lineTo(rx, ry + height - radius); - context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); - context.lineTo(rx + width - radius, ry + height); - context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); - context.lineTo(rx + width, ry + radius); - context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); - context.lineTo(rx + radius, ry); - context.quadraticCurveTo(rx, ry, rx, ry + radius); - context.closePath(); - } - } -}; - -PIXI.CanvasGraphics.updateGraphicsTint = function(graphics) -{ - if(graphics.tint === 0xFFFFFF)return; - - var tintR = (graphics.tint >> 16 & 0xFF) / 255; - var tintG = (graphics.tint >> 8 & 0xFF) / 255; - var tintB = (graphics.tint & 0xFF)/ 255; - - for (var i = 0; i < graphics.graphicsData.length; i++) - { - var data = graphics.graphicsData[i]; - - var fillColor = data.fillColor | 0; - var lineColor = data.lineColor | 0; - - /* - var colorR = (fillColor >> 16 & 0xFF) / 255; - var colorG = (fillColor >> 8 & 0xFF) / 255; - var colorB = (fillColor & 0xFF) / 255; - - colorR *= tintR; - colorG *= tintG; - colorB *= tintB; - - fillColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); - - colorR = (lineColor >> 16 & 0xFF) / 255; - colorG = (lineColor >> 8 & 0xFF) / 255; - colorB = (lineColor & 0xFF) / 255; - - colorR *= tintR; - colorG *= tintG; - colorB *= tintB; - - lineColor = ((colorR*255 << 16) + (colorG*255 << 8) + colorB*255); - */ - - // super inline cos im an optimization NAZI :) - data._fillTint = (((fillColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((fillColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (fillColor & 0xFF) / 255 * tintB*255); - data._lineTint = (((lineColor >> 16 & 0xFF) / 255 * tintR*255 << 16) + ((lineColor >> 8 & 0xFF) / 255 * tintG*255 << 8) + (lineColor & 0xFF) / 255 * tintB*255); - - } -}; - - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * The Graphics class contains methods used to draw primitive shapes such as lines, circles and rectangles to the display, and color and fill them. - * - * @class Graphics - * @extends DisplayObjectContainer - * @constructor - */ -PIXI.Graphics = function() -{ - PIXI.DisplayObjectContainer.call( this ); - - this.renderable = true; - - /** - * The alpha value used when filling the Graphics object. - * - * @property fillAlpha - * @type Number - */ - this.fillAlpha = 1; - - /** - * The width (thickness) of any lines drawn. - * - * @property lineWidth - * @type Number - */ - this.lineWidth = 0; - - /** - * The color of any lines drawn. - * - * @property lineColor - * @type String - * @default 0 - */ - this.lineColor = 0; - - /** - * Graphics data - * - * @property graphicsData - * @type Array - * @private - */ - this.graphicsData = []; - - /** - * The tint applied to the graphic shape. This is a hex value. Apply a value of 0xFFFFFF to reset the tint. - * - * @property tint - * @type Number - * @default 0xFFFFFF - */ - this.tint = 0xFFFFFF; - - /** - * The blend mode to be applied to the graphic shape. Apply a value of PIXI.blendModes.NORMAL to reset the blend mode. - * - * @property blendMode - * @type Number - * @default PIXI.blendModes.NORMAL; - */ - this.blendMode = PIXI.blendModes.NORMAL; - - /** - * Current path - * - * @property currentPath - * @type Object - * @private - */ - this.currentPath = null; - - /** - * Array containing some WebGL-related properties used by the WebGL renderer. - * - * @property _webGL - * @type Array - * @private - */ - this._webGL = []; - - /** - * Whether this shape is being used as a mask. - * - * @property isMask - * @type Boolean - */ - this.isMask = false; - - /** - * The bounds' padding used for bounds calculation. - * - * @property boundsPadding - * @type Number - */ - this.boundsPadding = 0; - - this._localBounds = new PIXI.Rectangle(0,0,1,1); - - /** - * Used to detect if the graphics object has changed. If this is set to true then the graphics object will be recalculated. - * - * @property dirty - * @type Boolean - * @private - */ - this.dirty = true; - - /** - * Used to detect if the webgl graphics object has changed. If this is set to true then the graphics object will be recalculated. - * - * @property webGLDirty - * @type Boolean - * @private - */ - this.webGLDirty = false; - - /** - * Used to detect if the cached sprite object needs to be updated. - * - * @property cachedSpriteDirty - * @type Boolean - * @private - */ - this.cachedSpriteDirty = false; - -}; - -// constructor -PIXI.Graphics.prototype = Object.create( PIXI.DisplayObjectContainer.prototype ); -PIXI.Graphics.prototype.constructor = PIXI.Graphics; - -/** - * When cacheAsBitmap is set to true the graphics object will be rendered as if it was a sprite. - * This is useful if your graphics element does not change often, as it will speed up the rendering of the object in exchange for taking up texture memory. - * It is also useful if you need the graphics object to be anti-aliased, because it will be rendered using canvas. - * This is not recommended if you are constantly redrawing the graphics element. - * - * @property cacheAsBitmap - * @type Boolean - * @default false - * @private - */ -Object.defineProperty(PIXI.Graphics.prototype, "cacheAsBitmap", { - get: function() { - return this._cacheAsBitmap; - }, - set: function(value) { - this._cacheAsBitmap = value; - - if(this._cacheAsBitmap) - { - - this._generateCachedSprite(); - } - else - { - this.destroyCachedSprite(); - this.dirty = true; - } - - } -}); - -/** - * Specifies the line style used for subsequent calls to Graphics methods such as the lineTo() method or the drawCircle() method. - * - * @method lineStyle - * @param lineWidth {Number} width of the line to draw, will update the objects stored style - * @param color {Number} color of the line to draw, will update the objects stored style - * @param alpha {Number} alpha of the line to draw, will update the objects stored style - * @return {Graphics} - */ -PIXI.Graphics.prototype.lineStyle = function(lineWidth, color, alpha) -{ - this.lineWidth = lineWidth || 0; - this.lineColor = color || 0; - this.lineAlpha = (arguments.length < 3) ? 1 : alpha; - - if(this.currentPath) - { - if(this.currentPath.shape.points.length) - { - // halfway through a line? start a new one! - this.drawShape( new PIXI.Polygon( this.currentPath.shape.points.slice(-2) )); - return this; - } - - // otherwise its empty so lets just set the line properties - this.currentPath.lineWidth = this.lineWidth; - this.currentPath.lineColor = this.lineColor; - this.currentPath.lineAlpha = this.lineAlpha; - - } - - return this; -}; - -/** - * Moves the current drawing position to x, y. - * - * @method moveTo - * @param x {Number} the X coordinate to move to - * @param y {Number} the Y coordinate to move to - * @return {Graphics} - */ -PIXI.Graphics.prototype.moveTo = function(x, y) -{ - this.drawShape(new PIXI.Polygon([x,y])); - - return this; -}; - -/** - * Draws a line using the current line style from the current drawing position to (x, y); - * The current drawing position is then set to (x, y). - * - * @method lineTo - * @param x {Number} the X coordinate to draw to - * @param y {Number} the Y coordinate to draw to - * @return {Graphics} - */ -PIXI.Graphics.prototype.lineTo = function(x, y) -{ - this.currentPath.shape.points.push(x, y); - this.dirty = true; - - return this; -}; - -/** - * Calculate the points for a quadratic bezier curve and then draws it. - * Based on: https://stackoverflow.com/questions/785097/how-do-i-implement-a-bezier-curve-in-c - * - * @method quadraticCurveTo - * @param cpX {Number} Control point x - * @param cpY {Number} Control point y - * @param toX {Number} Destination point x - * @param toY {Number} Destination point y - * @return {Graphics} - */ -PIXI.Graphics.prototype.quadraticCurveTo = function(cpX, cpY, toX, toY) -{ - if( this.currentPath ) - { - if(this.currentPath.shape.points.length === 0)this.currentPath.shape.points = [0,0]; - } - else - { - this.moveTo(0,0); - } - - var xa, - ya, - n = 20, - points = this.currentPath.shape.points; - if(points.length === 0)this.moveTo(0, 0); - - - var fromX = points[points.length-2]; - var fromY = points[points.length-1]; - - var j = 0; - for (var i = 1; i <= n; i++ ) - { - j = i / n; - - xa = fromX + ( (cpX - fromX) * j ); - ya = fromY + ( (cpY - fromY) * j ); - - points.push( xa + ( ((cpX + ( (toX - cpX) * j )) - xa) * j ), - ya + ( ((cpY + ( (toY - cpY) * j )) - ya) * j ) ); - } - - - this.dirty = true; - - return this; -}; - -/** - * Calculate the points for a bezier curve and then draws it. - * - * @method bezierCurveTo - * @param cpX {Number} Control point x - * @param cpY {Number} Control point y - * @param cpX2 {Number} Second Control point x - * @param cpY2 {Number} Second Control point y - * @param toX {Number} Destination point x - * @param toY {Number} Destination point y - * @return {Graphics} - */ -PIXI.Graphics.prototype.bezierCurveTo = function(cpX, cpY, cpX2, cpY2, toX, toY) -{ - if( this.currentPath ) - { - if(this.currentPath.shape.points.length === 0)this.currentPath.shape.points = [0,0]; - } - else - { - this.moveTo(0,0); - } - - var n = 20, - dt, - dt2, - dt3, - t2, - t3, - points = this.currentPath.shape.points; - - var fromX = points[points.length-2]; - var fromY = points[points.length-1]; - - var j = 0; - - for (var i=1; i<=n; i++) - { - j = i / n; - - dt = (1 - j); - dt2 = dt * dt; - dt3 = dt2 * dt; - - t2 = j * j; - t3 = t2 * j; - - points.push( dt3 * fromX + 3 * dt2 * j * cpX + 3 * dt * t2 * cpX2 + t3 * toX, - dt3 * fromY + 3 * dt2 * j * cpY + 3 * dt * t2 * cpY2 + t3 * toY); - } - - this.dirty = true; - - return this; -}; - -/* - * The arcTo() method creates an arc/curve between two tangents on the canvas. - * - * "borrowed" from https://code.google.com/p/fxcanvas/ - thanks google! - * - * @method arcTo - * @param x1 {Number} The x-coordinate of the beginning of the arc - * @param y1 {Number} The y-coordinate of the beginning of the arc - * @param x2 {Number} The x-coordinate of the end of the arc - * @param y2 {Number} The y-coordinate of the end of the arc - * @param radius {Number} The radius of the arc - * @return {Graphics} - */ -PIXI.Graphics.prototype.arcTo = function(x1, y1, x2, y2, radius) -{ - if( this.currentPath ) - { - if(this.currentPath.shape.points.length === 0) - { - this.currentPath.shape.points.push(x1, y1); - } - } - else - { - this.moveTo(x1, y1); - } - - var points = this.currentPath.shape.points; - var fromX = points[points.length-2]; - var fromY = points[points.length-1]; - var a1 = fromY - y1; - var b1 = fromX - x1; - var a2 = y2 - y1; - var b2 = x2 - x1; - var mm = Math.abs(a1 * b2 - b1 * a2); - - - if (mm < 1.0e-8 || radius === 0) - { - if( points[points.length-2] !== x1 || points[points.length-1] !== y1) - { - //console.log(">>") - points.push(x1, y1); - } - } - else - { - var dd = a1 * a1 + b1 * b1; - var cc = a2 * a2 + b2 * b2; - var tt = a1 * a2 + b1 * b2; - var k1 = radius * Math.sqrt(dd) / mm; - var k2 = radius * Math.sqrt(cc) / mm; - var j1 = k1 * tt / dd; - var j2 = k2 * tt / cc; - var cx = k1 * b2 + k2 * b1; - var cy = k1 * a2 + k2 * a1; - var px = b1 * (k2 + j1); - var py = a1 * (k2 + j1); - var qx = b2 * (k1 + j2); - var qy = a2 * (k1 + j2); - var startAngle = Math.atan2(py - cy, px - cx); - var endAngle = Math.atan2(qy - cy, qx - cx); - - this.arc(cx + x1, cy + y1, radius, startAngle, endAngle, b1 * a2 > b2 * a1); - } - - this.dirty = true; - - return this; -}; - -/** - * The arc method creates an arc/curve (used to create circles, or parts of circles). - * - * @method arc - * @param cx {Number} The x-coordinate of the center of the circle - * @param cy {Number} The y-coordinate of the center of the circle - * @param radius {Number} The radius of the circle - * @param startAngle {Number} The starting angle, in radians (0 is at the 3 o'clock position of the arc's circle) - * @param endAngle {Number} The ending angle, in radians - * @param anticlockwise {Boolean} Optional. Specifies whether the drawing should be counterclockwise or clockwise. False is default, and indicates clockwise, while true indicates counter-clockwise. - * @return {Graphics} - */ -PIXI.Graphics.prototype.arc = function(cx, cy, radius, startAngle, endAngle, anticlockwise) -{ - var startX = cx + Math.cos(startAngle) * radius; - var startY = cy + Math.sin(startAngle) * radius; - var points; - - if( this.currentPath ) - { - points = this.currentPath.shape.points; - - if(points.length === 0) - { - points.push(startX, startY); - } - else if( points[points.length-2] !== startX || points[points.length-1] !== startY) - { - points.push(startX, startY); - } - } - else - { - this.moveTo(startX, startY); - points = this.currentPath.shape.points; - } - - if (startAngle === endAngle)return this; - - if( !anticlockwise && endAngle <= startAngle ) - { - endAngle += Math.PI * 2; - } - else if( anticlockwise && startAngle <= endAngle ) - { - startAngle += Math.PI * 2; - } - - var sweep = anticlockwise ? (startAngle - endAngle) *-1 : (endAngle - startAngle); - var segs = ( Math.abs(sweep)/ (Math.PI * 2) ) * 40; - - if( sweep === 0 ) return this; - - var theta = sweep/(segs*2); - var theta2 = theta*2; - - var cTheta = Math.cos(theta); - var sTheta = Math.sin(theta); - - var segMinus = segs - 1; - - var remainder = ( segMinus % 1 ) / segMinus; - - for(var i=0; i<=segMinus; i++) - { - var real = i + remainder * i; - - - var angle = ((theta) + startAngle + (theta2 * real)); - - var c = Math.cos(angle); - var s = -Math.sin(angle); - - points.push(( (cTheta * c) + (sTheta * s) ) * radius + cx, - ( (cTheta * -s) + (sTheta * c) ) * radius + cy); - } - - this.dirty = true; - - return this; -}; - -/** - * Specifies a simple one-color fill that subsequent calls to other Graphics methods - * (such as lineTo() or drawCircle()) use when drawing. - * - * @method beginFill - * @param color {Number} the color of the fill - * @param alpha {Number} the alpha of the fill - * @return {Graphics} - */ -PIXI.Graphics.prototype.beginFill = function(color, alpha) -{ - this.filling = true; - this.fillColor = color || 0; - this.fillAlpha = (alpha === undefined) ? 1 : alpha; - - if(this.currentPath) - { - if(this.currentPath.shape.points.length <= 2) - { - this.currentPath.fill = this.filling; - this.currentPath.fillColor = this.fillColor; - this.currentPath.fillAlpha = this.fillAlpha; - } - } - return this; -}; - -/** - * Applies a fill to the lines and shapes that were added since the last call to the beginFill() method. - * - * @method endFill - * @return {Graphics} - */ -PIXI.Graphics.prototype.endFill = function() -{ - this.filling = false; - this.fillColor = null; - this.fillAlpha = 1; - - return this; -}; - -/** - * @method drawRect - * - * @param x {Number} The X coord of the top-left of the rectangle - * @param y {Number} The Y coord of the top-left of the rectangle - * @param width {Number} The width of the rectangle - * @param height {Number} The height of the rectangle - * @return {Graphics} - */ -PIXI.Graphics.prototype.drawRect = function( x, y, width, height ) -{ - this.drawShape(new PIXI.Rectangle(x,y, width, height)); - - return this; -}; - -/** - * @method drawRoundedRect - * - * @param x {Number} The X coord of the top-left of the rectangle - * @param y {Number} The Y coord of the top-left of the rectangle - * @param width {Number} The width of the rectangle - * @param height {Number} The height of the rectangle - * @param radius {Number} Radius of the rectangle corners - */ -PIXI.Graphics.prototype.drawRoundedRect = function( x, y, width, height, radius ) -{ - this.drawShape(new PIXI.RoundedRectangle(x, y, width, height, radius)); - - return this; -}; - -/** - * Draws a circle. - * - * @method drawCircle - * @param x {Number} The X coordinate of the center of the circle - * @param y {Number} The Y coordinate of the center of the circle - * @param radius {Number} The radius of the circle - * @return {Graphics} - */ -PIXI.Graphics.prototype.drawCircle = function(x, y, radius) -{ - this.drawShape(new PIXI.Circle(x,y, radius)); - - return this; -}; - -/** - * Draws an ellipse. - * - * @method drawEllipse - * @param x {Number} The X coordinate of the center of the ellipse - * @param y {Number} The Y coordinate of the center of the ellipse - * @param width {Number} The half width of the ellipse - * @param height {Number} The half height of the ellipse - * @return {Graphics} - */ -PIXI.Graphics.prototype.drawEllipse = function(x, y, width, height) -{ - this.drawShape(new PIXI.Ellipse(x, y, width, height)); - - return this; -}; - -/** - * Draws a polygon using the given path. - * - * @method drawPolygon - * @param path {Array} The path data used to construct the polygon. - * @return {Graphics} - */ -PIXI.Graphics.prototype.drawPolygon = function(path) -{ - if(!(path instanceof Array))path = Array.prototype.slice.call(arguments); - this.drawShape(new PIXI.Polygon(path)); - return this; -}; - -/** - * Clears the graphics that were drawn to this Graphics object, and resets fill and line style settings. - * - * @method clear - * @return {Graphics} - */ -PIXI.Graphics.prototype.clear = function() -{ - this.lineWidth = 0; - this.filling = false; - - this.dirty = true; - this.clearDirty = true; - this.graphicsData = []; - - return this; -}; - -/** - * Useful function that returns a texture of the graphics object that can then be used to create sprites - * This can be quite useful if your geometry is complicated and needs to be reused multiple times. - * - * @method generateTexture - * @param resolution {Number} The resolution of the texture being generated - * @param scaleMode {Number} Should be one of the PIXI.scaleMode consts - * @return {Texture} a texture of the graphics object - */ -PIXI.Graphics.prototype.generateTexture = function(resolution, scaleMode) -{ - resolution = resolution || 1; - - var bounds = this.getBounds(); - - var canvasBuffer = new PIXI.CanvasBuffer(bounds.width * resolution, bounds.height * resolution); - - var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas, scaleMode); - texture.baseTexture.resolution = resolution; - - canvasBuffer.context.scale(resolution, resolution); - - canvasBuffer.context.translate(-bounds.x,-bounds.y); - - PIXI.CanvasGraphics.renderGraphics(this, canvasBuffer.context); - - return texture; -}; - -/** -* Renders the object using the WebGL renderer -* -* @method _renderWebGL -* @param renderSession {RenderSession} -* @private -*/ -PIXI.Graphics.prototype._renderWebGL = function(renderSession) -{ - // if the sprite is not visible or the alpha is 0 then no need to render this element - if(this.visible === false || this.alpha === 0 || this.isMask === true)return; - - if(this._cacheAsBitmap) - { - - if(this.dirty || this.cachedSpriteDirty) - { - - this._generateCachedSprite(); - - // we will also need to update the texture on the gpu too! - this.updateCachedSpriteTexture(); - - this.cachedSpriteDirty = false; - this.dirty = false; - } - - this._cachedSprite.worldAlpha = this.worldAlpha; - PIXI.Sprite.prototype._renderWebGL.call(this._cachedSprite, renderSession); - - return; - } - else - { - renderSession.spriteBatch.stop(); - renderSession.blendModeManager.setBlendMode(this.blendMode); - - if(this._mask)renderSession.maskManager.pushMask(this._mask, renderSession); - if(this._filters)renderSession.filterManager.pushFilter(this._filterBlock); - - // check blend mode - if(this.blendMode !== renderSession.spriteBatch.currentBlendMode) - { - renderSession.spriteBatch.currentBlendMode = this.blendMode; - var blendModeWebGL = PIXI.blendModesWebGL[renderSession.spriteBatch.currentBlendMode]; - renderSession.spriteBatch.gl.blendFunc(blendModeWebGL[0], blendModeWebGL[1]); - } - - // check if the webgl graphic needs to be updated - if(this.webGLDirty) - { - this.dirty = true; - this.webGLDirty = false; - } - - PIXI.WebGLGraphics.renderGraphics(this, renderSession); - - // only render if it has children! - if(this.children.length) - { - renderSession.spriteBatch.start(); - - // simple render children! - for(var i=0, j=this.children.length; i maxX ? x2 : maxX; - maxX = x3 > maxX ? x3 : maxX; - maxX = x4 > maxX ? x4 : maxX; - - maxY = y2 > maxY ? y2 : maxY; - maxY = y3 > maxY ? y3 : maxY; - maxY = y4 > maxY ? y4 : maxY; - - this._bounds.x = minX; - this._bounds.width = maxX - minX; - - this._bounds.y = minY; - this._bounds.height = maxY - minY; - - return this._bounds; -}; - -/** - * Update the bounds of the object - * - * @method updateLocalBounds - */ -PIXI.Graphics.prototype.updateLocalBounds = function() -{ - var minX = Infinity; - var maxX = -Infinity; - - var minY = Infinity; - var maxY = -Infinity; - - if(this.graphicsData.length) - { - var shape, points, x, y, w, h; - - for (var i = 0; i < this.graphicsData.length; i++) { - var data = this.graphicsData[i]; - var type = data.type; - var lineWidth = data.lineWidth; - shape = data.shape; - - - if(type === PIXI.Graphics.RECT || type === PIXI.Graphics.RREC) - { - x = shape.x - lineWidth/2; - y = shape.y - lineWidth/2; - w = shape.width + lineWidth; - h = shape.height + lineWidth; - - minX = x < minX ? x : minX; - maxX = x + w > maxX ? x + w : maxX; - - minY = y < minY ? y : minY; - maxY = y + h > maxY ? y + h : maxY; - } - else if(type === PIXI.Graphics.CIRC) - { - x = shape.x; - y = shape.y; - w = shape.radius + lineWidth/2; - h = shape.radius + lineWidth/2; - - minX = x - w < minX ? x - w : minX; - maxX = x + w > maxX ? x + w : maxX; - - minY = y - h < minY ? y - h : minY; - maxY = y + h > maxY ? y + h : maxY; - } - else if(type === PIXI.Graphics.ELIP) - { - x = shape.x; - y = shape.y; - w = shape.width + lineWidth/2; - h = shape.height + lineWidth/2; - - minX = x - w < minX ? x - w : minX; - maxX = x + w > maxX ? x + w : maxX; - - minY = y - h < minY ? y - h : minY; - maxY = y + h > maxY ? y + h : maxY; - } - else - { - // POLY - points = shape.points; - - for (var j = 0; j < points.length; j+=2) - { - - x = points[j]; - y = points[j+1]; - minX = x-lineWidth < minX ? x-lineWidth : minX; - maxX = x+lineWidth > maxX ? x+lineWidth : maxX; - - minY = y-lineWidth < minY ? y-lineWidth : minY; - maxY = y+lineWidth > maxY ? y+lineWidth : maxY; - } - } - } - } - else - { - minX = 0; - maxX = 0; - minY = 0; - maxY = 0; - } - - var padding = this.boundsPadding; - - this._localBounds.x = minX - padding; - this._localBounds.width = (maxX - minX) + padding * 2; - - this._localBounds.y = minY - padding; - this._localBounds.height = (maxY - minY) + padding * 2; -}; - -/** - * Generates the cached sprite when the sprite has cacheAsBitmap = true - * - * @method _generateCachedSprite - * @private - */ -PIXI.Graphics.prototype._generateCachedSprite = function() -{ - var bounds = this.getLocalBounds(); - - if(!this._cachedSprite) - { - var canvasBuffer = new PIXI.CanvasBuffer(bounds.width, bounds.height); - var texture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); - - this._cachedSprite = new PIXI.Sprite(texture); - this._cachedSprite.buffer = canvasBuffer; - - this._cachedSprite.worldTransform = this.worldTransform; - } - else - { - this._cachedSprite.buffer.resize(bounds.width, bounds.height); - } - - // leverage the anchor to account for the offset of the element - this._cachedSprite.anchor.x = -( bounds.x / bounds.width ); - this._cachedSprite.anchor.y = -( bounds.y / bounds.height ); - - // this._cachedSprite.buffer.context.save(); - this._cachedSprite.buffer.context.translate(-bounds.x,-bounds.y); - - // make sure we set the alpha of the graphics to 1 for the render.. - this.worldAlpha = 1; - - // now render the graphic.. - PIXI.CanvasGraphics.renderGraphics(this, this._cachedSprite.buffer.context); - this._cachedSprite.alpha = this.alpha; -}; - -/** - * Updates texture size based on canvas size - * - * @method updateCachedSpriteTexture - * @private - */ -PIXI.Graphics.prototype.updateCachedSpriteTexture = function() -{ - var cachedSprite = this._cachedSprite; - var texture = cachedSprite.texture; - var canvas = cachedSprite.buffer.canvas; - - texture.baseTexture.width = canvas.width; - texture.baseTexture.height = canvas.height; - texture.crop.width = texture.frame.width = canvas.width; - texture.crop.height = texture.frame.height = canvas.height; - - cachedSprite._width = canvas.width; - cachedSprite._height = canvas.height; - - // update the dirty base textures - texture.baseTexture.dirty(); -}; - -/** - * Destroys a previous cached sprite. - * - * @method destroyCachedSprite - */ -PIXI.Graphics.prototype.destroyCachedSprite = function() -{ - this._cachedSprite.texture.destroy(true); - - // let the gc collect the unused sprite - // TODO could be object pooled! - this._cachedSprite = null; -}; - -/** - * Draws the given shape to this Graphics object. Can be any of Circle, Rectangle, Ellipse, Line or Polygon. - * - * @method drawShape - * @param {Circle|Rectangle|Ellipse|Line|Polygon} shape The Shape object to draw. - * @return {GraphicsData} The generated GraphicsData object. - */ -PIXI.Graphics.prototype.drawShape = function(shape) -{ - if(this.currentPath) - { - // check current path! - if(this.currentPath.shape.points.length <= 2)this.graphicsData.pop(); - } - - this.currentPath = null; - - var data = new PIXI.GraphicsData(this.lineWidth, this.lineColor, this.lineAlpha, this.fillColor, this.fillAlpha, this.filling, shape); - - this.graphicsData.push(data); - - if(data.type === PIXI.Graphics.POLY) - { - data.shape.closed = this.filling; - this.currentPath = data; - } - - this.dirty = true; - - return data; -}; - -/** - * A GraphicsData object. - * - * @class GraphicsData - * @constructor - */ -PIXI.GraphicsData = function(lineWidth, lineColor, lineAlpha, fillColor, fillAlpha, fill, shape) -{ - this.lineWidth = lineWidth; - this.lineColor = lineColor; - this.lineAlpha = lineAlpha; - this._lineTint = lineColor; - - this.fillColor = fillColor; - this.fillAlpha = fillAlpha; - this._fillTint = fillColor; - this.fill = fill; - - this.shape = shape; - this.type = shape.type; -}; - -// SOME TYPES: -PIXI.Graphics.POLY = 0; -PIXI.Graphics.RECT = 1; -PIXI.Graphics.CIRC = 2; -PIXI.Graphics.ELIP = 3; -PIXI.Graphics.RREC = 4; - -PIXI.Polygon.prototype.type = PIXI.Graphics.POLY; -PIXI.Rectangle.prototype.type = PIXI.Graphics.RECT; -PIXI.Circle.prototype.type = PIXI.Graphics.CIRC; -PIXI.Ellipse.prototype.type = PIXI.Graphics.ELIP; -PIXI.RoundedRectangle.prototype.type = PIXI.Graphics.RREC; - - -/** - * @author Mat Groves http://matgroves.com/ - */ - - /** - * - * @class Strip - * @extends DisplayObjectContainer - * @constructor - * @param texture {Texture} The texture to use - * @param width {Number} the width - * @param height {Number} the height - * - */ -PIXI.Strip = function(texture) -{ - PIXI.DisplayObjectContainer.call( this ); - - - /** - * The texture of the strip - * - * @property texture - * @type Texture - */ - this.texture = texture; - - // set up the main bits.. - this.uvs = new PIXI.Float32Array([0, 1, - 1, 1, - 1, 0, - 0, 1]); - - this.vertices = new PIXI.Float32Array([0, 0, - 100, 0, - 100, 100, - 0, 100]); - - this.colors = new PIXI.Float32Array([1, 1, 1, 1]); - - this.indices = new PIXI.Uint16Array([0, 1, 2, 3]); - - /** - * Whether the strip is dirty or not - * - * @property dirty - * @type Boolean - */ - this.dirty = true; - - /** - * The blend mode to be applied to the sprite. Set to PIXI.blendModes.NORMAL to remove any blend mode. - * - * @property blendMode - * @type Number - * @default PIXI.blendModes.NORMAL; - */ - this.blendMode = PIXI.blendModes.NORMAL; - - /** - * Triangles in canvas mode are automatically antialiased, use this value to force triangles to overlap a bit with each other. - * - * @property canvasPadding - * @type Number - */ - this.canvasPadding = 0; - - this.drawMode = PIXI.Strip.DrawModes.TRIANGLE_STRIP; - -}; - -// constructor -PIXI.Strip.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); -PIXI.Strip.prototype.constructor = PIXI.Strip; - -PIXI.Strip.prototype._renderWebGL = function(renderSession) -{ - // if the sprite is not visible or the alpha is 0 then no need to render this element - if(!this.visible || this.alpha <= 0)return; - // render triangle strip.. - - renderSession.spriteBatch.stop(); - - // init! init! - if(!this._vertexBuffer)this._initWebGL(renderSession); - - renderSession.shaderManager.setShader(renderSession.shaderManager.stripShader); - - this._renderStrip(renderSession); - - ///renderSession.shaderManager.activateDefaultShader(); - - renderSession.spriteBatch.start(); - - //TODO check culling -}; - -PIXI.Strip.prototype._initWebGL = function(renderSession) -{ - // build the strip! - var gl = renderSession.gl; - - this._vertexBuffer = gl.createBuffer(); - this._indexBuffer = gl.createBuffer(); - this._uvBuffer = gl.createBuffer(); - this._colorBuffer = gl.createBuffer(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.DYNAMIC_DRAW); - - gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.STATIC_DRAW); - - gl.bindBuffer(gl.ARRAY_BUFFER, this._colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colors, gl.STATIC_DRAW); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); -}; - -PIXI.Strip.prototype._renderStrip = function(renderSession) -{ - var gl = renderSession.gl; - var projection = renderSession.projection, - offset = renderSession.offset, - shader = renderSession.shaderManager.stripShader; - - var drawMode = this.drawMode === PIXI.Strip.DrawModes.TRIANGLE_STRIP ? gl.TRIANGLE_STRIP : gl.TRIANGLES; - - // gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mat4Real); - - renderSession.blendModeManager.setBlendMode(this.blendMode); - - - // set uniforms - gl.uniformMatrix3fv(shader.translationMatrix, false, this.worldTransform.toArray(true)); - gl.uniform2f(shader.projectionVector, projection.x, -projection.y); - gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); - gl.uniform1f(shader.alpha, this.worldAlpha); - - if(!this.dirty) - { - - gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertices); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - // update the uvs - gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.activeTexture(gl.TEXTURE0); - - // check if a texture is dirty.. - if(this.texture.baseTexture._dirty[gl.id]) - { - renderSession.renderer.updateTexture(this.texture.baseTexture); - } - else - { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]); - } - - // dont need to upload! - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); - - - } - else - { - - this.dirty = false; - gl.bindBuffer(gl.ARRAY_BUFFER, this._vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertices, gl.STATIC_DRAW); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - // update the uvs - gl.bindBuffer(gl.ARRAY_BUFFER, this._uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvs, gl.STATIC_DRAW); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.activeTexture(gl.TEXTURE0); - - // check if a texture is dirty.. - if(this.texture.baseTexture._dirty[gl.id]) - { - renderSession.renderer.updateTexture(this.texture.baseTexture); - } - else - { - gl.bindTexture(gl.TEXTURE_2D, this.texture.baseTexture._glTextures[gl.id]); - } - - // dont need to upload! - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); - - } - //console.log(gl.TRIANGLE_STRIP) - // - // - gl.drawElements(drawMode, this.indices.length, gl.UNSIGNED_SHORT, 0); - - -}; - - - -PIXI.Strip.prototype._renderCanvas = function(renderSession) -{ - var context = renderSession.context; - - var transform = this.worldTransform; - - if (renderSession.roundPixels) - { - context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx | 0, transform.ty | 0); - } - else - { - context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); - } - - if (this.drawMode === PIXI.Strip.DrawModes.TRIANGLE_STRIP) - { - this._renderCanvasTriangleStrip(context); - } - else - { - this._renderCanvasTriangles(context); - } -}; - -PIXI.Strip.prototype._renderCanvasTriangleStrip = function(context) -{ - // draw triangles!! - var vertices = this.vertices; - var uvs = this.uvs; - - var length = vertices.length / 2; - this.count++; - - for (var i = 0; i < length - 2; i++) { - // draw some triangles! - var index = i * 2; - this._renderCanvasDrawTriangle(context, vertices, uvs, index, (index + 2), (index + 4)); - } -}; - -PIXI.Strip.prototype._renderCanvasTriangles = function(context) -{ - // draw triangles!! - var vertices = this.vertices; - var uvs = this.uvs; - var indices = this.indices; - - var length = indices.length; - this.count++; - - for (var i = 0; i < length; i += 3) { - // draw some triangles! - var index0 = indices[i] * 2, index1 = indices[i + 1] * 2, index2 = indices[i + 2] * 2; - this._renderCanvasDrawTriangle(context, vertices, uvs, index0, index1, index2); - } -}; - -PIXI.Strip.prototype._renderCanvasDrawTriangle = function(context, vertices, uvs, index0, index1, index2) -{ - var textureSource = this.texture.baseTexture.source; - var textureWidth = this.texture.width; - var textureHeight = this.texture.height; - - var x0 = vertices[index0], x1 = vertices[index1], x2 = vertices[index2]; - var y0 = vertices[index0 + 1], y1 = vertices[index1 + 1], y2 = vertices[index2 + 1]; - - var u0 = uvs[index0] * textureWidth, u1 = uvs[index1] * textureWidth, u2 = uvs[index2] * textureWidth; - var v0 = uvs[index0 + 1] * textureHeight, v1 = uvs[index1 + 1] * textureHeight, v2 = uvs[index2 + 1] * textureHeight; - - if (this.canvasPadding > 0) { - var paddingX = this.canvasPadding / this.worldTransform.a; - var paddingY = this.canvasPadding / this.worldTransform.d; - var centerX = (x0 + x1 + x2) / 3; - var centerY = (y0 + y1 + y2) / 3; - - var normX = x0 - centerX; - var normY = y0 - centerY; - - var dist = Math.sqrt(normX * normX + normY * normY); - x0 = centerX + (normX / dist) * (dist + paddingX); - y0 = centerY + (normY / dist) * (dist + paddingY); - - // - - normX = x1 - centerX; - normY = y1 - centerY; - - dist = Math.sqrt(normX * normX + normY * normY); - x1 = centerX + (normX / dist) * (dist + paddingX); - y1 = centerY + (normY / dist) * (dist + paddingY); - - normX = x2 - centerX; - normY = y2 - centerY; - - dist = Math.sqrt(normX * normX + normY * normY); - x2 = centerX + (normX / dist) * (dist + paddingX); - y2 = centerY + (normY / dist) * (dist + paddingY); - } - - context.save(); - context.beginPath(); - - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - - context.closePath(); - - context.clip(); - - // Compute matrix transform - var delta = (u0 * v1) + (v0 * u2) + (u1 * v2) - (v1 * u2) - (v0 * u1) - (u0 * v2); - var deltaA = (x0 * v1) + (v0 * x2) + (x1 * v2) - (v1 * x2) - (v0 * x1) - (x0 * v2); - var deltaB = (u0 * x1) + (x0 * u2) + (u1 * x2) - (x1 * u2) - (x0 * u1) - (u0 * x2); - var deltaC = (u0 * v1 * x2) + (v0 * x1 * u2) + (x0 * u1 * v2) - (x0 * v1 * u2) - (v0 * u1 * x2) - (u0 * x1 * v2); - var deltaD = (y0 * v1) + (v0 * y2) + (y1 * v2) - (v1 * y2) - (v0 * y1) - (y0 * v2); - var deltaE = (u0 * y1) + (y0 * u2) + (u1 * y2) - (y1 * u2) - (y0 * u1) - (u0 * y2); - var deltaF = (u0 * v1 * y2) + (v0 * y1 * u2) + (y0 * u1 * v2) - (y0 * v1 * u2) - (v0 * u1 * y2) - (u0 * y1 * v2); - - context.transform(deltaA / delta, deltaD / delta, - deltaB / delta, deltaE / delta, - deltaC / delta, deltaF / delta); - - context.drawImage(textureSource, 0, 0); - context.restore(); -}; - - - -/** - * Renders a flat strip - * - * @method renderStripFlat - * @param strip {Strip} The Strip to render - * @private - */ -PIXI.Strip.prototype.renderStripFlat = function(strip) -{ - var context = this.context; - var vertices = strip.vertices; - - var length = vertices.length/2; - this.count++; - - context.beginPath(); - for (var i=1; i < length-2; i++) - { - // draw some triangles! - var index = i*2; - - var x0 = vertices[index], x1 = vertices[index+2], x2 = vertices[index+4]; - var y0 = vertices[index+1], y1 = vertices[index+3], y2 = vertices[index+5]; - - context.moveTo(x0, y0); - context.lineTo(x1, y1); - context.lineTo(x2, y2); - } - - context.fillStyle = '#FF0000'; - context.fill(); - context.closePath(); -}; - -/* -PIXI.Strip.prototype.setTexture = function(texture) -{ - //TODO SET THE TEXTURES - //TODO VISIBILITY - - // stop current texture - this.texture = texture; - this.width = texture.frame.width; - this.height = texture.frame.height; - this.updateFrame = true; -}; -*/ - -/** - * When the texture is updated, this event will fire to update the scale and frame - * - * @method onTextureUpdate - * @param event - * @private - */ - -PIXI.Strip.prototype.onTextureUpdate = function() -{ - this.updateFrame = true; -}; - -/** - * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account. - * - * @method getBounds - * @param matrix {Matrix} the transformation matrix of the sprite - * @return {Rectangle} the framing rectangle - */ -PIXI.Strip.prototype.getBounds = function(matrix) -{ - var worldTransform = matrix || this.worldTransform; - - var a = worldTransform.a; - var b = worldTransform.b; - var c = worldTransform.c; - var d = worldTransform.d; - var tx = worldTransform.tx; - var ty = worldTransform.ty; - - var maxX = -Infinity; - var maxY = -Infinity; - - var minX = Infinity; - var minY = Infinity; - - var vertices = this.vertices; - for (var i = 0, n = vertices.length; i < n; i += 2) - { - var rawX = vertices[i], rawY = vertices[i + 1]; - var x = (a * rawX) + (c * rawY) + tx; - var y = (d * rawY) + (b * rawX) + ty; - - minX = x < minX ? x : minX; - minY = y < minY ? y : minY; - - maxX = x > maxX ? x : maxX; - maxY = y > maxY ? y : maxY; - } - - if (minX === -Infinity || maxY === Infinity) - { - return PIXI.EmptyRectangle; - } - - var bounds = this._bounds; - - bounds.x = minX; - bounds.width = maxX - minX; - - bounds.y = minY; - bounds.height = maxY - minY; - - // store a reference so that if this function gets called again in the render cycle we do not have to recalculate - this._currentBounds = bounds; - - return bounds; -}; - -/** - * Different drawing buffer modes supported - * - * @property - * @type {{TRIANGLE_STRIP: number, TRIANGLES: number}} - * @static - */ -PIXI.Strip.DrawModes = { - TRIANGLE_STRIP: 0, - TRIANGLES: 1 -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - * @copyright Mat Groves, Rovanion Luckey - */ - -/** - * - * @class Rope - * @constructor - * @extends Strip - * @param {Texture} texture - The texture to use on the rope. - * @param {Array} points - An array of {PIXI.Point}. - * - */ -PIXI.Rope = function(texture, points) -{ - PIXI.Strip.call( this, texture ); - this.points = points; - - this.vertices = new PIXI.Float32Array(points.length * 4); - this.uvs = new PIXI.Float32Array(points.length * 4); - this.colors = new PIXI.Float32Array(points.length * 2); - this.indices = new PIXI.Uint16Array(points.length * 2); - - - this.refresh(); -}; - - -// constructor -PIXI.Rope.prototype = Object.create( PIXI.Strip.prototype ); -PIXI.Rope.prototype.constructor = PIXI.Rope; - -/* - * Refreshes - * - * @method refresh - */ -PIXI.Rope.prototype.refresh = function() -{ - var points = this.points; - if(points.length < 1) return; - - var uvs = this.uvs; - - var lastPoint = points[0]; - var indices = this.indices; - var colors = this.colors; - - this.count-=0.2; - - uvs[0] = 0; - uvs[1] = 0; - uvs[2] = 0; - uvs[3] = 1; - - colors[0] = 1; - colors[1] = 1; - - indices[0] = 0; - indices[1] = 1; - - var total = points.length, - point, index, amount; - - for (var i = 1; i < total; i++) - { - point = points[i]; - index = i * 4; - // time to do some smart drawing! - amount = i / (total-1); - - if(i%2) - { - uvs[index] = amount; - uvs[index+1] = 0; - - uvs[index+2] = amount; - uvs[index+3] = 1; - } - else - { - uvs[index] = amount; - uvs[index+1] = 0; - - uvs[index+2] = amount; - uvs[index+3] = 1; - } - - index = i * 2; - colors[index] = 1; - colors[index+1] = 1; - - index = i * 2; - indices[index] = index; - indices[index + 1] = index + 1; - - lastPoint = point; - } -}; - -/* - * Updates the object transform for rendering - * - * @method updateTransform - * @private - */ -PIXI.Rope.prototype.updateTransform = function() -{ - - var points = this.points; - if(points.length < 1)return; - - var lastPoint = points[0]; - var nextPoint; - var perp = {x:0, y:0}; - - this.count-=0.2; - - var vertices = this.vertices; - var total = points.length, - point, index, ratio, perpLength, num; - - for (var i = 0; i < total; i++) - { - point = points[i]; - index = i * 4; - - if(i < points.length-1) - { - nextPoint = points[i+1]; - } - else - { - nextPoint = point; - } - - perp.y = -(nextPoint.x - lastPoint.x); - perp.x = nextPoint.y - lastPoint.y; - - ratio = (1 - (i / (total-1))) * 10; - - if(ratio > 1) ratio = 1; - - perpLength = Math.sqrt(perp.x * perp.x + perp.y * perp.y); - num = this.texture.height / 2; //(20 + Math.abs(Math.sin((i + this.count) * 0.3) * 50) )* ratio; - perp.x /= perpLength; - perp.y /= perpLength; - - perp.x *= num; - perp.y *= num; - - vertices[index] = point.x + perp.x; - vertices[index+1] = point.y + perp.y; - vertices[index+2] = point.x - perp.x; - vertices[index+3] = point.y - perp.y; - - lastPoint = point; - } - - PIXI.DisplayObjectContainer.prototype.updateTransform.call( this ); -}; -/* - * Sets the texture that the Rope will use - * - * @method setTexture - * @param texture {Texture} the texture that will be used - */ -PIXI.Rope.prototype.setTexture = function(texture) -{ - // stop current texture - this.texture = texture; - //this.updateFrame = true; -}; - -/** - * @author Mat Groves http://matgroves.com/ - */ - -/** - * A tiling sprite is a fast way of rendering a tiling image - * - * @class TilingSprite - * @extends Sprite - * @constructor - * @param texture {Texture} the texture of the tiling sprite - * @param width {Number} the width of the tiling sprite - * @param height {Number} the height of the tiling sprite - */ -PIXI.TilingSprite = function(texture, width, height) -{ - PIXI.Sprite.call( this, texture); - - /** - * The with of the tiling sprite - * - * @property width - * @type Number - */ - this._width = width || 100; - - /** - * The height of the tiling sprite - * - * @property height - * @type Number - */ - this._height = height || 100; - - /** - * The scaling of the image that is being tiled - * - * @property tileScale - * @type Point - */ - this.tileScale = new PIXI.Point(1,1); - - /** - * A point that represents the scale of the texture object - * - * @property tileScaleOffset - * @type Point - */ - this.tileScaleOffset = new PIXI.Point(1,1); - - /** - * The offset position of the image that is being tiled - * - * @property tilePosition - * @type Point - */ - this.tilePosition = new PIXI.Point(0,0); - - /** - * Whether this sprite is renderable or not - * - * @property renderable - * @type Boolean - * @default true - */ - this.renderable = true; - - /** - * The tint applied to the sprite. This is a hex value - * - * @property tint - * @type Number - * @default 0xFFFFFF - */ - this.tint = 0xFFFFFF; - - /** - * The blend mode to be applied to the sprite - * - * @property blendMode - * @type Number - * @default PIXI.blendModes.NORMAL; - */ - this.blendMode = PIXI.blendModes.NORMAL; - - - -}; - -// constructor -PIXI.TilingSprite.prototype = Object.create(PIXI.Sprite.prototype); -PIXI.TilingSprite.prototype.constructor = PIXI.TilingSprite; - - -/** - * The width of the sprite, setting this will actually modify the scale to achieve the value set - * - * @property width - * @type Number - */ -Object.defineProperty(PIXI.TilingSprite.prototype, 'width', { - get: function() { - return this._width; - }, - set: function(value) { - - this._width = value; - } -}); - -/** - * The height of the TilingSprite, setting this will actually modify the scale to achieve the value set - * - * @property height - * @type Number - */ -Object.defineProperty(PIXI.TilingSprite.prototype, 'height', { - get: function() { - return this._height; - }, - set: function(value) { - this._height = value; - } -}); - -PIXI.TilingSprite.prototype.setTexture = function(texture) -{ - if (this.texture === texture) return; - - this.texture = texture; - - this.refreshTexture = true; - - this.cachedTint = 0xFFFFFF; -}; - -/** -* Renders the object using the WebGL renderer -* -* @method _renderWebGL -* @param renderSession {RenderSession} -* @private -*/ -PIXI.TilingSprite.prototype._renderWebGL = function(renderSession) -{ - if (this.visible === false || this.alpha === 0) return; - var i,j; - - if (this._mask) - { - renderSession.spriteBatch.stop(); - renderSession.maskManager.pushMask(this.mask, renderSession); - renderSession.spriteBatch.start(); - } - - if (this._filters) - { - renderSession.spriteBatch.flush(); - renderSession.filterManager.pushFilter(this._filterBlock); - } - - - - if (!this.tilingTexture || this.refreshTexture) - { - this.generateTilingTexture(true); - - if (this.tilingTexture && this.tilingTexture.needsUpdate) - { - //TODO - tweaking - renderSession.renderer.updateTexture(this.tilingTexture.baseTexture); - this.tilingTexture.needsUpdate = false; - // this.tilingTexture._uvs = null; - } - } - else - { - renderSession.spriteBatch.renderTilingSprite(this); - } - // simple render children! - for (i=0,j=this.children.length; i maxX ? x1 : maxX; - maxX = x2 > maxX ? x2 : maxX; - maxX = x3 > maxX ? x3 : maxX; - maxX = x4 > maxX ? x4 : maxX; - - maxY = y1 > maxY ? y1 : maxY; - maxY = y2 > maxY ? y2 : maxY; - maxY = y3 > maxY ? y3 : maxY; - maxY = y4 > maxY ? y4 : maxY; - - var bounds = this._bounds; - - bounds.x = minX; - bounds.width = maxX - minX; - - bounds.y = minY; - bounds.height = maxY - minY; - - // store a reference so that if this function gets called again in the render cycle we do not have to recalculate - this._currentBounds = bounds; - - return bounds; -}; - - - -/** - * When the texture is updated, this event will fire to update the scale and frame - * - * @method onTextureUpdate - * @param event - * @private - */ -PIXI.TilingSprite.prototype.onTextureUpdate = function() -{ - // overriding the sprite version of this! -}; - - -/** -* -* @method generateTilingTexture -* -* @param forcePowerOfTwo {Boolean} Whether we want to force the texture to be a power of two -*/ -PIXI.TilingSprite.prototype.generateTilingTexture = function(forcePowerOfTwo) -{ - if (!this.texture.baseTexture.hasLoaded) return; - - var texture = this.originalTexture || this.texture; - var frame = texture.frame; - var targetWidth, targetHeight; - - // Check that the frame is the same size as the base texture. - var isFrame = frame.width !== texture.baseTexture.width || frame.height !== texture.baseTexture.height; - - var newTextureRequired = false; - - if (!forcePowerOfTwo) - { - if (isFrame) - { - targetWidth = frame.width; - targetHeight = frame.height; - - newTextureRequired = true; - } - } - else - { - targetWidth = PIXI.getNextPowerOfTwo(frame.width); - targetHeight = PIXI.getNextPowerOfTwo(frame.height); - - // If the BaseTexture dimensions don't match the texture frame then we need a new texture anyway because it's part of a texture atlas - if (frame.width !== targetWidth || frame.height !== targetHeight || texture.baseTexture.width !== targetWidth || texture.baseTexture.height || targetHeight) newTextureRequired = true; - } - - if (newTextureRequired) - { - var canvasBuffer; - - if (this.tilingTexture && this.tilingTexture.isTiling) - { - canvasBuffer = this.tilingTexture.canvasBuffer; - canvasBuffer.resize(targetWidth, targetHeight); - this.tilingTexture.baseTexture.width = targetWidth; - this.tilingTexture.baseTexture.height = targetHeight; - this.tilingTexture.needsUpdate = true; - } - else - { - canvasBuffer = new PIXI.CanvasBuffer(targetWidth, targetHeight); - - this.tilingTexture = PIXI.Texture.fromCanvas(canvasBuffer.canvas); - this.tilingTexture.canvasBuffer = canvasBuffer; - this.tilingTexture.isTiling = true; - } - - canvasBuffer.context.drawImage(texture.baseTexture.source, - texture.crop.x, - texture.crop.y, - texture.crop.width, - texture.crop.height, - 0, - 0, - targetWidth, - targetHeight); - - this.tileScaleOffset.x = frame.width / targetWidth; - this.tileScaleOffset.y = frame.height / targetHeight; - } - else - { - // TODO - switching? - if (this.tilingTexture && this.tilingTexture.isTiling) - { - // destroy the tiling texture! - // TODO could store this somewhere? - this.tilingTexture.destroy(true); - } - - this.tileScaleOffset.x = 1; - this.tileScaleOffset.y = 1; - this.tilingTexture = texture; - } - - this.refreshTexture = false; - - this.originalTexture = this.texture; - this.texture = this.tilingTexture; - - this.tilingTexture.baseTexture._powerOf2 = true; -}; - -PIXI.TilingSprite.prototype.destroy = function () { - PIXI.Sprite.prototype.destroy.call(this); - - this.tileScale = null; - this.tileScaleOffset = null; - this.tilePosition = null; - - this.tilingTexture.destroy(true); - this.tilingTexture = null; -}; - -/****************************************************************************** - * Spine Runtimes Software License - * Version 2.1 - * - * Copyright (c) 2013, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable and - * non-transferable license to install, execute and perform the Spine Runtimes - * Software (the "Software") solely for internal use. Without the written - * permission of Esoteric Software (typically granted by licensing Spine), you - * may not (a) modify, translate, adapt or otherwise create derivative works, - * improvements of the Software or develop new applications using the Software - * or (b) remove, delete, alter or obscure any trademarks or any copyright, - * trademark, patent or other intellectual property or proprietary rights - * notices on or in the Software, including any copy thereof. Redistributions - * in binary or source form must include this license and terms. - * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -var spine = { - radDeg: 180 / Math.PI, - degRad: Math.PI / 180, - temp: [], - Float32Array: (typeof(Float32Array) === 'undefined') ? Array : Float32Array, - Uint16Array: (typeof(Uint16Array) === 'undefined') ? Array : Uint16Array -}; - -spine.BoneData = function (name, parent) { - this.name = name; - this.parent = parent; -}; -spine.BoneData.prototype = { - length: 0, - x: 0, y: 0, - rotation: 0, - scaleX: 1, scaleY: 1, - inheritScale: true, - inheritRotation: true, - flipX: false, flipY: false -}; - -spine.SlotData = function (name, boneData) { - this.name = name; - this.boneData = boneData; -}; -spine.SlotData.prototype = { - r: 1, g: 1, b: 1, a: 1, - attachmentName: null, - additiveBlending: false -}; - -spine.IkConstraintData = function (name) { - this.name = name; - this.bones = []; -}; -spine.IkConstraintData.prototype = { - target: null, - bendDirection: 1, - mix: 1 -}; - -spine.Bone = function (boneData, skeleton, parent) { - this.data = boneData; - this.skeleton = skeleton; - this.parent = parent; - this.setToSetupPose(); -}; -spine.Bone.yDown = false; -spine.Bone.prototype = { - x: 0, y: 0, - rotation: 0, rotationIK: 0, - scaleX: 1, scaleY: 1, - flipX: false, flipY: false, - m00: 0, m01: 0, worldX: 0, // a b x - m10: 0, m11: 0, worldY: 0, // c d y - worldRotation: 0, - worldScaleX: 1, worldScaleY: 1, - worldFlipX: false, worldFlipY: false, - updateWorldTransform: function () { - var parent = this.parent; - if (parent) { - this.worldX = this.x * parent.m00 + this.y * parent.m01 + parent.worldX; - this.worldY = this.x * parent.m10 + this.y * parent.m11 + parent.worldY; - if (this.data.inheritScale) { - this.worldScaleX = parent.worldScaleX * this.scaleX; - this.worldScaleY = parent.worldScaleY * this.scaleY; - } else { - this.worldScaleX = this.scaleX; - this.worldScaleY = this.scaleY; - } - this.worldRotation = this.data.inheritRotation ? (parent.worldRotation + this.rotationIK) : this.rotationIK; - this.worldFlipX = parent.worldFlipX != this.flipX; - this.worldFlipY = parent.worldFlipY != this.flipY; - } else { - var skeletonFlipX = this.skeleton.flipX, skeletonFlipY = this.skeleton.flipY; - this.worldX = skeletonFlipX ? -this.x : this.x; - this.worldY = (skeletonFlipY != spine.Bone.yDown) ? -this.y : this.y; - this.worldScaleX = this.scaleX; - this.worldScaleY = this.scaleY; - this.worldRotation = this.rotationIK; - this.worldFlipX = skeletonFlipX != this.flipX; - this.worldFlipY = skeletonFlipY != this.flipY; - } - var radians = this.worldRotation * spine.degRad; - var cos = Math.cos(radians); - var sin = Math.sin(radians); - if (this.worldFlipX) { - this.m00 = -cos * this.worldScaleX; - this.m01 = sin * this.worldScaleY; - } else { - this.m00 = cos * this.worldScaleX; - this.m01 = -sin * this.worldScaleY; - } - if (this.worldFlipY != spine.Bone.yDown) { - this.m10 = -sin * this.worldScaleX; - this.m11 = -cos * this.worldScaleY; - } else { - this.m10 = sin * this.worldScaleX; - this.m11 = cos * this.worldScaleY; - } - }, - setToSetupPose: function () { - var data = this.data; - this.x = data.x; - this.y = data.y; - this.rotation = data.rotation; - this.rotationIK = this.rotation; - this.scaleX = data.scaleX; - this.scaleY = data.scaleY; - this.flipX = data.flipX; - this.flipY = data.flipY; - }, - worldToLocal: function (world) { - var dx = world[0] - this.worldX, dy = world[1] - this.worldY; - var m00 = this.m00, m10 = this.m10, m01 = this.m01, m11 = this.m11; - if (this.worldFlipX != (this.worldFlipY != spine.Bone.yDown)) { - m00 = -m00; - m11 = -m11; - } - var invDet = 1 / (m00 * m11 - m01 * m10); - world[0] = dx * m00 * invDet - dy * m01 * invDet; - world[1] = dy * m11 * invDet - dx * m10 * invDet; - }, - localToWorld: function (local) { - var localX = local[0], localY = local[1]; - local[0] = localX * this.m00 + localY * this.m01 + this.worldX; - local[1] = localX * this.m10 + localY * this.m11 + this.worldY; - } -}; - -spine.Slot = function (slotData, bone) { - this.data = slotData; - this.bone = bone; - this.setToSetupPose(); -}; -spine.Slot.prototype = { - r: 1, g: 1, b: 1, a: 1, - _attachmentTime: 0, - attachment: null, - attachmentVertices: [], - setAttachment: function (attachment) { - this.attachment = attachment; - this._attachmentTime = this.bone.skeleton.time; - this.attachmentVertices.length = 0; - }, - setAttachmentTime: function (time) { - this._attachmentTime = this.bone.skeleton.time - time; - }, - getAttachmentTime: function () { - return this.bone.skeleton.time - this._attachmentTime; - }, - setToSetupPose: function () { - var data = this.data; - this.r = data.r; - this.g = data.g; - this.b = data.b; - this.a = data.a; - - var slotDatas = this.bone.skeleton.data.slots; - for (var i = 0, n = slotDatas.length; i < n; i++) { - if (slotDatas[i] == data) { - this.setAttachment(!data.attachmentName ? null : this.bone.skeleton.getAttachmentBySlotIndex(i, data.attachmentName)); - break; - } - } - } -}; - -spine.IkConstraint = function (data, skeleton) { - this.data = data; - this.mix = data.mix; - this.bendDirection = data.bendDirection; - - this.bones = []; - for (var i = 0, n = data.bones.length; i < n; i++) - this.bones.push(skeleton.findBone(data.bones[i].name)); - this.target = skeleton.findBone(data.target.name); -}; -spine.IkConstraint.prototype = { - apply: function () { - var target = this.target; - var bones = this.bones; - switch (bones.length) { - case 1: - spine.IkConstraint.apply1(bones[0], target.worldX, target.worldY, this.mix); - break; - case 2: - spine.IkConstraint.apply2(bones[0], bones[1], target.worldX, target.worldY, this.bendDirection, this.mix); - break; - } - } -}; -/** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world - * coordinate system. */ -spine.IkConstraint.apply1 = function (bone, targetX, targetY, alpha) { - var parentRotation = (!bone.data.inheritRotation || !bone.parent) ? 0 : bone.parent.worldRotation; - var rotation = bone.rotation; - var rotationIK = Math.atan2(targetY - bone.worldY, targetX - bone.worldX) * spine.radDeg - parentRotation; - bone.rotationIK = rotation + (rotationIK - rotation) * alpha; -}; -/** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The - * target is specified in the world coordinate system. - * @param child Any descendant bone of the parent. */ -spine.IkConstraint.apply2 = function (parent, child, targetX, targetY, bendDirection, alpha) { - var childRotation = child.rotation, parentRotation = parent.rotation; - if (!alpha) { - child.rotationIK = childRotation; - parent.rotationIK = parentRotation; - return; - } - var positionX, positionY, tempPosition = spine.temp; - var parentParent = parent.parent; - if (parentParent) { - tempPosition[0] = targetX; - tempPosition[1] = targetY; - parentParent.worldToLocal(tempPosition); - targetX = (tempPosition[0] - parent.x) * parentParent.worldScaleX; - targetY = (tempPosition[1] - parent.y) * parentParent.worldScaleY; - } else { - targetX -= parent.x; - targetY -= parent.y; - } - if (child.parent == parent) { - positionX = child.x; - positionY = child.y; - } else { - tempPosition[0] = child.x; - tempPosition[1] = child.y; - child.parent.localToWorld(tempPosition); - parent.worldToLocal(tempPosition); - positionX = tempPosition[0]; - positionY = tempPosition[1]; - } - var childX = positionX * parent.worldScaleX, childY = positionY * parent.worldScaleY; - var offset = Math.atan2(childY, childX); - var len1 = Math.sqrt(childX * childX + childY * childY), len2 = child.data.length * child.worldScaleX; - // Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/ - var cosDenom = 2 * len1 * len2; - if (cosDenom < 0.0001) { - child.rotationIK = childRotation + (Math.atan2(targetY, targetX) * spine.radDeg - parentRotation - childRotation) * alpha; - return; - } - var cos = (targetX * targetX + targetY * targetY - len1 * len1 - len2 * len2) / cosDenom; - if (cos < -1) - cos = -1; - else if (cos > 1) - cos = 1; - var childAngle = Math.acos(cos) * bendDirection; - var adjacent = len1 + len2 * cos, opposite = len2 * Math.sin(childAngle); - var parentAngle = Math.atan2(targetY * adjacent - targetX * opposite, targetX * adjacent + targetY * opposite); - var rotation = (parentAngle - offset) * spine.radDeg - parentRotation; - if (rotation > 180) - rotation -= 360; - else if (rotation < -180) // - rotation += 360; - parent.rotationIK = parentRotation + rotation * alpha; - rotation = (childAngle + offset) * spine.radDeg - childRotation; - if (rotation > 180) - rotation -= 360; - else if (rotation < -180) // - rotation += 360; - child.rotationIK = childRotation + (rotation + parent.worldRotation - child.parent.worldRotation) * alpha; -}; - -spine.Skin = function (name) { - this.name = name; - this.attachments = {}; -}; -spine.Skin.prototype = { - addAttachment: function (slotIndex, name, attachment) { - this.attachments[slotIndex + ":" + name] = attachment; - }, - getAttachment: function (slotIndex, name) { - return this.attachments[slotIndex + ":" + name]; - }, - _attachAll: function (skeleton, oldSkin) { - for (var key in oldSkin.attachments) { - var colon = key.indexOf(":"); - var slotIndex = parseInt(key.substring(0, colon)); - var name = key.substring(colon + 1); - var slot = skeleton.slots[slotIndex]; - if (slot.attachment && slot.attachment.name == name) { - var attachment = this.getAttachment(slotIndex, name); - if (attachment) slot.setAttachment(attachment); - } - } - } -}; - -spine.Animation = function (name, timelines, duration) { - this.name = name; - this.timelines = timelines; - this.duration = duration; -}; -spine.Animation.prototype = { - apply: function (skeleton, lastTime, time, loop, events) { - if (loop && this.duration != 0) { - time %= this.duration; - lastTime %= this.duration; - } - var timelines = this.timelines; - for (var i = 0, n = timelines.length; i < n; i++) - timelines[i].apply(skeleton, lastTime, time, events, 1); - }, - mix: function (skeleton, lastTime, time, loop, events, alpha) { - if (loop && this.duration != 0) { - time %= this.duration; - lastTime %= this.duration; - } - var timelines = this.timelines; - for (var i = 0, n = timelines.length; i < n; i++) - timelines[i].apply(skeleton, lastTime, time, events, alpha); - } -}; -spine.Animation.binarySearch = function (values, target, step) { - var low = 0; - var high = Math.floor(values.length / step) - 2; - if (!high) return step; - var current = high >>> 1; - while (true) { - if (values[(current + 1) * step] <= target) - low = current + 1; - else - high = current; - if (low == high) return (low + 1) * step; - current = (low + high) >>> 1; - } -}; -spine.Animation.binarySearch1 = function (values, target) { - var low = 0; - var high = values.length - 2; - if (!high) return 1; - var current = high >>> 1; - while (true) { - if (values[current + 1] <= target) - low = current + 1; - else - high = current; - if (low == high) return low + 1; - current = (low + high) >>> 1; - } -}; -spine.Animation.linearSearch = function (values, target, step) { - for (var i = 0, last = values.length - step; i <= last; i += step) - if (values[i] > target) return i; - return -1; -}; - -spine.Curves = function (frameCount) { - this.curves = []; // type, x, y, ... - //this.curves.length = (frameCount - 1) * 19/*BEZIER_SIZE*/; -}; -spine.Curves.prototype = { - setLinear: function (frameIndex) { - this.curves[frameIndex * 19/*BEZIER_SIZE*/] = 0/*LINEAR*/; - }, - setStepped: function (frameIndex) { - this.curves[frameIndex * 19/*BEZIER_SIZE*/] = 1/*STEPPED*/; - }, - /** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. - * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of - * the difference between the keyframe's values. */ - setCurve: function (frameIndex, cx1, cy1, cx2, cy2) { - var subdiv1 = 1 / 10/*BEZIER_SEGMENTS*/, subdiv2 = subdiv1 * subdiv1, subdiv3 = subdiv2 * subdiv1; - var pre1 = 3 * subdiv1, pre2 = 3 * subdiv2, pre4 = 6 * subdiv2, pre5 = 6 * subdiv3; - var tmp1x = -cx1 * 2 + cx2, tmp1y = -cy1 * 2 + cy2, tmp2x = (cx1 - cx2) * 3 + 1, tmp2y = (cy1 - cy2) * 3 + 1; - var dfx = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv3, dfy = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv3; - var ddfx = tmp1x * pre4 + tmp2x * pre5, ddfy = tmp1y * pre4 + tmp2y * pre5; - var dddfx = tmp2x * pre5, dddfy = tmp2y * pre5; - - var i = frameIndex * 19/*BEZIER_SIZE*/; - var curves = this.curves; - curves[i++] = 2/*BEZIER*/; - - var x = dfx, y = dfy; - for (var n = i + 19/*BEZIER_SIZE*/ - 1; i < n; i += 2) { - curves[i] = x; - curves[i + 1] = y; - dfx += ddfx; - dfy += ddfy; - ddfx += dddfx; - ddfy += dddfy; - x += dfx; - y += dfy; - } - }, - getCurvePercent: function (frameIndex, percent) { - percent = percent < 0 ? 0 : (percent > 1 ? 1 : percent); - var curves = this.curves; - var i = frameIndex * 19/*BEZIER_SIZE*/; - var type = curves[i]; - if (type === 0/*LINEAR*/) return percent; - if (type == 1/*STEPPED*/) return 0; - i++; - var x = 0; - for (var start = i, n = i + 19/*BEZIER_SIZE*/ - 1; i < n; i += 2) { - x = curves[i]; - if (x >= percent) { - var prevX, prevY; - if (i == start) { - prevX = 0; - prevY = 0; - } else { - prevX = curves[i - 2]; - prevY = curves[i - 1]; - } - return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX); - } - } - var y = curves[i - 1]; - return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. - } -}; - -spine.RotateTimeline = function (frameCount) { - this.curves = new spine.Curves(frameCount); - this.frames = []; // time, angle, ... - this.frames.length = frameCount * 2; -}; -spine.RotateTimeline.prototype = { - boneIndex: 0, - getFrameCount: function () { - return this.frames.length / 2; - }, - setFrame: function (frameIndex, time, angle) { - frameIndex *= 2; - this.frames[frameIndex] = time; - this.frames[frameIndex + 1] = angle; - }, - apply: function (skeleton, lastTime, time, firedEvents, alpha) { - var frames = this.frames; - if (time < frames[0]) return; // Time is before first frame. - - var bone = skeleton.bones[this.boneIndex]; - - if (time >= frames[frames.length - 2]) { // Time is after last frame. - var amount = bone.data.rotation + frames[frames.length - 1] - bone.rotation; - while (amount > 180) - amount -= 360; - while (amount < -180) - amount += 360; - bone.rotation += amount * alpha; - return; - } - - // Interpolate between the previous frame and the current frame. - var frameIndex = spine.Animation.binarySearch(frames, time, 2); - var prevFrameValue = frames[frameIndex - 1]; - var frameTime = frames[frameIndex]; - var percent = 1 - (time - frameTime) / (frames[frameIndex - 2/*PREV_FRAME_TIME*/] - frameTime); - percent = this.curves.getCurvePercent(frameIndex / 2 - 1, percent); - - var amount = frames[frameIndex + 1/*FRAME_VALUE*/] - prevFrameValue; - while (amount > 180) - amount -= 360; - while (amount < -180) - amount += 360; - amount = bone.data.rotation + (prevFrameValue + amount * percent) - bone.rotation; - while (amount > 180) - amount -= 360; - while (amount < -180) - amount += 360; - bone.rotation += amount * alpha; - } -}; - -spine.TranslateTimeline = function (frameCount) { - this.curves = new spine.Curves(frameCount); - this.frames = []; // time, x, y, ... - this.frames.length = frameCount * 3; -}; -spine.TranslateTimeline.prototype = { - boneIndex: 0, - getFrameCount: function () { - return this.frames.length / 3; - }, - setFrame: function (frameIndex, time, x, y) { - frameIndex *= 3; - this.frames[frameIndex] = time; - this.frames[frameIndex + 1] = x; - this.frames[frameIndex + 2] = y; - }, - apply: function (skeleton, lastTime, time, firedEvents, alpha) { - var frames = this.frames; - if (time < frames[0]) return; // Time is before first frame. - - var bone = skeleton.bones[this.boneIndex]; - - if (time >= frames[frames.length - 3]) { // Time is after last frame. - bone.x += (bone.data.x + frames[frames.length - 2] - bone.x) * alpha; - bone.y += (bone.data.y + frames[frames.length - 1] - bone.y) * alpha; - return; - } - - // Interpolate between the previous frame and the current frame. - var frameIndex = spine.Animation.binarySearch(frames, time, 3); - var prevFrameX = frames[frameIndex - 2]; - var prevFrameY = frames[frameIndex - 1]; - var frameTime = frames[frameIndex]; - var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime); - percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); - - bone.x += (bone.data.x + prevFrameX + (frames[frameIndex + 1/*FRAME_X*/] - prevFrameX) * percent - bone.x) * alpha; - bone.y += (bone.data.y + prevFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - prevFrameY) * percent - bone.y) * alpha; - } -}; - -spine.ScaleTimeline = function (frameCount) { - this.curves = new spine.Curves(frameCount); - this.frames = []; // time, x, y, ... - this.frames.length = frameCount * 3; -}; -spine.ScaleTimeline.prototype = { - boneIndex: 0, - getFrameCount: function () { - return this.frames.length / 3; - }, - setFrame: function (frameIndex, time, x, y) { - frameIndex *= 3; - this.frames[frameIndex] = time; - this.frames[frameIndex + 1] = x; - this.frames[frameIndex + 2] = y; - }, - apply: function (skeleton, lastTime, time, firedEvents, alpha) { - var frames = this.frames; - if (time < frames[0]) return; // Time is before first frame. - - var bone = skeleton.bones[this.boneIndex]; - - if (time >= frames[frames.length - 3]) { // Time is after last frame. - bone.scaleX += (bone.data.scaleX * frames[frames.length - 2] - bone.scaleX) * alpha; - bone.scaleY += (bone.data.scaleY * frames[frames.length - 1] - bone.scaleY) * alpha; - return; - } - - // Interpolate between the previous frame and the current frame. - var frameIndex = spine.Animation.binarySearch(frames, time, 3); - var prevFrameX = frames[frameIndex - 2]; - var prevFrameY = frames[frameIndex - 1]; - var frameTime = frames[frameIndex]; - var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime); - percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); - - bone.scaleX += (bone.data.scaleX * (prevFrameX + (frames[frameIndex + 1/*FRAME_X*/] - prevFrameX) * percent) - bone.scaleX) * alpha; - bone.scaleY += (bone.data.scaleY * (prevFrameY + (frames[frameIndex + 2/*FRAME_Y*/] - prevFrameY) * percent) - bone.scaleY) * alpha; - } -}; - -spine.ColorTimeline = function (frameCount) { - this.curves = new spine.Curves(frameCount); - this.frames = []; // time, r, g, b, a, ... - this.frames.length = frameCount * 5; -}; -spine.ColorTimeline.prototype = { - slotIndex: 0, - getFrameCount: function () { - return this.frames.length / 5; - }, - setFrame: function (frameIndex, time, r, g, b, a) { - frameIndex *= 5; - this.frames[frameIndex] = time; - this.frames[frameIndex + 1] = r; - this.frames[frameIndex + 2] = g; - this.frames[frameIndex + 3] = b; - this.frames[frameIndex + 4] = a; - }, - apply: function (skeleton, lastTime, time, firedEvents, alpha) { - var frames = this.frames; - if (time < frames[0]) return; // Time is before first frame. - - var r, g, b, a; - if (time >= frames[frames.length - 5]) { - // Time is after last frame. - var i = frames.length - 1; - r = frames[i - 3]; - g = frames[i - 2]; - b = frames[i - 1]; - a = frames[i]; - } else { - // Interpolate between the previous frame and the current frame. - var frameIndex = spine.Animation.binarySearch(frames, time, 5); - var prevFrameR = frames[frameIndex - 4]; - var prevFrameG = frames[frameIndex - 3]; - var prevFrameB = frames[frameIndex - 2]; - var prevFrameA = frames[frameIndex - 1]; - var frameTime = frames[frameIndex]; - var percent = 1 - (time - frameTime) / (frames[frameIndex - 5/*PREV_FRAME_TIME*/] - frameTime); - percent = this.curves.getCurvePercent(frameIndex / 5 - 1, percent); - - r = prevFrameR + (frames[frameIndex + 1/*FRAME_R*/] - prevFrameR) * percent; - g = prevFrameG + (frames[frameIndex + 2/*FRAME_G*/] - prevFrameG) * percent; - b = prevFrameB + (frames[frameIndex + 3/*FRAME_B*/] - prevFrameB) * percent; - a = prevFrameA + (frames[frameIndex + 4/*FRAME_A*/] - prevFrameA) * percent; - } - var slot = skeleton.slots[this.slotIndex]; - if (alpha < 1) { - slot.r += (r - slot.r) * alpha; - slot.g += (g - slot.g) * alpha; - slot.b += (b - slot.b) * alpha; - slot.a += (a - slot.a) * alpha; - } else { - slot.r = r; - slot.g = g; - slot.b = b; - slot.a = a; - } - } -}; - -spine.AttachmentTimeline = function (frameCount) { - this.curves = new spine.Curves(frameCount); - this.frames = []; // time, ... - this.frames.length = frameCount; - this.attachmentNames = []; - this.attachmentNames.length = frameCount; -}; -spine.AttachmentTimeline.prototype = { - slotIndex: 0, - getFrameCount: function () { - return this.frames.length; - }, - setFrame: function (frameIndex, time, attachmentName) { - this.frames[frameIndex] = time; - this.attachmentNames[frameIndex] = attachmentName; - }, - apply: function (skeleton, lastTime, time, firedEvents, alpha) { - var frames = this.frames; - if (time < frames[0]) { - if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0); - return; - } else if (lastTime > time) // - lastTime = -1; - - var frameIndex = time >= frames[frames.length - 1] ? frames.length - 1 : spine.Animation.binarySearch1(frames, time) - 1; - if (frames[frameIndex] < lastTime) return; - - var attachmentName = this.attachmentNames[frameIndex]; - skeleton.slots[this.slotIndex].setAttachment( - !attachmentName ? null : skeleton.getAttachmentBySlotIndex(this.slotIndex, attachmentName)); - } -}; - -spine.EventTimeline = function (frameCount) { - this.frames = []; // time, ... - this.frames.length = frameCount; - this.events = []; - this.events.length = frameCount; -}; -spine.EventTimeline.prototype = { - getFrameCount: function () { - return this.frames.length; - }, - setFrame: function (frameIndex, time, event) { - this.frames[frameIndex] = time; - this.events[frameIndex] = event; - }, - /** Fires events for frames > lastTime and <= time. */ - apply: function (skeleton, lastTime, time, firedEvents, alpha) { - if (!firedEvents) return; - - var frames = this.frames; - var frameCount = frames.length; - - if (lastTime > time) { // Fire events after last time for looped animations. - this.apply(skeleton, lastTime, Number.MAX_VALUE, firedEvents, alpha); - lastTime = -1; - } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame. - return; - if (time < frames[0]) return; // Time is before first frame. - - var frameIndex; - if (lastTime < frames[0]) - frameIndex = 0; - else { - frameIndex = spine.Animation.binarySearch1(frames, lastTime); - var frame = frames[frameIndex]; - while (frameIndex > 0) { // Fire multiple events with the same frame. - if (frames[frameIndex - 1] != frame) break; - frameIndex--; - } - } - var events = this.events; - for (; frameIndex < frameCount && time >= frames[frameIndex]; frameIndex++) - firedEvents.push(events[frameIndex]); - } -}; - -spine.DrawOrderTimeline = function (frameCount) { - this.frames = []; // time, ... - this.frames.length = frameCount; - this.drawOrders = []; - this.drawOrders.length = frameCount; -}; -spine.DrawOrderTimeline.prototype = { - getFrameCount: function () { - return this.frames.length; - }, - setFrame: function (frameIndex, time, drawOrder) { - this.frames[frameIndex] = time; - this.drawOrders[frameIndex] = drawOrder; - }, - apply: function (skeleton, lastTime, time, firedEvents, alpha) { - var frames = this.frames; - if (time < frames[0]) return; // Time is before first frame. - - var frameIndex; - if (time >= frames[frames.length - 1]) // Time is after last frame. - frameIndex = frames.length - 1; - else - frameIndex = spine.Animation.binarySearch1(frames, time) - 1; - - var drawOrder = skeleton.drawOrder; - var slots = skeleton.slots; - var drawOrderToSetupIndex = this.drawOrders[frameIndex]; - if (!drawOrderToSetupIndex) { - for (var i = 0, n = slots.length; i < n; i++) - drawOrder[i] = slots[i]; - } else { - for (var i = 0, n = drawOrderToSetupIndex.length; i < n; i++) - drawOrder[i] = skeleton.slots[drawOrderToSetupIndex[i]]; - } - - } -}; - -spine.FfdTimeline = function (frameCount) { - this.curves = new spine.Curves(frameCount); - this.frames = []; - this.frames.length = frameCount; - this.frameVertices = []; - this.frameVertices.length = frameCount; -}; -spine.FfdTimeline.prototype = { - slotIndex: 0, - attachment: 0, - getFrameCount: function () { - return this.frames.length; - }, - setFrame: function (frameIndex, time, vertices) { - this.frames[frameIndex] = time; - this.frameVertices[frameIndex] = vertices; - }, - apply: function (skeleton, lastTime, time, firedEvents, alpha) { - var slot = skeleton.slots[this.slotIndex]; - if (slot.attachment != this.attachment) return; - - var frames = this.frames; - if (time < frames[0]) return; // Time is before first frame. - - var frameVertices = this.frameVertices; - var vertexCount = frameVertices[0].length; - - var vertices = slot.attachmentVertices; - if (vertices.length != vertexCount) alpha = 1; - vertices.length = vertexCount; - - if (time >= frames[frames.length - 1]) { // Time is after last frame. - var lastVertices = frameVertices[frames.length - 1]; - if (alpha < 1) { - for (var i = 0; i < vertexCount; i++) - vertices[i] += (lastVertices[i] - vertices[i]) * alpha; - } else { - for (var i = 0; i < vertexCount; i++) - vertices[i] = lastVertices[i]; - } - return; - } - - // Interpolate between the previous frame and the current frame. - var frameIndex = spine.Animation.binarySearch1(frames, time); - var frameTime = frames[frameIndex]; - var percent = 1 - (time - frameTime) / (frames[frameIndex - 1] - frameTime); - percent = this.curves.getCurvePercent(frameIndex - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); - - var prevVertices = frameVertices[frameIndex - 1]; - var nextVertices = frameVertices[frameIndex]; - - if (alpha < 1) { - for (var i = 0; i < vertexCount; i++) { - var prev = prevVertices[i]; - vertices[i] += (prev + (nextVertices[i] - prev) * percent - vertices[i]) * alpha; - } - } else { - for (var i = 0; i < vertexCount; i++) { - var prev = prevVertices[i]; - vertices[i] = prev + (nextVertices[i] - prev) * percent; - } - } - } -}; - -spine.IkConstraintTimeline = function (frameCount) { - this.curves = new spine.Curves(frameCount); - this.frames = []; // time, mix, bendDirection, ... - this.frames.length = frameCount * 3; -}; -spine.IkConstraintTimeline.prototype = { - ikConstraintIndex: 0, - getFrameCount: function () { - return this.frames.length / 3; - }, - setFrame: function (frameIndex, time, mix, bendDirection) { - frameIndex *= 3; - this.frames[frameIndex] = time; - this.frames[frameIndex + 1] = mix; - this.frames[frameIndex + 2] = bendDirection; - }, - apply: function (skeleton, lastTime, time, firedEvents, alpha) { - var frames = this.frames; - if (time < frames[0]) return; // Time is before first frame. - - var ikConstraint = skeleton.ikConstraints[this.ikConstraintIndex]; - - if (time >= frames[frames.length - 3]) { // Time is after last frame. - ikConstraint.mix += (frames[frames.length - 2] - ikConstraint.mix) * alpha; - ikConstraint.bendDirection = frames[frames.length - 1]; - return; - } - - // Interpolate between the previous frame and the current frame. - var frameIndex = spine.Animation.binarySearch(frames, time, 3); - var prevFrameMix = frames[frameIndex + -2/*PREV_FRAME_MIX*/]; - var frameTime = frames[frameIndex]; - var percent = 1 - (time - frameTime) / (frames[frameIndex + -3/*PREV_FRAME_TIME*/] - frameTime); - percent = this.curves.getCurvePercent(frameIndex / 3 - 1, percent); - - var mix = prevFrameMix + (frames[frameIndex + 1/*FRAME_MIX*/] - prevFrameMix) * percent; - ikConstraint.mix += (mix - ikConstraint.mix) * alpha; - ikConstraint.bendDirection = frames[frameIndex + -1/*PREV_FRAME_BEND_DIRECTION*/]; - } -}; - -spine.FlipXTimeline = function (frameCount) { - this.curves = new spine.Curves(frameCount); - this.frames = []; // time, flip, ... - this.frames.length = frameCount * 2; -}; -spine.FlipXTimeline.prototype = { - boneIndex: 0, - getFrameCount: function () { - return this.frames.length / 2; - }, - setFrame: function (frameIndex, time, flip) { - frameIndex *= 2; - this.frames[frameIndex] = time; - this.frames[frameIndex + 1] = flip ? 1 : 0; - }, - apply: function (skeleton, lastTime, time, firedEvents, alpha) { - var frames = this.frames; - if (time < frames[0]) { - if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0); - return; - } else if (lastTime > time) // - lastTime = -1; - var frameIndex = (time >= frames[frames.length - 2] ? frames.length : spine.Animation.binarySearch(frames, time, 2)) - 2; - if (frames[frameIndex] < lastTime) return; - skeleton.bones[boneIndex].flipX = frames[frameIndex + 1] != 0; - } -}; - -spine.FlipYTimeline = function (frameCount) { - this.curves = new spine.Curves(frameCount); - this.frames = []; // time, flip, ... - this.frames.length = frameCount * 2; -}; -spine.FlipYTimeline.prototype = { - boneIndex: 0, - getFrameCount: function () { - return this.frames.length / 2; - }, - setFrame: function (frameIndex, time, flip) { - frameIndex *= 2; - this.frames[frameIndex] = time; - this.frames[frameIndex + 1] = flip ? 1 : 0; - }, - apply: function (skeleton, lastTime, time, firedEvents, alpha) { - var frames = this.frames; - if (time < frames[0]) { - if (lastTime > time) this.apply(skeleton, lastTime, Number.MAX_VALUE, null, 0); - return; - } else if (lastTime > time) // - lastTime = -1; - var frameIndex = (time >= frames[frames.length - 2] ? frames.length : spine.Animation.binarySearch(frames, time, 2)) - 2; - if (frames[frameIndex] < lastTime) return; - skeleton.bones[boneIndex].flipY = frames[frameIndex + 1] != 0; - } -}; - -spine.SkeletonData = function () { - this.bones = []; - this.slots = []; - this.skins = []; - this.events = []; - this.animations = []; - this.ikConstraints = []; -}; -spine.SkeletonData.prototype = { - name: null, - defaultSkin: null, - width: 0, height: 0, - version: null, hash: null, - /** @return May be null. */ - findBone: function (boneName) { - var bones = this.bones; - for (var i = 0, n = bones.length; i < n; i++) - if (bones[i].name == boneName) return bones[i]; - return null; - }, - /** @return -1 if the bone was not found. */ - findBoneIndex: function (boneName) { - var bones = this.bones; - for (var i = 0, n = bones.length; i < n; i++) - if (bones[i].name == boneName) return i; - return -1; - }, - /** @return May be null. */ - findSlot: function (slotName) { - var slots = this.slots; - for (var i = 0, n = slots.length; i < n; i++) { - if (slots[i].name == slotName) return slot[i]; - } - return null; - }, - /** @return -1 if the bone was not found. */ - findSlotIndex: function (slotName) { - var slots = this.slots; - for (var i = 0, n = slots.length; i < n; i++) - if (slots[i].name == slotName) return i; - return -1; - }, - /** @return May be null. */ - findSkin: function (skinName) { - var skins = this.skins; - for (var i = 0, n = skins.length; i < n; i++) - if (skins[i].name == skinName) return skins[i]; - return null; - }, - /** @return May be null. */ - findEvent: function (eventName) { - var events = this.events; - for (var i = 0, n = events.length; i < n; i++) - if (events[i].name == eventName) return events[i]; - return null; - }, - /** @return May be null. */ - findAnimation: function (animationName) { - var animations = this.animations; - for (var i = 0, n = animations.length; i < n; i++) - if (animations[i].name == animationName) return animations[i]; - return null; - }, - /** @return May be null. */ - findIkConstraint: function (ikConstraintName) { - var ikConstraints = this.ikConstraints; - for (var i = 0, n = ikConstraints.length; i < n; i++) - if (ikConstraints[i].name == ikConstraintName) return ikConstraints[i]; - return null; - } -}; - -spine.Skeleton = function (skeletonData) { - this.data = skeletonData; - - this.bones = []; - for (var i = 0, n = skeletonData.bones.length; i < n; i++) { - var boneData = skeletonData.bones[i]; - var parent = !boneData.parent ? null : this.bones[skeletonData.bones.indexOf(boneData.parent)]; - this.bones.push(new spine.Bone(boneData, this, parent)); - } - - this.slots = []; - this.drawOrder = []; - for (var i = 0, n = skeletonData.slots.length; i < n; i++) { - var slotData = skeletonData.slots[i]; - var bone = this.bones[skeletonData.bones.indexOf(slotData.boneData)]; - var slot = new spine.Slot(slotData, bone); - this.slots.push(slot); - this.drawOrder.push(slot); - } - - this.ikConstraints = []; - for (var i = 0, n = skeletonData.ikConstraints.length; i < n; i++) - this.ikConstraints.push(new spine.IkConstraint(skeletonData.ikConstraints[i], this)); - - this.boneCache = []; - this.updateCache(); -}; -spine.Skeleton.prototype = { - x: 0, y: 0, - skin: null, - r: 1, g: 1, b: 1, a: 1, - time: 0, - flipX: false, flipY: false, - /** Caches information about bones and IK constraints. Must be called if bones or IK constraints are added or removed. */ - updateCache: function () { - var ikConstraints = this.ikConstraints; - var ikConstraintsCount = ikConstraints.length; - - var arrayCount = ikConstraintsCount + 1; - var boneCache = this.boneCache; - if (boneCache.length > arrayCount) boneCache.length = arrayCount; - for (var i = 0, n = boneCache.length; i < n; i++) - boneCache[i].length = 0; - while (boneCache.length < arrayCount) - boneCache[boneCache.length] = []; - - var nonIkBones = boneCache[0]; - var bones = this.bones; - - outer: - for (var i = 0, n = bones.length; i < n; i++) { - var bone = bones[i]; - var current = bone; - do { - for (var ii = 0; ii < ikConstraintsCount; ii++) { - var ikConstraint = ikConstraints[ii]; - var parent = ikConstraint.bones[0]; - var child= ikConstraint.bones[ikConstraint.bones.length - 1]; - while (true) { - if (current == child) { - boneCache[ii].push(bone); - boneCache[ii + 1].push(bone); - continue outer; - } - if (child == parent) break; - child = child.parent; - } - } - current = current.parent; - } while (current); - nonIkBones[nonIkBones.length] = bone; - } - }, - /** Updates the world transform for each bone. */ - updateWorldTransform: function () { - var bones = this.bones; - for (var i = 0, n = bones.length; i < n; i++) { - var bone = bones[i]; - bone.rotationIK = bone.rotation; - } - var i = 0, last = this.boneCache.length - 1; - while (true) { - var cacheBones = this.boneCache[i]; - for (var ii = 0, nn = cacheBones.length; ii < nn; ii++) - cacheBones[ii].updateWorldTransform(); - if (i == last) break; - this.ikConstraints[i].apply(); - i++; - } - }, - /** Sets the bones and slots to their setup pose values. */ - setToSetupPose: function () { - this.setBonesToSetupPose(); - this.setSlotsToSetupPose(); - }, - setBonesToSetupPose: function () { - var bones = this.bones; - for (var i = 0, n = bones.length; i < n; i++) - bones[i].setToSetupPose(); - - var ikConstraints = this.ikConstraints; - for (var i = 0, n = ikConstraints.length; i < n; i++) { - var ikConstraint = ikConstraints[i]; - ikConstraint.bendDirection = ikConstraint.data.bendDirection; - ikConstraint.mix = ikConstraint.data.mix; - } - }, - setSlotsToSetupPose: function () { - var slots = this.slots; - var drawOrder = this.drawOrder; - for (var i = 0, n = slots.length; i < n; i++) { - drawOrder[i] = slots[i]; - slots[i].setToSetupPose(i); - } - }, - /** @return May return null. */ - getRootBone: function () { - return this.bones.length ? this.bones[0] : null; - }, - /** @return May be null. */ - findBone: function (boneName) { - var bones = this.bones; - for (var i = 0, n = bones.length; i < n; i++) - if (bones[i].data.name == boneName) return bones[i]; - return null; - }, - /** @return -1 if the bone was not found. */ - findBoneIndex: function (boneName) { - var bones = this.bones; - for (var i = 0, n = bones.length; i < n; i++) - if (bones[i].data.name == boneName) return i; - return -1; - }, - /** @return May be null. */ - findSlot: function (slotName) { - var slots = this.slots; - for (var i = 0, n = slots.length; i < n; i++) - if (slots[i].data.name == slotName) return slots[i]; - return null; - }, - /** @return -1 if the bone was not found. */ - findSlotIndex: function (slotName) { - var slots = this.slots; - for (var i = 0, n = slots.length; i < n; i++) - if (slots[i].data.name == slotName) return i; - return -1; - }, - setSkinByName: function (skinName) { - var skin = this.data.findSkin(skinName); - if (!skin) throw "Skin not found: " + skinName; - this.setSkin(skin); - }, - /** Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}. - * Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was - * no old skin, each slot's setup mode attachment is attached from the new skin. - * @param newSkin May be null. */ - setSkin: function (newSkin) { - if (newSkin) { - if (this.skin) - newSkin._attachAll(this, this.skin); - else { - var slots = this.slots; - for (var i = 0, n = slots.length; i < n; i++) { - var slot = slots[i]; - var name = slot.data.attachmentName; - if (name) { - var attachment = newSkin.getAttachment(i, name); - if (attachment) slot.setAttachment(attachment); - } - } - } - } - this.skin = newSkin; - }, - /** @return May be null. */ - getAttachmentBySlotName: function (slotName, attachmentName) { - return this.getAttachmentBySlotIndex(this.data.findSlotIndex(slotName), attachmentName); - }, - /** @return May be null. */ - getAttachmentBySlotIndex: function (slotIndex, attachmentName) { - if (this.skin) { - var attachment = this.skin.getAttachment(slotIndex, attachmentName); - if (attachment) return attachment; - } - if (this.data.defaultSkin) return this.data.defaultSkin.getAttachment(slotIndex, attachmentName); - return null; - }, - /** @param attachmentName May be null. */ - setAttachment: function (slotName, attachmentName) { - var slots = this.slots; - for (var i = 0, n = slots.length; i < n; i++) { - var slot = slots[i]; - if (slot.data.name == slotName) { - var attachment = null; - if (attachmentName) { - attachment = this.getAttachmentBySlotIndex(i, attachmentName); - if (!attachment) throw "Attachment not found: " + attachmentName + ", for slot: " + slotName; - } - slot.setAttachment(attachment); - return; - } - } - throw "Slot not found: " + slotName; - }, - /** @return May be null. */ - findIkConstraint: function (ikConstraintName) { - var ikConstraints = this.ikConstraints; - for (var i = 0, n = ikConstraints.length; i < n; i++) - if (ikConstraints[i].data.name == ikConstraintName) return ikConstraints[i]; - return null; - }, - update: function (delta) { - this.time += delta; - } -}; - -spine.EventData = function (name) { - this.name = name; -}; -spine.EventData.prototype = { - intValue: 0, - floatValue: 0, - stringValue: null -}; - -spine.Event = function (data) { - this.data = data; -}; -spine.Event.prototype = { - intValue: 0, - floatValue: 0, - stringValue: null -}; - -spine.AttachmentType = { - region: 0, - boundingbox: 1, - mesh: 2, - skinnedmesh: 3 -}; - -spine.RegionAttachment = function (name) { - this.name = name; - this.offset = []; - this.offset.length = 8; - this.uvs = []; - this.uvs.length = 8; -}; -spine.RegionAttachment.prototype = { - type: spine.AttachmentType.region, - x: 0, y: 0, - rotation: 0, - scaleX: 1, scaleY: 1, - width: 0, height: 0, - r: 1, g: 1, b: 1, a: 1, - path: null, - rendererObject: null, - regionOffsetX: 0, regionOffsetY: 0, - regionWidth: 0, regionHeight: 0, - regionOriginalWidth: 0, regionOriginalHeight: 0, - setUVs: function (u, v, u2, v2, rotate) { - var uvs = this.uvs; - if (rotate) { - uvs[2/*X2*/] = u; - uvs[3/*Y2*/] = v2; - uvs[4/*X3*/] = u; - uvs[5/*Y3*/] = v; - uvs[6/*X4*/] = u2; - uvs[7/*Y4*/] = v; - uvs[0/*X1*/] = u2; - uvs[1/*Y1*/] = v2; - } else { - uvs[0/*X1*/] = u; - uvs[1/*Y1*/] = v2; - uvs[2/*X2*/] = u; - uvs[3/*Y2*/] = v; - uvs[4/*X3*/] = u2; - uvs[5/*Y3*/] = v; - uvs[6/*X4*/] = u2; - uvs[7/*Y4*/] = v2; - } - }, - updateOffset: function () { - var regionScaleX = this.width / this.regionOriginalWidth * this.scaleX; - var regionScaleY = this.height / this.regionOriginalHeight * this.scaleY; - var localX = -this.width / 2 * this.scaleX + this.regionOffsetX * regionScaleX; - var localY = -this.height / 2 * this.scaleY + this.regionOffsetY * regionScaleY; - var localX2 = localX + this.regionWidth * regionScaleX; - var localY2 = localY + this.regionHeight * regionScaleY; - var radians = this.rotation * spine.degRad; - var cos = Math.cos(radians); - var sin = Math.sin(radians); - var localXCos = localX * cos + this.x; - var localXSin = localX * sin; - var localYCos = localY * cos + this.y; - var localYSin = localY * sin; - var localX2Cos = localX2 * cos + this.x; - var localX2Sin = localX2 * sin; - var localY2Cos = localY2 * cos + this.y; - var localY2Sin = localY2 * sin; - var offset = this.offset; - offset[0/*X1*/] = localXCos - localYSin; - offset[1/*Y1*/] = localYCos + localXSin; - offset[2/*X2*/] = localXCos - localY2Sin; - offset[3/*Y2*/] = localY2Cos + localXSin; - offset[4/*X3*/] = localX2Cos - localY2Sin; - offset[5/*Y3*/] = localY2Cos + localX2Sin; - offset[6/*X4*/] = localX2Cos - localYSin; - offset[7/*Y4*/] = localYCos + localX2Sin; - }, - computeVertices: function (x, y, bone, vertices) { - x += bone.worldX; - y += bone.worldY; - var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11; - var offset = this.offset; - vertices[0/*X1*/] = offset[0/*X1*/] * m00 + offset[1/*Y1*/] * m01 + x; - vertices[1/*Y1*/] = offset[0/*X1*/] * m10 + offset[1/*Y1*/] * m11 + y; - vertices[2/*X2*/] = offset[2/*X2*/] * m00 + offset[3/*Y2*/] * m01 + x; - vertices[3/*Y2*/] = offset[2/*X2*/] * m10 + offset[3/*Y2*/] * m11 + y; - vertices[4/*X3*/] = offset[4/*X3*/] * m00 + offset[5/*X3*/] * m01 + x; - vertices[5/*X3*/] = offset[4/*X3*/] * m10 + offset[5/*X3*/] * m11 + y; - vertices[6/*X4*/] = offset[6/*X4*/] * m00 + offset[7/*Y4*/] * m01 + x; - vertices[7/*Y4*/] = offset[6/*X4*/] * m10 + offset[7/*Y4*/] * m11 + y; - } -}; - -spine.MeshAttachment = function (name) { - this.name = name; -}; -spine.MeshAttachment.prototype = { - type: spine.AttachmentType.mesh, - vertices: null, - uvs: null, - regionUVs: null, - triangles: null, - hullLength: 0, - r: 1, g: 1, b: 1, a: 1, - path: null, - rendererObject: null, - regionU: 0, regionV: 0, regionU2: 0, regionV2: 0, regionRotate: false, - regionOffsetX: 0, regionOffsetY: 0, - regionWidth: 0, regionHeight: 0, - regionOriginalWidth: 0, regionOriginalHeight: 0, - edges: null, - width: 0, height: 0, - updateUVs: function () { - var width = this.regionU2 - this.regionU, height = this.regionV2 - this.regionV; - var n = this.regionUVs.length; - if (!this.uvs || this.uvs.length != n) { - this.uvs = new spine.Float32Array(n); - } - if (this.regionRotate) { - for (var i = 0; i < n; i += 2) { - this.uvs[i] = this.regionU + this.regionUVs[i + 1] * width; - this.uvs[i + 1] = this.regionV + height - this.regionUVs[i] * height; - } - } else { - for (var i = 0; i < n; i += 2) { - this.uvs[i] = this.regionU + this.regionUVs[i] * width; - this.uvs[i + 1] = this.regionV + this.regionUVs[i + 1] * height; - } - } - }, - computeWorldVertices: function (x, y, slot, worldVertices) { - var bone = slot.bone; - x += bone.worldX; - y += bone.worldY; - var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11; - var vertices = this.vertices; - var verticesCount = vertices.length; - if (slot.attachmentVertices.length == verticesCount) vertices = slot.attachmentVertices; - for (var i = 0; i < verticesCount; i += 2) { - var vx = vertices[i]; - var vy = vertices[i + 1]; - worldVertices[i] = vx * m00 + vy * m01 + x; - worldVertices[i + 1] = vx * m10 + vy * m11 + y; - } - } -}; - -spine.SkinnedMeshAttachment = function (name) { - this.name = name; -}; -spine.SkinnedMeshAttachment.prototype = { - type: spine.AttachmentType.skinnedmesh, - bones: null, - weights: null, - uvs: null, - regionUVs: null, - triangles: null, - hullLength: 0, - r: 1, g: 1, b: 1, a: 1, - path: null, - rendererObject: null, - regionU: 0, regionV: 0, regionU2: 0, regionV2: 0, regionRotate: false, - regionOffsetX: 0, regionOffsetY: 0, - regionWidth: 0, regionHeight: 0, - regionOriginalWidth: 0, regionOriginalHeight: 0, - edges: null, - width: 0, height: 0, - updateUVs: function (u, v, u2, v2, rotate) { - var width = this.regionU2 - this.regionU, height = this.regionV2 - this.regionV; - var n = this.regionUVs.length; - if (!this.uvs || this.uvs.length != n) { - this.uvs = new spine.Float32Array(n); - } - if (this.regionRotate) { - for (var i = 0; i < n; i += 2) { - this.uvs[i] = this.regionU + this.regionUVs[i + 1] * width; - this.uvs[i + 1] = this.regionV + height - this.regionUVs[i] * height; - } - } else { - for (var i = 0; i < n; i += 2) { - this.uvs[i] = this.regionU + this.regionUVs[i] * width; - this.uvs[i + 1] = this.regionV + this.regionUVs[i + 1] * height; - } - } - }, - computeWorldVertices: function (x, y, slot, worldVertices) { - var skeletonBones = slot.bone.skeleton.bones; - var weights = this.weights; - var bones = this.bones; - - var w = 0, v = 0, b = 0, f = 0, n = bones.length, nn; - var wx, wy, bone, vx, vy, weight; - if (!slot.attachmentVertices.length) { - for (; v < n; w += 2) { - wx = 0; - wy = 0; - nn = bones[v++] + v; - for (; v < nn; v++, b += 3) { - bone = skeletonBones[bones[v]]; - vx = weights[b]; - vy = weights[b + 1]; - weight = weights[b + 2]; - wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; - wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; - } - worldVertices[w] = wx + x; - worldVertices[w + 1] = wy + y; - } - } else { - var ffd = slot.attachmentVertices; - for (; v < n; w += 2) { - wx = 0; - wy = 0; - nn = bones[v++] + v; - for (; v < nn; v++, b += 3, f += 2) { - bone = skeletonBones[bones[v]]; - vx = weights[b] + ffd[f]; - vy = weights[b + 1] + ffd[f + 1]; - weight = weights[b + 2]; - wx += (vx * bone.m00 + vy * bone.m01 + bone.worldX) * weight; - wy += (vx * bone.m10 + vy * bone.m11 + bone.worldY) * weight; - } - worldVertices[w] = wx + x; - worldVertices[w + 1] = wy + y; - } - } - } -}; - -spine.BoundingBoxAttachment = function (name) { - this.name = name; - this.vertices = []; -}; -spine.BoundingBoxAttachment.prototype = { - type: spine.AttachmentType.boundingbox, - computeWorldVertices: function (x, y, bone, worldVertices) { - x += bone.worldX; - y += bone.worldY; - var m00 = bone.m00, m01 = bone.m01, m10 = bone.m10, m11 = bone.m11; - var vertices = this.vertices; - for (var i = 0, n = vertices.length; i < n; i += 2) { - var px = vertices[i]; - var py = vertices[i + 1]; - worldVertices[i] = px * m00 + py * m01 + x; - worldVertices[i + 1] = px * m10 + py * m11 + y; - } - } -}; - -spine.AnimationStateData = function (skeletonData) { - this.skeletonData = skeletonData; - this.animationToMixTime = {}; -}; -spine.AnimationStateData.prototype = { - defaultMix: 0, - setMixByName: function (fromName, toName, duration) { - var from = this.skeletonData.findAnimation(fromName); - if (!from) throw "Animation not found: " + fromName; - var to = this.skeletonData.findAnimation(toName); - if (!to) throw "Animation not found: " + toName; - this.setMix(from, to, duration); - }, - setMix: function (from, to, duration) { - this.animationToMixTime[from.name + ":" + to.name] = duration; - }, - getMix: function (from, to) { - var key = from.name + ":" + to.name; - return this.animationToMixTime.hasOwnProperty(key) ? this.animationToMixTime[key] : this.defaultMix; - } -}; - -spine.TrackEntry = function () {}; -spine.TrackEntry.prototype = { - next: null, previous: null, - animation: null, - loop: false, - delay: 0, time: 0, lastTime: -1, endTime: 0, - timeScale: 1, - mixTime: 0, mixDuration: 0, mix: 1, - onStart: null, onEnd: null, onComplete: null, onEvent: null -}; - -spine.AnimationState = function (stateData) { - this.data = stateData; - this.tracks = []; - this.events = []; -}; -spine.AnimationState.prototype = { - onStart: null, - onEnd: null, - onComplete: null, - onEvent: null, - timeScale: 1, - update: function (delta) { - delta *= this.timeScale; - for (var i = 0; i < this.tracks.length; i++) { - var current = this.tracks[i]; - if (!current) continue; - - current.time += delta * current.timeScale; - if (current.previous) { - var previousDelta = delta * current.previous.timeScale; - current.previous.time += previousDelta; - current.mixTime += previousDelta; - } - - var next = current.next; - if (next) { - next.time = current.lastTime - next.delay; - if (next.time >= 0) this.setCurrent(i, next); - } else { - // End non-looping animation when it reaches its end time and there is no next entry. - if (!current.loop && current.lastTime >= current.endTime) this.clearTrack(i); - } - } - }, - apply: function (skeleton) { - for (var i = 0; i < this.tracks.length; i++) { - var current = this.tracks[i]; - if (!current) continue; - - this.events.length = 0; - - var time = current.time; - var lastTime = current.lastTime; - var endTime = current.endTime; - var loop = current.loop; - if (!loop && time > endTime) time = endTime; - - var previous = current.previous; - if (!previous) { - if (current.mix == 1) - current.animation.apply(skeleton, current.lastTime, time, loop, this.events); - else - current.animation.mix(skeleton, current.lastTime, time, loop, this.events, current.mix); - } else { - var previousTime = previous.time; - if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime; - previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null); - - var alpha = current.mixTime / current.mixDuration * current.mix; - if (alpha >= 1) { - alpha = 1; - current.previous = null; - } - current.animation.mix(skeleton, current.lastTime, time, loop, this.events, alpha); - } - - for (var ii = 0, nn = this.events.length; ii < nn; ii++) { - var event = this.events[ii]; - if (current.onEvent) current.onEvent(i, event); - if (this.onEvent) this.onEvent(i, event); - } - - // Check if completed the animation or a loop iteration. - if (loop ? (lastTime % endTime > time % endTime) : (lastTime < endTime && time >= endTime)) { - var count = Math.floor(time / endTime); - if (current.onComplete) current.onComplete(i, count); - if (this.onComplete) this.onComplete(i, count); - } - - current.lastTime = current.time; - } - }, - clearTracks: function () { - for (var i = 0, n = this.tracks.length; i < n; i++) - this.clearTrack(i); - this.tracks.length = 0; - }, - clearTrack: function (trackIndex) { - if (trackIndex >= this.tracks.length) return; - var current = this.tracks[trackIndex]; - if (!current) return; - - if (current.onEnd) current.onEnd(trackIndex); - if (this.onEnd) this.onEnd(trackIndex); - - this.tracks[trackIndex] = null; - }, - _expandToIndex: function (index) { - if (index < this.tracks.length) return this.tracks[index]; - while (index >= this.tracks.length) - this.tracks.push(null); - return null; - }, - setCurrent: function (index, entry) { - var current = this._expandToIndex(index); - if (current) { - var previous = current.previous; - current.previous = null; - - if (current.onEnd) current.onEnd(index); - if (this.onEnd) this.onEnd(index); - - entry.mixDuration = this.data.getMix(current.animation, entry.animation); - if (entry.mixDuration > 0) { - entry.mixTime = 0; - // If a mix is in progress, mix from the closest animation. - if (previous && current.mixTime / current.mixDuration < 0.5) - entry.previous = previous; - else - entry.previous = current; - } - } - - this.tracks[index] = entry; - - if (entry.onStart) entry.onStart(index); - if (this.onStart) this.onStart(index); - }, - setAnimationByName: function (trackIndex, animationName, loop) { - var animation = this.data.skeletonData.findAnimation(animationName); - if (!animation) throw "Animation not found: " + animationName; - return this.setAnimation(trackIndex, animation, loop); - }, - /** Set the current animation. Any queued animations are cleared. */ - setAnimation: function (trackIndex, animation, loop) { - var entry = new spine.TrackEntry(); - entry.animation = animation; - entry.loop = loop; - entry.endTime = animation.duration; - this.setCurrent(trackIndex, entry); - return entry; - }, - addAnimationByName: function (trackIndex, animationName, loop, delay) { - var animation = this.data.skeletonData.findAnimation(animationName); - if (!animation) throw "Animation not found: " + animationName; - return this.addAnimation(trackIndex, animation, loop, delay); - }, - /** Adds an animation to be played delay seconds after the current or last queued animation. - * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ - addAnimation: function (trackIndex, animation, loop, delay) { - var entry = new spine.TrackEntry(); - entry.animation = animation; - entry.loop = loop; - entry.endTime = animation.duration; - - var last = this._expandToIndex(trackIndex); - if (last) { - while (last.next) - last = last.next; - last.next = entry; - } else - this.tracks[trackIndex] = entry; - - if (delay <= 0) { - if (last) - delay += last.endTime - this.data.getMix(last.animation, animation); - else - delay = 0; - } - entry.delay = delay; - - return entry; - }, - /** May be null. */ - getCurrent: function (trackIndex) { - if (trackIndex >= this.tracks.length) return null; - return this.tracks[trackIndex]; - } -}; - -spine.SkeletonJson = function (attachmentLoader) { - this.attachmentLoader = attachmentLoader; -}; -spine.SkeletonJson.prototype = { - scale: 1, - readSkeletonData: function (root, name) { - var skeletonData = new spine.SkeletonData(); - skeletonData.name = name; - - // Skeleton. - var skeletonMap = root["skeleton"]; - if (skeletonMap) { - skeletonData.hash = skeletonMap["hash"]; - skeletonData.version = skeletonMap["spine"]; - skeletonData.width = skeletonMap["width"] || 0; - skeletonData.height = skeletonMap["height"] || 0; - } - - // Bones. - var bones = root["bones"]; - for (var i = 0, n = bones.length; i < n; i++) { - var boneMap = bones[i]; - var parent = null; - if (boneMap["parent"]) { - parent = skeletonData.findBone(boneMap["parent"]); - if (!parent) throw "Parent bone not found: " + boneMap["parent"]; - } - var boneData = new spine.BoneData(boneMap["name"], parent); - boneData.length = (boneMap["length"] || 0) * this.scale; - boneData.x = (boneMap["x"] || 0) * this.scale; - boneData.y = (boneMap["y"] || 0) * this.scale; - boneData.rotation = (boneMap["rotation"] || 0); - boneData.scaleX = boneMap.hasOwnProperty("scaleX") ? boneMap["scaleX"] : 1; - boneData.scaleY = boneMap.hasOwnProperty("scaleY") ? boneMap["scaleY"] : 1; - boneData.inheritScale = boneMap.hasOwnProperty("inheritScale") ? boneMap["inheritScale"] : true; - boneData.inheritRotation = boneMap.hasOwnProperty("inheritRotation") ? boneMap["inheritRotation"] : true; - skeletonData.bones.push(boneData); - } - - // IK constraints. - var ik = root["ik"]; - if (ik) { - for (var i = 0, n = ik.length; i < n; i++) { - var ikMap = ik[i]; - var ikConstraintData = new spine.IkConstraintData(ikMap["name"]); - - var bones = ikMap["bones"]; - for (var ii = 0, nn = bones.length; ii < nn; ii++) { - var bone = skeletonData.findBone(bones[ii]); - if (!bone) throw "IK bone not found: " + bones[ii]; - ikConstraintData.bones.push(bone); - } - - ikConstraintData.target = skeletonData.findBone(ikMap["target"]); - if (!ikConstraintData.target) throw "Target bone not found: " + ikMap["target"]; - - ikConstraintData.bendDirection = (!ikMap.hasOwnProperty("bendPositive") || ikMap["bendPositive"]) ? 1 : -1; - ikConstraintData.mix = ikMap.hasOwnProperty("mix") ? ikMap["mix"] : 1; - - skeletonData.ikConstraints.push(ikConstraintData); - } - } - - // Slots. - var slots = root["slots"]; - for (var i = 0, n = slots.length; i < n; i++) { - var slotMap = slots[i]; - var boneData = skeletonData.findBone(slotMap["bone"]); - if (!boneData) throw "Slot bone not found: " + slotMap["bone"]; - var slotData = new spine.SlotData(slotMap["name"], boneData); - - var color = slotMap["color"]; - if (color) { - slotData.r = this.toColor(color, 0); - slotData.g = this.toColor(color, 1); - slotData.b = this.toColor(color, 2); - slotData.a = this.toColor(color, 3); - } - - slotData.attachmentName = slotMap["attachment"]; - slotData.additiveBlending = slotMap["additive"] && slotMap["additive"] == "true"; - - skeletonData.slots.push(slotData); - } - - // Skins. - var skins = root["skins"]; - for (var skinName in skins) { - if (!skins.hasOwnProperty(skinName)) continue; - var skinMap = skins[skinName]; - var skin = new spine.Skin(skinName); - for (var slotName in skinMap) { - if (!skinMap.hasOwnProperty(slotName)) continue; - var slotIndex = skeletonData.findSlotIndex(slotName); - var slotEntry = skinMap[slotName]; - for (var attachmentName in slotEntry) { - if (!slotEntry.hasOwnProperty(attachmentName)) continue; - var attachment = this.readAttachment(skin, attachmentName, slotEntry[attachmentName]); - if (attachment) skin.addAttachment(slotIndex, attachmentName, attachment); - } - } - skeletonData.skins.push(skin); - if (skin.name == "default") skeletonData.defaultSkin = skin; - } - - // Events. - var events = root["events"]; - for (var eventName in events) { - if (!events.hasOwnProperty(eventName)) continue; - var eventMap = events[eventName]; - var eventData = new spine.EventData(eventName); - eventData.intValue = eventMap["int"] || 0; - eventData.floatValue = eventMap["float"] || 0; - eventData.stringValue = eventMap["string"] || null; - skeletonData.events.push(eventData); - } - - // Animations. - var animations = root["animations"]; - for (var animationName in animations) { - if (!animations.hasOwnProperty(animationName)) continue; - this.readAnimation(animationName, animations[animationName], skeletonData); - } - - return skeletonData; - }, - readAttachment: function (skin, name, map) { - name = map["name"] || name; - - var type = spine.AttachmentType[map["type"] || "region"]; - var path = map["path"] || name; - - var scale = this.scale; - if (type == spine.AttachmentType.region) { - var region = this.attachmentLoader.newRegionAttachment(skin, name, path); - if (!region) return null; - region.path = path; - region.x = (map["x"] || 0) * scale; - region.y = (map["y"] || 0) * scale; - region.scaleX = map.hasOwnProperty("scaleX") ? map["scaleX"] : 1; - region.scaleY = map.hasOwnProperty("scaleY") ? map["scaleY"] : 1; - region.rotation = map["rotation"] || 0; - region.width = (map["width"] || 0) * scale; - region.height = (map["height"] || 0) * scale; - - var color = map["color"]; - if (color) { - region.r = this.toColor(color, 0); - region.g = this.toColor(color, 1); - region.b = this.toColor(color, 2); - region.a = this.toColor(color, 3); - } - - region.updateOffset(); - return region; - } else if (type == spine.AttachmentType.mesh) { - var mesh = this.attachmentLoader.newMeshAttachment(skin, name, path); - if (!mesh) return null; - mesh.path = path; - mesh.vertices = this.getFloatArray(map, "vertices", scale); - mesh.triangles = this.getIntArray(map, "triangles"); - mesh.regionUVs = this.getFloatArray(map, "uvs", 1); - mesh.updateUVs(); - - color = map["color"]; - if (color) { - mesh.r = this.toColor(color, 0); - mesh.g = this.toColor(color, 1); - mesh.b = this.toColor(color, 2); - mesh.a = this.toColor(color, 3); - } - - mesh.hullLength = (map["hull"] || 0) * 2; - if (map["edges"]) mesh.edges = this.getIntArray(map, "edges"); - mesh.width = (map["width"] || 0) * scale; - mesh.height = (map["height"] || 0) * scale; - return mesh; - } else if (type == spine.AttachmentType.skinnedmesh) { - var mesh = this.attachmentLoader.newSkinnedMeshAttachment(skin, name, path); - if (!mesh) return null; - mesh.path = path; - - var uvs = this.getFloatArray(map, "uvs", 1); - var vertices = this.getFloatArray(map, "vertices", 1); - var weights = []; - var bones = []; - for (var i = 0, n = vertices.length; i < n; ) { - var boneCount = vertices[i++] | 0; - bones[bones.length] = boneCount; - for (var nn = i + boneCount * 4; i < nn; ) { - bones[bones.length] = vertices[i]; - weights[weights.length] = vertices[i + 1] * scale; - weights[weights.length] = vertices[i + 2] * scale; - weights[weights.length] = vertices[i + 3]; - i += 4; - } - } - mesh.bones = bones; - mesh.weights = weights; - mesh.triangles = this.getIntArray(map, "triangles"); - mesh.regionUVs = uvs; - mesh.updateUVs(); - - color = map["color"]; - if (color) { - mesh.r = this.toColor(color, 0); - mesh.g = this.toColor(color, 1); - mesh.b = this.toColor(color, 2); - mesh.a = this.toColor(color, 3); - } - - mesh.hullLength = (map["hull"] || 0) * 2; - if (map["edges"]) mesh.edges = this.getIntArray(map, "edges"); - mesh.width = (map["width"] || 0) * scale; - mesh.height = (map["height"] || 0) * scale; - return mesh; - } else if (type == spine.AttachmentType.boundingbox) { - var attachment = this.attachmentLoader.newBoundingBoxAttachment(skin, name); - var vertices = map["vertices"]; - for (var i = 0, n = vertices.length; i < n; i++) - attachment.vertices.push(vertices[i] * scale); - return attachment; - } - throw "Unknown attachment type: " + type; - }, - readAnimation: function (name, map, skeletonData) { - var timelines = []; - var duration = 0; - - var slots = map["slots"]; - for (var slotName in slots) { - if (!slots.hasOwnProperty(slotName)) continue; - var slotMap = slots[slotName]; - var slotIndex = skeletonData.findSlotIndex(slotName); - - for (var timelineName in slotMap) { - if (!slotMap.hasOwnProperty(timelineName)) continue; - var values = slotMap[timelineName]; - if (timelineName == "color") { - var timeline = new spine.ColorTimeline(values.length); - timeline.slotIndex = slotIndex; - - var frameIndex = 0; - for (var i = 0, n = values.length; i < n; i++) { - var valueMap = values[i]; - var color = valueMap["color"]; - var r = this.toColor(color, 0); - var g = this.toColor(color, 1); - var b = this.toColor(color, 2); - var a = this.toColor(color, 3); - timeline.setFrame(frameIndex, valueMap["time"], r, g, b, a); - this.readCurve(timeline, frameIndex, valueMap); - frameIndex++; - } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 5 - 5]); - - } else if (timelineName == "attachment") { - var timeline = new spine.AttachmentTimeline(values.length); - timeline.slotIndex = slotIndex; - - var frameIndex = 0; - for (var i = 0, n = values.length; i < n; i++) { - var valueMap = values[i]; - timeline.setFrame(frameIndex++, valueMap["time"], valueMap["name"]); - } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); - - } else - throw "Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")"; - } - } - - var bones = map["bones"]; - for (var boneName in bones) { - if (!bones.hasOwnProperty(boneName)) continue; - var boneIndex = skeletonData.findBoneIndex(boneName); - if (boneIndex == -1) throw "Bone not found: " + boneName; - var boneMap = bones[boneName]; - - for (var timelineName in boneMap) { - if (!boneMap.hasOwnProperty(timelineName)) continue; - var values = boneMap[timelineName]; - if (timelineName == "rotate") { - var timeline = new spine.RotateTimeline(values.length); - timeline.boneIndex = boneIndex; - - var frameIndex = 0; - for (var i = 0, n = values.length; i < n; i++) { - var valueMap = values[i]; - timeline.setFrame(frameIndex, valueMap["time"], valueMap["angle"]); - this.readCurve(timeline, frameIndex, valueMap); - frameIndex++; - } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]); - - } else if (timelineName == "translate" || timelineName == "scale") { - var timeline; - var timelineScale = 1; - if (timelineName == "scale") - timeline = new spine.ScaleTimeline(values.length); - else { - timeline = new spine.TranslateTimeline(values.length); - timelineScale = this.scale; - } - timeline.boneIndex = boneIndex; - - var frameIndex = 0; - for (var i = 0, n = values.length; i < n; i++) { - var valueMap = values[i]; - var x = (valueMap["x"] || 0) * timelineScale; - var y = (valueMap["y"] || 0) * timelineScale; - timeline.setFrame(frameIndex, valueMap["time"], x, y); - this.readCurve(timeline, frameIndex, valueMap); - frameIndex++; - } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 3 - 3]); - - } else if (timelineName == "flipX" || timelineName == "flipY") { - var x = timelineName == "flipX"; - var timeline = x ? new spine.FlipXTimeline(values.length) : new spine.FlipYTimeline(values.length); - timeline.boneIndex = boneIndex; - - var field = x ? "x" : "y"; - var frameIndex = 0; - for (var i = 0, n = values.length; i < n; i++) { - var valueMap = values[i]; - timeline.setFrame(frameIndex, valueMap["time"], valueMap[field] || false); - frameIndex++; - } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[timeline.getFrameCount() * 2 - 2]); - } else - throw "Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"; - } - } - - var ikMap = map["ik"]; - for (var ikConstraintName in ikMap) { - if (!ikMap.hasOwnProperty(ikConstraintName)) continue; - var ikConstraint = skeletonData.findIkConstraint(ikConstraintName); - var values = ikMap[ikConstraintName]; - var timeline = new spine.IkConstraintTimeline(values.length); - timeline.ikConstraintIndex = skeletonData.ikConstraints.indexOf(ikConstraint); - var frameIndex = 0; - for (var i = 0, n = values.length; i < n; i++) { - var valueMap = values[i]; - var mix = valueMap.hasOwnProperty("mix") ? valueMap["mix"] : 1; - var bendDirection = (!valueMap.hasOwnProperty("bendPositive") || valueMap["bendPositive"]) ? 1 : -1; - timeline.setFrame(frameIndex, valueMap["time"], mix, bendDirection); - this.readCurve(timeline, frameIndex, valueMap); - frameIndex++; - } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[timeline.frameCount * 3 - 3]); - } - - var ffd = map["ffd"]; - for (var skinName in ffd) { - var skin = skeletonData.findSkin(skinName); - var slotMap = ffd[skinName]; - for (slotName in slotMap) { - var slotIndex = skeletonData.findSlotIndex(slotName); - var meshMap = slotMap[slotName]; - for (var meshName in meshMap) { - var values = meshMap[meshName]; - var timeline = new spine.FfdTimeline(values.length); - var attachment = skin.getAttachment(slotIndex, meshName); - if (!attachment) throw "FFD attachment not found: " + meshName; - timeline.slotIndex = slotIndex; - timeline.attachment = attachment; - - var isMesh = attachment.type == spine.AttachmentType.mesh; - var vertexCount; - if (isMesh) - vertexCount = attachment.vertices.length; - else - vertexCount = attachment.weights.length / 3 * 2; - - var frameIndex = 0; - for (var i = 0, n = values.length; i < n; i++) { - var valueMap = values[i]; - var vertices; - if (!valueMap["vertices"]) { - if (isMesh) - vertices = attachment.vertices; - else { - vertices = []; - vertices.length = vertexCount; - } - } else { - var verticesValue = valueMap["vertices"]; - var vertices = []; - vertices.length = vertexCount; - var start = valueMap["offset"] || 0; - var nn = verticesValue.length; - if (this.scale == 1) { - for (var ii = 0; ii < nn; ii++) - vertices[ii + start] = verticesValue[ii]; - } else { - for (var ii = 0; ii < nn; ii++) - vertices[ii + start] = verticesValue[ii] * this.scale; - } - if (isMesh) { - var meshVertices = attachment.vertices; - for (var ii = 0, nn = vertices.length; ii < nn; ii++) - vertices[ii] += meshVertices[ii]; - } - } - - timeline.setFrame(frameIndex, valueMap["time"], vertices); - this.readCurve(timeline, frameIndex, valueMap); - frameIndex++; - } - timelines[timelines.length] = timeline; - duration = Math.max(duration, timeline.frames[timeline.frameCount - 1]); - } - } - } - - var drawOrderValues = map["drawOrder"]; - if (!drawOrderValues) drawOrderValues = map["draworder"]; - if (drawOrderValues) { - var timeline = new spine.DrawOrderTimeline(drawOrderValues.length); - var slotCount = skeletonData.slots.length; - var frameIndex = 0; - for (var i = 0, n = drawOrderValues.length; i < n; i++) { - var drawOrderMap = drawOrderValues[i]; - var drawOrder = null; - if (drawOrderMap["offsets"]) { - drawOrder = []; - drawOrder.length = slotCount; - for (var ii = slotCount - 1; ii >= 0; ii--) - drawOrder[ii] = -1; - var offsets = drawOrderMap["offsets"]; - var unchanged = []; - unchanged.length = slotCount - offsets.length; - var originalIndex = 0, unchangedIndex = 0; - for (var ii = 0, nn = offsets.length; ii < nn; ii++) { - var offsetMap = offsets[ii]; - var slotIndex = skeletonData.findSlotIndex(offsetMap["slot"]); - if (slotIndex == -1) throw "Slot not found: " + offsetMap["slot"]; - // Collect unchanged items. - while (originalIndex != slotIndex) - unchanged[unchangedIndex++] = originalIndex++; - // Set changed items. - drawOrder[originalIndex + offsetMap["offset"]] = originalIndex++; - } - // Collect remaining unchanged items. - while (originalIndex < slotCount) - unchanged[unchangedIndex++] = originalIndex++; - // Fill in unchanged items. - for (var ii = slotCount - 1; ii >= 0; ii--) - if (drawOrder[ii] == -1) drawOrder[ii] = unchanged[--unchangedIndex]; - } - timeline.setFrame(frameIndex++, drawOrderMap["time"], drawOrder); - } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); - } - - var events = map["events"]; - if (events) { - var timeline = new spine.EventTimeline(events.length); - var frameIndex = 0; - for (var i = 0, n = events.length; i < n; i++) { - var eventMap = events[i]; - var eventData = skeletonData.findEvent(eventMap["name"]); - if (!eventData) throw "Event not found: " + eventMap["name"]; - var event = new spine.Event(eventData); - event.intValue = eventMap.hasOwnProperty("int") ? eventMap["int"] : eventData.intValue; - event.floatValue = eventMap.hasOwnProperty("float") ? eventMap["float"] : eventData.floatValue; - event.stringValue = eventMap.hasOwnProperty("string") ? eventMap["string"] : eventData.stringValue; - timeline.setFrame(frameIndex++, eventMap["time"], event); - } - timelines.push(timeline); - duration = Math.max(duration, timeline.frames[timeline.getFrameCount() - 1]); - } - - skeletonData.animations.push(new spine.Animation(name, timelines, duration)); - }, - readCurve: function (timeline, frameIndex, valueMap) { - var curve = valueMap["curve"]; - if (!curve) - timeline.curves.setLinear(frameIndex); - else if (curve == "stepped") - timeline.curves.setStepped(frameIndex); - else if (curve instanceof Array) - timeline.curves.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); - }, - toColor: function (hexString, colorIndex) { - if (hexString.length != 8) throw "Color hexidecimal length must be 8, recieved: " + hexString; - return parseInt(hexString.substring(colorIndex * 2, (colorIndex * 2) + 2), 16) / 255; - }, - getFloatArray: function (map, name, scale) { - var list = map[name]; - var values = new spine.Float32Array(list.length); - var i = 0, n = list.length; - if (scale == 1) { - for (; i < n; i++) - values[i] = list[i]; - } else { - for (; i < n; i++) - values[i] = list[i] * scale; - } - return values; - }, - getIntArray: function (map, name) { - var list = map[name]; - var values = new spine.Uint16Array(list.length); - for (var i = 0, n = list.length; i < n; i++) - values[i] = list[i] | 0; - return values; - } -}; - -spine.Atlas = function (atlasText, textureLoader) { - this.textureLoader = textureLoader; - this.pages = []; - this.regions = []; - - var reader = new spine.AtlasReader(atlasText); - var tuple = []; - tuple.length = 4; - var page = null; - while (true) { - var line = reader.readLine(); - if (line === null) break; - line = reader.trim(line); - if (!line.length) - page = null; - else if (!page) { - page = new spine.AtlasPage(); - page.name = line; - - if (reader.readTuple(tuple) == 2) { // size is only optional for an atlas packed with an old TexturePacker. - page.width = parseInt(tuple[0]); - page.height = parseInt(tuple[1]); - reader.readTuple(tuple); - } - page.format = spine.Atlas.Format[tuple[0]]; - - reader.readTuple(tuple); - page.minFilter = spine.Atlas.TextureFilter[tuple[0]]; - page.magFilter = spine.Atlas.TextureFilter[tuple[1]]; - - var direction = reader.readValue(); - page.uWrap = spine.Atlas.TextureWrap.clampToEdge; - page.vWrap = spine.Atlas.TextureWrap.clampToEdge; - if (direction == "x") - page.uWrap = spine.Atlas.TextureWrap.repeat; - else if (direction == "y") - page.vWrap = spine.Atlas.TextureWrap.repeat; - else if (direction == "xy") - page.uWrap = page.vWrap = spine.Atlas.TextureWrap.repeat; - - textureLoader.load(page, line, this); - - this.pages.push(page); - - } else { - var region = new spine.AtlasRegion(); - region.name = line; - region.page = page; - - region.rotate = reader.readValue() == "true"; - - reader.readTuple(tuple); - var x = parseInt(tuple[0]); - var y = parseInt(tuple[1]); - - reader.readTuple(tuple); - var width = parseInt(tuple[0]); - var height = parseInt(tuple[1]); - - region.u = x / page.width; - region.v = y / page.height; - if (region.rotate) { - region.u2 = (x + height) / page.width; - region.v2 = (y + width) / page.height; - } else { - region.u2 = (x + width) / page.width; - region.v2 = (y + height) / page.height; - } - region.x = x; - region.y = y; - region.width = Math.abs(width); - region.height = Math.abs(height); - - if (reader.readTuple(tuple) == 4) { // split is optional - region.splits = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])]; - - if (reader.readTuple(tuple) == 4) { // pad is optional, but only present with splits - region.pads = [parseInt(tuple[0]), parseInt(tuple[1]), parseInt(tuple[2]), parseInt(tuple[3])]; - - reader.readTuple(tuple); - } - } - - region.originalWidth = parseInt(tuple[0]); - region.originalHeight = parseInt(tuple[1]); - - reader.readTuple(tuple); - region.offsetX = parseInt(tuple[0]); - region.offsetY = parseInt(tuple[1]); - - region.index = parseInt(reader.readValue()); - - this.regions.push(region); - } - } -}; -spine.Atlas.prototype = { - findRegion: function (name) { - var regions = this.regions; - for (var i = 0, n = regions.length; i < n; i++) - if (regions[i].name == name) return regions[i]; - return null; - }, - dispose: function () { - var pages = this.pages; - for (var i = 0, n = pages.length; i < n; i++) - this.textureLoader.unload(pages[i].rendererObject); - }, - updateUVs: function (page) { - var regions = this.regions; - for (var i = 0, n = regions.length; i < n; i++) { - var region = regions[i]; - if (region.page != page) continue; - region.u = region.x / page.width; - region.v = region.y / page.height; - if (region.rotate) { - region.u2 = (region.x + region.height) / page.width; - region.v2 = (region.y + region.width) / page.height; - } else { - region.u2 = (region.x + region.width) / page.width; - region.v2 = (region.y + region.height) / page.height; - } - } - } -}; - -spine.Atlas.Format = { - alpha: 0, - intensity: 1, - luminanceAlpha: 2, - rgb565: 3, - rgba4444: 4, - rgb888: 5, - rgba8888: 6 -}; - -spine.Atlas.TextureFilter = { - nearest: 0, - linear: 1, - mipMap: 2, - mipMapNearestNearest: 3, - mipMapLinearNearest: 4, - mipMapNearestLinear: 5, - mipMapLinearLinear: 6 -}; - -spine.Atlas.TextureWrap = { - mirroredRepeat: 0, - clampToEdge: 1, - repeat: 2 -}; - -spine.AtlasPage = function () {}; -spine.AtlasPage.prototype = { - name: null, - format: null, - minFilter: null, - magFilter: null, - uWrap: null, - vWrap: null, - rendererObject: null, - width: 0, - height: 0 -}; - -spine.AtlasRegion = function () {}; -spine.AtlasRegion.prototype = { - page: null, - name: null, - x: 0, y: 0, - width: 0, height: 0, - u: 0, v: 0, u2: 0, v2: 0, - offsetX: 0, offsetY: 0, - originalWidth: 0, originalHeight: 0, - index: 0, - rotate: false, - splits: null, - pads: null -}; - -spine.AtlasReader = function (text) { - this.lines = text.split(/\r\n|\r|\n/); -}; -spine.AtlasReader.prototype = { - index: 0, - trim: function (value) { - return value.replace(/^\s+|\s+$/g, ""); - }, - readLine: function () { - if (this.index >= this.lines.length) return null; - return this.lines[this.index++]; - }, - readValue: function () { - var line = this.readLine(); - var colon = line.indexOf(":"); - if (colon == -1) throw "Invalid line: " + line; - return this.trim(line.substring(colon + 1)); - }, - /** Returns the number of tuple values read (1, 2 or 4). */ - readTuple: function (tuple) { - var line = this.readLine(); - var colon = line.indexOf(":"); - if (colon == -1) throw "Invalid line: " + line; - var i = 0, lastMatch = colon + 1; - for (; i < 3; i++) { - var comma = line.indexOf(",", lastMatch); - if (comma == -1) break; - tuple[i] = this.trim(line.substr(lastMatch, comma - lastMatch)); - lastMatch = comma + 1; - } - tuple[i] = this.trim(line.substring(lastMatch)); - return i + 1; - } -}; - -spine.AtlasAttachmentLoader = function (atlas) { - this.atlas = atlas; -}; -spine.AtlasAttachmentLoader.prototype = { - newRegionAttachment: function (skin, name, path) { - var region = this.atlas.findRegion(path); - if (!region) throw "Region not found in atlas: " + path + " (region attachment: " + name + ")"; - var attachment = new spine.RegionAttachment(name); - attachment.rendererObject = region; - attachment.setUVs(region.u, region.v, region.u2, region.v2, region.rotate); - attachment.regionOffsetX = region.offsetX; - attachment.regionOffsetY = region.offsetY; - attachment.regionWidth = region.width; - attachment.regionHeight = region.height; - attachment.regionOriginalWidth = region.originalWidth; - attachment.regionOriginalHeight = region.originalHeight; - return attachment; - }, - newMeshAttachment: function (skin, name, path) { - var region = this.atlas.findRegion(path); - if (!region) throw "Region not found in atlas: " + path + " (mesh attachment: " + name + ")"; - var attachment = new spine.MeshAttachment(name); - attachment.rendererObject = region; - attachment.regionU = region.u; - attachment.regionV = region.v; - attachment.regionU2 = region.u2; - attachment.regionV2 = region.v2; - attachment.regionRotate = region.rotate; - attachment.regionOffsetX = region.offsetX; - attachment.regionOffsetY = region.offsetY; - attachment.regionWidth = region.width; - attachment.regionHeight = region.height; - attachment.regionOriginalWidth = region.originalWidth; - attachment.regionOriginalHeight = region.originalHeight; - return attachment; - }, - newSkinnedMeshAttachment: function (skin, name, path) { - var region = this.atlas.findRegion(path); - if (!region) throw "Region not found in atlas: " + path + " (skinned mesh attachment: " + name + ")"; - var attachment = new spine.SkinnedMeshAttachment(name); - attachment.rendererObject = region; - attachment.regionU = region.u; - attachment.regionV = region.v; - attachment.regionU2 = region.u2; - attachment.regionV2 = region.v2; - attachment.regionRotate = region.rotate; - attachment.regionOffsetX = region.offsetX; - attachment.regionOffsetY = region.offsetY; - attachment.regionWidth = region.width; - attachment.regionHeight = region.height; - attachment.regionOriginalWidth = region.originalWidth; - attachment.regionOriginalHeight = region.originalHeight; - return attachment; - }, - newBoundingBoxAttachment: function (skin, name) { - return new spine.BoundingBoxAttachment(name); - } -}; - -spine.SkeletonBounds = function () { - this.polygonPool = []; - this.polygons = []; - this.boundingBoxes = []; -}; -spine.SkeletonBounds.prototype = { - minX: 0, minY: 0, maxX: 0, maxY: 0, - update: function (skeleton, updateAabb) { - var slots = skeleton.slots; - var slotCount = slots.length; - var x = skeleton.x, y = skeleton.y; - var boundingBoxes = this.boundingBoxes; - var polygonPool = this.polygonPool; - var polygons = this.polygons; - - boundingBoxes.length = 0; - for (var i = 0, n = polygons.length; i < n; i++) - polygonPool.push(polygons[i]); - polygons.length = 0; - - for (var i = 0; i < slotCount; i++) { - var slot = slots[i]; - var boundingBox = slot.attachment; - if (boundingBox.type != spine.AttachmentType.boundingbox) continue; - boundingBoxes.push(boundingBox); - - var poolCount = polygonPool.length, polygon; - if (poolCount > 0) { - polygon = polygonPool[poolCount - 1]; - polygonPool.splice(poolCount - 1, 1); - } else - polygon = []; - polygons.push(polygon); - - polygon.length = boundingBox.vertices.length; - boundingBox.computeWorldVertices(x, y, slot.bone, polygon); - } - - if (updateAabb) this.aabbCompute(); - }, - aabbCompute: function () { - var polygons = this.polygons; - var minX = Number.MAX_VALUE, minY = Number.MAX_VALUE, maxX = Number.MIN_VALUE, maxY = Number.MIN_VALUE; - for (var i = 0, n = polygons.length; i < n; i++) { - var vertices = polygons[i]; - for (var ii = 0, nn = vertices.length; ii < nn; ii += 2) { - var x = vertices[ii]; - var y = vertices[ii + 1]; - minX = Math.min(minX, x); - minY = Math.min(minY, y); - maxX = Math.max(maxX, x); - maxY = Math.max(maxY, y); - } - } - this.minX = minX; - this.minY = minY; - this.maxX = maxX; - this.maxY = maxY; - }, - /** Returns true if the axis aligned bounding box contains the point. */ - aabbContainsPoint: function (x, y) { - return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY; - }, - /** Returns true if the axis aligned bounding box intersects the line segment. */ - aabbIntersectsSegment: function (x1, y1, x2, y2) { - var minX = this.minX, minY = this.minY, maxX = this.maxX, maxY = this.maxY; - if ((x1 <= minX && x2 <= minX) || (y1 <= minY && y2 <= minY) || (x1 >= maxX && x2 >= maxX) || (y1 >= maxY && y2 >= maxY)) - return false; - var m = (y2 - y1) / (x2 - x1); - var y = m * (minX - x1) + y1; - if (y > minY && y < maxY) return true; - y = m * (maxX - x1) + y1; - if (y > minY && y < maxY) return true; - var x = (minY - y1) / m + x1; - if (x > minX && x < maxX) return true; - x = (maxY - y1) / m + x1; - if (x > minX && x < maxX) return true; - return false; - }, - /** Returns true if the axis aligned bounding box intersects the axis aligned bounding box of the specified bounds. */ - aabbIntersectsSkeleton: function (bounds) { - return this.minX < bounds.maxX && this.maxX > bounds.minX && this.minY < bounds.maxY && this.maxY > bounds.minY; - }, - /** Returns the first bounding box attachment that contains the point, or null. When doing many checks, it is usually more - * efficient to only call this method if {@link #aabbContainsPoint(float, float)} returns true. */ - containsPoint: function (x, y) { - var polygons = this.polygons; - for (var i = 0, n = polygons.length; i < n; i++) - if (this.polygonContainsPoint(polygons[i], x, y)) return this.boundingBoxes[i]; - return null; - }, - /** Returns the first bounding box attachment that contains the line segment, or null. When doing many checks, it is usually - * more efficient to only call this method if {@link #aabbIntersectsSegment(float, float, float, float)} returns true. */ - intersectsSegment: function (x1, y1, x2, y2) { - var polygons = this.polygons; - for (var i = 0, n = polygons.length; i < n; i++) - if (polygons[i].intersectsSegment(x1, y1, x2, y2)) return this.boundingBoxes[i]; - return null; - }, - /** Returns true if the polygon contains the point. */ - polygonContainsPoint: function (polygon, x, y) { - var nn = polygon.length; - var prevIndex = nn - 2; - var inside = false; - for (var ii = 0; ii < nn; ii += 2) { - var vertexY = polygon[ii + 1]; - var prevY = polygon[prevIndex + 1]; - if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) { - var vertexX = polygon[ii]; - if (vertexX + (y - vertexY) / (prevY - vertexY) * (polygon[prevIndex] - vertexX) < x) inside = !inside; - } - prevIndex = ii; - } - return inside; - }, - /** Returns true if the polygon contains the line segment. */ - polygonIntersectsSegment: function (polygon, x1, y1, x2, y2) { - var nn = polygon.length; - var width12 = x1 - x2, height12 = y1 - y2; - var det1 = x1 * y2 - y1 * x2; - var x3 = polygon[nn - 2], y3 = polygon[nn - 1]; - for (var ii = 0; ii < nn; ii += 2) { - var x4 = polygon[ii], y4 = polygon[ii + 1]; - var det2 = x3 * y4 - y3 * x4; - var width34 = x3 - x4, height34 = y3 - y4; - var det3 = width12 * height34 - height12 * width34; - var x = (det1 * width34 - width12 * det2) / det3; - if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) { - var y = (det1 * height34 - height12 * det2) / det3; - if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) return true; - } - x3 = x4; - y3 = y4; - } - return false; - }, - getPolygon: function (attachment) { - var index = this.boundingBoxes.indexOf(attachment); - return index == -1 ? null : this.polygons[index]; - }, - getWidth: function () { - return this.maxX - this.minX; - }, - getHeight: function () { - return this.maxY - this.minY; - } -}; - -/* Esoteric Software SPINE wrapper for pixi.js */ - -spine.Bone.yDown = true; -PIXI.AnimCache = {}; - -/** - * Supporting class to load images from spine atlases as per spine spec. - * - * @class SpineTextureLoader - * @uses EventTarget - * @constructor - * @param basePath {String} Tha base path where to look for the images to be loaded - * @param crossorigin {Boolean} Whether requests should be treated as crossorigin - */ -PIXI.SpineTextureLoader = function(basePath, crossorigin) -{ - PIXI.EventTarget.call(this); - - this.basePath = basePath; - this.crossorigin = crossorigin; - this.loadingCount = 0; -}; - -/* constructor */ -PIXI.SpineTextureLoader.prototype = PIXI.SpineTextureLoader; - -/** - * Starts loading a base texture as per spine specification - * - * @method load - * @param page {spine.AtlasPage} Atlas page to which texture belongs - * @param file {String} The file to load, this is just the file path relative to the base path configured in the constructor - */ -PIXI.SpineTextureLoader.prototype.load = function(page, file) -{ - page.rendererObject = PIXI.BaseTexture.fromImage(this.basePath + '/' + file, this.crossorigin); - if (!page.rendererObject.hasLoaded) - { - var scope = this; - ++scope.loadingCount; - page.rendererObject.addEventListener('loaded', function(){ - --scope.loadingCount; - scope.dispatchEvent({ - type: 'loadedBaseTexture', - content: scope - }); - }); - } -}; - -/** - * Unloads a previously loaded texture as per spine specification - * - * @method unload - * @param texture {BaseTexture} Texture object to destroy - */ -PIXI.SpineTextureLoader.prototype.unload = function(texture) -{ - texture.destroy(true); -}; - -/** - * A class that enables the you to import and run your spine animations in pixi. - * Spine animation data needs to be loaded using the PIXI.AssetLoader or PIXI.SpineLoader before it can be used by this class - * See example 12 (http://www.goodboydigital.com/pixijs/examples/12/) to see a working example and check out the source - * - * @class Spine - * @extends DisplayObjectContainer - * @constructor - * @param url {String} The url of the spine anim file to be used - */ -PIXI.Spine = function (url) { - PIXI.DisplayObjectContainer.call(this); - - this.spineData = PIXI.AnimCache[url]; - - if (!this.spineData) { - throw new Error('Spine data must be preloaded using PIXI.SpineLoader or PIXI.AssetLoader: ' + url); - } - - this.skeleton = new spine.Skeleton(this.spineData); - this.skeleton.updateWorldTransform(); - - this.stateData = new spine.AnimationStateData(this.spineData); - this.state = new spine.AnimationState(this.stateData); - - this.slotContainers = []; - - for (var i = 0, n = this.skeleton.drawOrder.length; i < n; i++) { - var slot = this.skeleton.drawOrder[i]; - var attachment = slot.attachment; - var slotContainer = new PIXI.DisplayObjectContainer(); - this.slotContainers.push(slotContainer); - this.addChild(slotContainer); - - if (attachment instanceof spine.RegionAttachment) - { - var spriteName = attachment.rendererObject.name; - var sprite = this.createSprite(slot, attachment); - slot.currentSprite = sprite; - slot.currentSpriteName = spriteName; - slotContainer.addChild(sprite); - } - else if (attachment instanceof spine.MeshAttachment) - { - var mesh = this.createMesh(slot, attachment); - slot.currentMesh = mesh; - slot.currentMeshName = attachment.name; - slotContainer.addChild(mesh); - } - else - { - continue; - } - - } - - this.autoUpdate = true; -}; - -PIXI.Spine.prototype = Object.create(PIXI.DisplayObjectContainer.prototype); -PIXI.Spine.prototype.constructor = PIXI.Spine; - -/** - * If this flag is set to true, the spine animation will be autoupdated every time - * the object id drawn. The down side of this approach is that the delta time is - * automatically calculated and you could miss out on cool effects like slow motion, - * pause, skip ahead and the sorts. Most of these effects can be achieved even with - * autoupdate enabled but are harder to achieve. - * - * @property autoUpdate - * @type { Boolean } - * @default true - */ -Object.defineProperty(PIXI.Spine.prototype, 'autoUpdate', { - get: function() - { - return (this.updateTransform === PIXI.Spine.prototype.autoUpdateTransform); - }, - - set: function(value) - { - this.updateTransform = value ? PIXI.Spine.prototype.autoUpdateTransform : PIXI.DisplayObjectContainer.prototype.updateTransform; - } -}); - -/** - * Update the spine skeleton and its animations by delta time (dt) - * - * @method update - * @param dt {Number} Delta time. Time by which the animation should be updated - */ -PIXI.Spine.prototype.update = function(dt) -{ - this.state.update(dt); - this.state.apply(this.skeleton); - this.skeleton.updateWorldTransform(); - - var drawOrder = this.skeleton.drawOrder; - for (var i = 0, n = drawOrder.length; i < n; i++) { - var slot = drawOrder[i]; - var attachment = slot.attachment; - var slotContainer = this.slotContainers[i]; - - if (!attachment) - { - slotContainer.visible = false; - continue; - } - - var type = attachment.type; - if (type === spine.AttachmentType.region) - { - if (attachment.rendererObject) - { - if (!slot.currentSpriteName || slot.currentSpriteName !== attachment.name) - { - var spriteName = attachment.rendererObject.name; - if (slot.currentSprite !== undefined) - { - slot.currentSprite.visible = false; - } - slot.sprites = slot.sprites || {}; - if (slot.sprites[spriteName] !== undefined) - { - slot.sprites[spriteName].visible = true; - } - else - { - var sprite = this.createSprite(slot, attachment); - slotContainer.addChild(sprite); - } - slot.currentSprite = slot.sprites[spriteName]; - slot.currentSpriteName = spriteName; - } - } - - var bone = slot.bone; - - slotContainer.position.x = bone.worldX + attachment.x * bone.m00 + attachment.y * bone.m01; - slotContainer.position.y = bone.worldY + attachment.x * bone.m10 + attachment.y * bone.m11; - slotContainer.scale.x = bone.worldScaleX; - slotContainer.scale.y = bone.worldScaleY; - - slotContainer.rotation = -(slot.bone.worldRotation * spine.degRad); - - slot.currentSprite.tint = PIXI.rgb2hex([slot.r,slot.g,slot.b]); - } - else if (type === spine.AttachmentType.skinnedmesh) - { - if (!slot.currentMeshName || slot.currentMeshName !== attachment.name) - { - var meshName = attachment.name; - if (slot.currentMesh !== undefined) - { - slot.currentMesh.visible = false; - } - - slot.meshes = slot.meshes || {}; - - if (slot.meshes[meshName] !== undefined) - { - slot.meshes[meshName].visible = true; - } - else - { - var mesh = this.createMesh(slot, attachment); - slotContainer.addChild(mesh); - } - - slot.currentMesh = slot.meshes[meshName]; - slot.currentMeshName = meshName; - } - - attachment.computeWorldVertices(slot.bone.skeleton.x, slot.bone.skeleton.y, slot, slot.currentMesh.vertices); - - } - else - { - slotContainer.visible = false; - continue; - } - slotContainer.visible = true; - - slotContainer.alpha = slot.a; - } -}; - -/** - * When autoupdate is set to yes this function is used as pixi's updateTransform function - * - * @method autoUpdateTransform - * @private - */ -PIXI.Spine.prototype.autoUpdateTransform = function () { - this.lastTime = this.lastTime || Date.now(); - var timeDelta = (Date.now() - this.lastTime) * 0.001; - this.lastTime = Date.now(); - - this.update(timeDelta); - - PIXI.DisplayObjectContainer.prototype.updateTransform.call(this); -}; - -/** - * Create a new sprite to be used with spine.RegionAttachment - * - * @method createSprite - * @param slot {spine.Slot} The slot to which the attachment is parented - * @param attachment {spine.RegionAttachment} The attachment that the sprite will represent - * @private - */ -PIXI.Spine.prototype.createSprite = function (slot, attachment) { - var descriptor = attachment.rendererObject; - var baseTexture = descriptor.page.rendererObject; - var spriteRect = new PIXI.Rectangle(descriptor.x, - descriptor.y, - descriptor.rotate ? descriptor.height : descriptor.width, - descriptor.rotate ? descriptor.width : descriptor.height); - var spriteTexture = new PIXI.Texture(baseTexture, spriteRect); - var sprite = new PIXI.Sprite(spriteTexture); - - var baseRotation = descriptor.rotate ? Math.PI * 0.5 : 0.0; - sprite.scale.set(descriptor.width / descriptor.originalWidth, descriptor.height / descriptor.originalHeight); - sprite.rotation = baseRotation - (attachment.rotation * spine.degRad); - sprite.anchor.x = sprite.anchor.y = 0.5; - - slot.sprites = slot.sprites || {}; - slot.sprites[descriptor.name] = sprite; - return sprite; -}; - -PIXI.Spine.prototype.createMesh = function (slot, attachment) { - var descriptor = attachment.rendererObject; - var baseTexture = descriptor.page.rendererObject; - var texture = new PIXI.Texture(baseTexture); - - var strip = new PIXI.Strip(texture); - strip.drawMode = PIXI.Strip.DrawModes.TRIANGLES; - strip.canvasPadding = 1.5; - - strip.vertices = new PIXI.Float32Array(attachment.uvs.length); - strip.uvs = attachment.uvs; - strip.indices = attachment.triangles; - - slot.meshes = slot.meshes || {}; - slot.meshes[attachment.name] = strip; - - return strip; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -PIXI.BaseTextureCache = {}; - -PIXI.BaseTextureCacheIdGenerator = 0; - -/** - * A texture stores the information that represents an image. All textures have a base texture. - * - * @class BaseTexture - * @uses EventTarget - * @constructor - * @param source {String} the source object (image or canvas) - * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values - */ -PIXI.BaseTexture = function(source, scaleMode) -{ - /** - * The Resolution of the texture. - * - * @property resolution - * @type Number - */ - this.resolution = 1; - - /** - * [read-only] The width of the base texture set when the image has loaded - * - * @property width - * @type Number - * @readOnly - */ - this.width = 100; - - /** - * [read-only] The height of the base texture set when the image has loaded - * - * @property height - * @type Number - * @readOnly - */ - this.height = 100; - - /** - * The scale mode to apply when scaling this texture - * - * @property scaleMode - * @type {Number} - * @default PIXI.scaleModes.LINEAR - */ - this.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; - - /** - * [read-only] Set to true once the base texture has loaded - * - * @property hasLoaded - * @type Boolean - * @readOnly - */ - this.hasLoaded = false; - - /** - * The image source that is used to create the texture. - * - * @property source - * @type Image - */ - this.source = source; - - this._UID = PIXI._UID++; - - /** - * Controls if RGB channels should be pre-multiplied by Alpha (WebGL only) - * - * @property premultipliedAlpha - * @type Boolean - * @default true - */ - this.premultipliedAlpha = true; - - // used for webGL - - /** - * @property _glTextures - * @type Array - * @private - */ - this._glTextures = []; - - /** - * - * Set this to true if a mipmap of this texture needs to be generated. This value needs to be set before the texture is used - * Also the texture must be a power of two size to work - * - * @property mipmap - * @type {Boolean} - */ - this.mipmap = false; - - // used for webGL texture updating... - // TODO - this needs to be addressed - - /** - * @property _dirty - * @type Array - * @private - */ - this._dirty = [true, true, true, true]; - - if(!source)return; - - if((this.source.complete || this.source.getContext) && this.source.width && this.source.height) - { - this.hasLoaded = true; - this.width = this.source.naturalWidth || this.source.width; - this.height = this.source.naturalHeight || this.source.height; - this.dirty(); - } - else - { - var scope = this; - - this.source.onload = function() { - - scope.hasLoaded = true; - scope.width = scope.source.naturalWidth || scope.source.width; - scope.height = scope.source.naturalHeight || scope.source.height; - - scope.dirty(); - - // add it to somewhere... - scope.dispatchEvent( { type: 'loaded', content: scope } ); - }; - - this.source.onerror = function() { - scope.dispatchEvent( { type: 'error', content: scope } ); - }; - } - - /** - * @property imageUrl - * @type String - */ - this.imageUrl = null; - - /** - * @property _powerOf2 - * @type Boolean - * @private - */ - this._powerOf2 = false; - -}; - -PIXI.BaseTexture.prototype.constructor = PIXI.BaseTexture; - -PIXI.EventTarget.mixin(PIXI.BaseTexture.prototype); - -/** - * Destroys this base texture - * - * @method destroy - */ -PIXI.BaseTexture.prototype.destroy = function() -{ - if(this.imageUrl) - { - delete PIXI.BaseTextureCache[this.imageUrl]; - delete PIXI.TextureCache[this.imageUrl]; - this.imageUrl = null; - if (!navigator.isCocoonJS) this.source.src = ''; - } - else if (this.source && this.source._pixiId) - { - delete PIXI.BaseTextureCache[this.source._pixiId]; - } - this.source = null; - - this.unloadFromGPU(); -}; - -/** - * Changes the source image of the texture - * - * @method updateSourceImage - * @param newSrc {String} the path of the image - */ -PIXI.BaseTexture.prototype.updateSourceImage = function(newSrc) -{ - this.hasLoaded = false; - this.source.src = null; - this.source.src = newSrc; -}; - -/** - * Sets all glTextures to be dirty. - * - * @method dirty - */ -PIXI.BaseTexture.prototype.dirty = function() -{ - for (var i = 0; i < this._glTextures.length; i++) - { - this._dirty[i] = true; - } -}; - -/** - * Removes the base texture from the GPU, useful for managing resources on the GPU. - * Atexture is still 100% usable and will simply be reuploaded if there is a sprite on screen that is using it. - * - * @method unloadFromGPU - */ -PIXI.BaseTexture.prototype.unloadFromGPU = function() -{ - this.dirty(); - - // delete the webGL textures if any. - for (var i = this._glTextures.length - 1; i >= 0; i--) - { - var glTexture = this._glTextures[i]; - var gl = PIXI.glContexts[i]; - - if(gl && glTexture) - { - gl.deleteTexture(glTexture); - } - - } - - this._glTextures.length = 0; - - this.dirty(); -}; - -/** - * Helper function that creates a base texture from the given image url. - * If the image is not in the base texture cache it will be created and loaded. - * - * @static - * @method fromImage - * @param imageUrl {String} The image url of the texture - * @param crossorigin {Boolean} - * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values - * @return BaseTexture - */ -PIXI.BaseTexture.fromImage = function(imageUrl, crossorigin, scaleMode) -{ - var baseTexture = PIXI.BaseTextureCache[imageUrl]; - - if(crossorigin === undefined && imageUrl.indexOf('data:') === -1) crossorigin = true; - - if(!baseTexture) - { - // new Image() breaks tex loading in some versions of Chrome. - // See https://code.google.com/p/chromium/issues/detail?id=238071 - var image = new Image();//document.createElement('img'); - if (crossorigin) - { - image.crossOrigin = ''; - } - - image.src = imageUrl; - baseTexture = new PIXI.BaseTexture(image, scaleMode); - baseTexture.imageUrl = imageUrl; - PIXI.BaseTextureCache[imageUrl] = baseTexture; - - // if there is an @2x at the end of the url we are going to assume its a highres image - if( imageUrl.indexOf(PIXI.RETINA_PREFIX + '.') !== -1) - { - baseTexture.resolution = 2; - } - } - - return baseTexture; -}; - -/** - * Helper function that creates a base texture from the given canvas element. - * - * @static - * @method fromCanvas - * @param canvas {Canvas} The canvas element source of the texture - * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values - * @return BaseTexture - */ -PIXI.BaseTexture.fromCanvas = function(canvas, scaleMode) -{ - if(!canvas._pixiId) - { - canvas._pixiId = 'canvas_' + PIXI.TextureCacheIdGenerator++; - } - - var baseTexture = PIXI.BaseTextureCache[canvas._pixiId]; - - if(!baseTexture) - { - baseTexture = new PIXI.BaseTexture(canvas, scaleMode); - PIXI.BaseTextureCache[canvas._pixiId] = baseTexture; - } - - return baseTexture; -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -PIXI.TextureCache = {}; -PIXI.FrameCache = {}; - -PIXI.TextureCacheIdGenerator = 0; - -/** - * A texture stores the information that represents an image or part of an image. It cannot be added - * to the display list directly. Instead use it as the texture for a PIXI.Sprite. If no frame is provided then the whole image is used. - * - * @class Texture - * @uses EventTarget - * @constructor - * @param baseTexture {BaseTexture} The base texture source to create the texture from - * @param frame {Rectangle} The rectangle frame of the texture to show - * @param [crop] {Rectangle} The area of original texture - * @param [trim] {Rectangle} Trimmed texture rectangle - */ -PIXI.Texture = function(baseTexture, frame, crop, trim) -{ - /** - * Does this Texture have any frame data assigned to it? - * - * @property noFrame - * @type Boolean - */ - this.noFrame = false; - - if (!frame) - { - this.noFrame = true; - frame = new PIXI.Rectangle(0,0,1,1); - } - - if (baseTexture instanceof PIXI.Texture) - { - baseTexture = baseTexture.baseTexture; - } - - /** - * The base texture that this texture uses. - * - * @property baseTexture - * @type BaseTexture - */ - this.baseTexture = baseTexture; - - /** - * The frame specifies the region of the base texture that this texture uses - * - * @property frame - * @type Rectangle - */ - this.frame = frame; - - /** - * The texture trim data. - * - * @property trim - * @type Rectangle - */ - this.trim = trim; - - /** - * This will let the renderer know if the texture is valid. If it's not then it cannot be rendered. - * - * @property valid - * @type Boolean - */ - this.valid = false; - - /** - * This will let a renderer know that a texture has been updated (used mainly for webGL uv updates) - * - * @property requiresUpdate - * @type Boolean - */ - this.requiresUpdate = false; - - /** - * The WebGL UV data cache. - * - * @property _uvs - * @type Object - * @private - */ - this._uvs = null; - - /** - * The width of the Texture in pixels. - * - * @property width - * @type Number - */ - this.width = 0; - - /** - * The height of the Texture in pixels. - * - * @property height - * @type Number - */ - this.height = 0; - - /** - * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, - * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) - * - * @property crop - * @type Rectangle - */ - this.crop = crop || new PIXI.Rectangle(0, 0, 1, 1); - - if (baseTexture.hasLoaded) - { - if (this.noFrame) frame = new PIXI.Rectangle(0, 0, baseTexture.width, baseTexture.height); - this.setFrame(frame); - } - else - { - baseTexture.addEventListener('loaded', this.onBaseTextureLoaded.bind(this)); - } -}; - -PIXI.Texture.prototype.constructor = PIXI.Texture; -PIXI.EventTarget.mixin(PIXI.Texture.prototype); - -/** - * Called when the base texture is loaded - * - * @method onBaseTextureLoaded - * @private - */ -PIXI.Texture.prototype.onBaseTextureLoaded = function() -{ - var baseTexture = this.baseTexture; - baseTexture.removeEventListener('loaded', this.onLoaded); - - if (this.noFrame) this.frame = new PIXI.Rectangle(0, 0, baseTexture.width, baseTexture.height); - - this.setFrame(this.frame); - - this.dispatchEvent( { type: 'update', content: this } ); -}; - -/** - * Destroys this texture - * - * @method destroy - * @param destroyBase {Boolean} Whether to destroy the base texture as well - */ -PIXI.Texture.prototype.destroy = function(destroyBase) -{ - if (destroyBase) this.baseTexture.destroy(); - - this.valid = false; -}; - -/** - * Specifies the region of the baseTexture that this texture will use. - * - * @method setFrame - * @param frame {Rectangle} The frame of the texture to set it to - */ -PIXI.Texture.prototype.setFrame = function(frame) -{ - this.noFrame = false; - - this.frame = frame; - this.width = frame.width; - this.height = frame.height; - - this.crop.x = frame.x; - this.crop.y = frame.y; - this.crop.width = frame.width; - this.crop.height = frame.height; - - if (!this.trim && (frame.x + frame.width > this.baseTexture.width || frame.y + frame.height > this.baseTexture.height)) - { - throw new Error('Texture Error: frame does not fit inside the base Texture dimensions ' + this); - } - - this.valid = frame && frame.width && frame.height && this.baseTexture.source && this.baseTexture.hasLoaded; - - if (this.trim) - { - this.width = this.trim.width; - this.height = this.trim.height; - this.frame.width = this.trim.width; - this.frame.height = this.trim.height; - } - - if (this.valid) this._updateUvs(); - -}; - -/** - * Updates the internal WebGL UV cache. - * - * @method _updateUvs - * @private - */ -PIXI.Texture.prototype._updateUvs = function() -{ - if(!this._uvs)this._uvs = new PIXI.TextureUvs(); - - var frame = this.crop; - var tw = this.baseTexture.width; - var th = this.baseTexture.height; - - this._uvs.x0 = frame.x / tw; - this._uvs.y0 = frame.y / th; - - this._uvs.x1 = (frame.x + frame.width) / tw; - this._uvs.y1 = frame.y / th; - - this._uvs.x2 = (frame.x + frame.width) / tw; - this._uvs.y2 = (frame.y + frame.height) / th; - - this._uvs.x3 = frame.x / tw; - this._uvs.y3 = (frame.y + frame.height) / th; -}; - -/** - * Helper function that creates a Texture object from the given image url. - * If the image is not in the texture cache it will be created and loaded. - * - * @static - * @method fromImage - * @param imageUrl {String} The image url of the texture - * @param crossorigin {Boolean} Whether requests should be treated as crossorigin - * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values - * @return Texture - */ -PIXI.Texture.fromImage = function(imageUrl, crossorigin, scaleMode) -{ - var texture = PIXI.TextureCache[imageUrl]; - - if(!texture) - { - texture = new PIXI.Texture(PIXI.BaseTexture.fromImage(imageUrl, crossorigin, scaleMode)); - PIXI.TextureCache[imageUrl] = texture; - } - - return texture; -}; - -/** - * Helper function that returns a Texture objected based on the given frame id. - * If the frame id is not in the texture cache an error will be thrown. - * - * @static - * @method fromFrame - * @param frameId {String} The frame id of the texture - * @return Texture - */ -PIXI.Texture.fromFrame = function(frameId) -{ - var texture = PIXI.TextureCache[frameId]; - if(!texture) throw new Error('The frameId "' + frameId + '" does not exist in the texture cache '); - return texture; -}; - -/** - * Helper function that creates a new a Texture based on the given canvas element. - * - * @static - * @method fromCanvas - * @param canvas {Canvas} The canvas element source of the texture - * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values - * @return Texture - */ -PIXI.Texture.fromCanvas = function(canvas, scaleMode) -{ - var baseTexture = PIXI.BaseTexture.fromCanvas(canvas, scaleMode); - - return new PIXI.Texture( baseTexture ); - -}; - -/** - * Adds a texture to the global PIXI.TextureCache. This cache is shared across the whole PIXI object. - * - * @static - * @method addTextureToCache - * @param texture {Texture} The Texture to add to the cache. - * @param id {String} The id that the texture will be stored against. - */ -PIXI.Texture.addTextureToCache = function(texture, id) -{ - PIXI.TextureCache[id] = texture; -}; - -/** - * Remove a texture from the global PIXI.TextureCache. - * - * @static - * @method removeTextureFromCache - * @param id {String} The id of the texture to be removed - * @return {Texture} The texture that was removed - */ -PIXI.Texture.removeTextureFromCache = function(id) -{ - var texture = PIXI.TextureCache[id]; - delete PIXI.TextureCache[id]; - delete PIXI.BaseTextureCache[id]; - return texture; -}; - -PIXI.TextureUvs = function() -{ - this.x0 = 0; - this.y0 = 0; - - this.x1 = 0; - this.y1 = 0; - - this.x2 = 0; - this.y2 = 0; - - this.x3 = 0; - this.y3 = 0; -}; - -PIXI.Texture.emptyTexture = new PIXI.Texture(new PIXI.BaseTexture()); - - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * A RenderTexture is a special texture that allows any Pixi display object to be rendered to it. - * - * __Hint__: All DisplayObjects (i.e. Sprites) that render to a RenderTexture should be preloaded otherwise black rectangles will be drawn instead. - * - * A RenderTexture takes a snapshot of any Display Object given to its render method. The position and rotation of the given Display Objects is ignored. For example: - * - * var renderTexture = new PIXI.RenderTexture(800, 600); - * var sprite = PIXI.Sprite.fromImage("spinObj_01.png"); - * sprite.position.x = 800/2; - * sprite.position.y = 600/2; - * sprite.anchor.x = 0.5; - * sprite.anchor.y = 0.5; - * renderTexture.render(sprite); - * - * The Sprite in this case will be rendered to a position of 0,0. To render this sprite at its actual position a DisplayObjectContainer should be used: - * - * var doc = new PIXI.DisplayObjectContainer(); - * doc.addChild(sprite); - * renderTexture.render(doc); // Renders to center of renderTexture - * - * @class RenderTexture - * @extends Texture - * @constructor - * @param width {Number} The width of the render texture - * @param height {Number} The height of the render texture - * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used for this RenderTexture - * @param scaleMode {Number} See {{#crossLink "PIXI/scaleModes:property"}}PIXI.scaleModes{{/crossLink}} for possible values - * @param resolution {Number} The resolution of the texture being generated - */ -PIXI.RenderTexture = function(width, height, renderer, scaleMode, resolution) -{ - /** - * The with of the render texture - * - * @property width - * @type Number - */ - this.width = width || 100; - - /** - * The height of the render texture - * - * @property height - * @type Number - */ - this.height = height || 100; - - /** - * The Resolution of the texture. - * - * @property resolution - * @type Number - */ - this.resolution = resolution || 1; - - /** - * The framing rectangle of the render texture - * - * @property frame - * @type Rectangle - */ - this.frame = new PIXI.Rectangle(0, 0, this.width * this.resolution, this.height * this.resolution); - - /** - * This is the area of the BaseTexture image to actually copy to the Canvas / WebGL when rendering, - * irrespective of the actual frame size or placement (which can be influenced by trimmed texture atlases) - * - * @property crop - * @type Rectangle - */ - this.crop = new PIXI.Rectangle(0, 0, this.width * this.resolution, this.height * this.resolution); - - /** - * The base texture object that this texture uses - * - * @property baseTexture - * @type BaseTexture - */ - this.baseTexture = new PIXI.BaseTexture(); - this.baseTexture.width = this.width * this.resolution; - this.baseTexture.height = this.height * this.resolution; - this.baseTexture._glTextures = []; - this.baseTexture.resolution = this.resolution; - - this.baseTexture.scaleMode = scaleMode || PIXI.scaleModes.DEFAULT; - - this.baseTexture.hasLoaded = true; - - PIXI.Texture.call(this, - this.baseTexture, - new PIXI.Rectangle(0, 0, this.width, this.height) - ); - - /** - * The renderer this RenderTexture uses. A RenderTexture can only belong to one renderer at the moment if its webGL. - * - * @property renderer - * @type CanvasRenderer|WebGLRenderer - */ - this.renderer = renderer || PIXI.defaultRenderer; - - if(this.renderer.type === PIXI.WEBGL_RENDERER) - { - var gl = this.renderer.gl; - this.baseTexture._dirty[gl.id] = false; - - this.textureBuffer = new PIXI.FilterTexture(gl, this.width * this.resolution, this.height * this.resolution, this.baseTexture.scaleMode); - this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; - - this.render = this.renderWebGL; - this.projection = new PIXI.Point(this.width*0.5, -this.height*0.5); - } - else - { - this.render = this.renderCanvas; - this.textureBuffer = new PIXI.CanvasBuffer(this.width* this.resolution, this.height* this.resolution); - this.baseTexture.source = this.textureBuffer.canvas; - } - - /** - * @property valid - * @type Boolean - */ - this.valid = true; - - this._updateUvs(); -}; - -PIXI.RenderTexture.prototype = Object.create(PIXI.Texture.prototype); -PIXI.RenderTexture.prototype.constructor = PIXI.RenderTexture; - -/** - * Resizes the RenderTexture. - * - * @method resize - * @param width {Number} The width to resize to. - * @param height {Number} The height to resize to. - * @param updateBase {Boolean} Should the baseTexture.width and height values be resized as well? - */ -PIXI.RenderTexture.prototype.resize = function(width, height, updateBase) -{ - if (width === this.width && height === this.height)return; - - this.valid = (width > 0 && height > 0); - - this.width = this.frame.width = this.crop.width = width; - this.height = this.frame.height = this.crop.height = height; - - if (updateBase) - { - this.baseTexture.width = this.width; - this.baseTexture.height = this.height; - } - - if (this.renderer.type === PIXI.WEBGL_RENDERER) - { - this.projection.x = this.width / 2; - this.projection.y = -this.height / 2; - } - - if(!this.valid)return; - - this.textureBuffer.resize(this.width * this.resolution, this.height * this.resolution); -}; - -/** - * Clears the RenderTexture. - * - * @method clear - */ -PIXI.RenderTexture.prototype.clear = function() -{ - if(!this.valid)return; - - if (this.renderer.type === PIXI.WEBGL_RENDERER) - { - this.renderer.gl.bindFramebuffer(this.renderer.gl.FRAMEBUFFER, this.textureBuffer.frameBuffer); - } - - this.textureBuffer.clear(); -}; - -/** - * This function will draw the display object to the texture. - * - * @method renderWebGL - * @param displayObject {DisplayObject} The display object to render this texture on - * @param [matrix] {Matrix} Optional matrix to apply to the display object before rendering. - * @param [clear] {Boolean} If true the texture will be cleared before the displayObject is drawn - * @private - */ -PIXI.RenderTexture.prototype.renderWebGL = function(displayObject, matrix, clear) -{ - if(!this.valid)return; - //TOOD replace position with matrix.. - - //Lets create a nice matrix to apply to our display object. Frame buffers come in upside down so we need to flip the matrix - var wt = displayObject.worldTransform; - wt.identity(); - wt.translate(0, this.projection.y * 2); - if(matrix)wt.append(matrix); - wt.scale(1,-1); - - // setWorld Alpha to ensure that the object is renderer at full opacity - displayObject.worldAlpha = 1; - - // Time to update all the children of the displayObject with the new matrix.. - var children = displayObject.children; - - for(var i=0,j=children.length; i 'image/gif' - var info = data.slice(0, sepIdx).split(';')[0]; - - //We might need to handle some special cases here... - //standardize text/plain to 'txt' file extension - if (!info || info.toLowerCase() === 'text/plain') - return 'txt'; - - //User specified mime type, try splitting it by '/' - return info.split('/').pop().toLowerCase(); - } - - return null; -}; - -/** - * Starts loading the assets sequentially - * - * @method load - */ -PIXI.AssetLoader.prototype.load = function() -{ - var scope = this; - - function onLoad(evt) { - scope.onAssetLoaded(evt.data.content); - } - - this.loadCount = this.assetURLs.length; - - for (var i=0; i < this.assetURLs.length; i++) - { - var fileName = this.assetURLs[i]; - //first see if we have a data URI scheme.. - var fileType = this._getDataType(fileName); - - //if not, assume it's a file URI - if (!fileType) - fileType = fileName.split('?').shift().split('.').pop().toLowerCase(); - - var Constructor = this.loadersByType[fileType]; - if(!Constructor) - throw new Error(fileType + ' is an unsupported file type'); - - var loader = new Constructor(fileName, this.crossorigin); - - loader.on('loaded', onLoad); - loader.load(); - } -}; - -/** - * Invoked after each file is loaded - * - * @method onAssetLoaded - * @private - */ -PIXI.AssetLoader.prototype.onAssetLoaded = function(loader) -{ - this.loadCount--; - this.emit('onProgress', { content: this, loader: loader }); - if (this.onProgress) this.onProgress(loader); - - if (!this.loadCount) - { - this.emit('onComplete', { content: this }); - if(this.onComplete) this.onComplete(); - } -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * The json file loader is used to load in JSON data and parse it - * When loaded this class will dispatch a 'loaded' event - * If loading fails this class will dispatch an 'error' event - * - * @class JsonLoader - * @uses EventTarget - * @constructor - * @param url {String} The url of the JSON file - * @param crossorigin {Boolean} Whether requests should be treated as crossorigin - */ -PIXI.JsonLoader = function (url, crossorigin) { - /** - * The url of the bitmap font data - * - * @property url - * @type String - */ - this.url = url; - - /** - * Whether the requests should be treated as cross origin - * - * @property crossorigin - * @type Boolean - */ - this.crossorigin = crossorigin; - - /** - * [read-only] The base url of the bitmap font data - * - * @property baseUrl - * @type String - * @readOnly - */ - this.baseUrl = url.replace(/[^\/]*$/, ''); - - /** - * [read-only] Whether the data has loaded yet - * - * @property loaded - * @type Boolean - * @readOnly - */ - this.loaded = false; - -}; - -// constructor -PIXI.JsonLoader.prototype.constructor = PIXI.JsonLoader; -PIXI.EventTarget.mixin(PIXI.JsonLoader.prototype); - -/** - * Loads the JSON data - * - * @method load - */ -PIXI.JsonLoader.prototype.load = function () { - - if(window.XDomainRequest && this.crossorigin) - { - this.ajaxRequest = new window.XDomainRequest(); - - // XDomainRequest has a few quirks. Occasionally it will abort requests - // A way to avoid this is to make sure ALL callbacks are set even if not used - // More info here: http://stackoverflow.com/questions/15786966/xdomainrequest-aborts-post-on-ie-9 - this.ajaxRequest.timeout = 3000; - - this.ajaxRequest.onerror = this.onError.bind(this); - - this.ajaxRequest.ontimeout = this.onError.bind(this); - - this.ajaxRequest.onprogress = function() {}; - - this.ajaxRequest.onload = this.onJSONLoaded.bind(this); - } - else - { - if (window.XMLHttpRequest) - { - this.ajaxRequest = new window.XMLHttpRequest(); - } - else - { - this.ajaxRequest = new window.ActiveXObject('Microsoft.XMLHTTP'); - } - - this.ajaxRequest.onreadystatechange = this.onReadyStateChanged.bind(this); - } - - this.ajaxRequest.open('GET',this.url,true); - - this.ajaxRequest.send(); -}; - -/** - * Bridge function to be able to use the more reliable onreadystatechange in XMLHttpRequest. - * - * @method onReadyStateChanged - * @private - */ -PIXI.JsonLoader.prototype.onReadyStateChanged = function () { - if (this.ajaxRequest.readyState === 4 && (this.ajaxRequest.status === 200 || window.location.href.indexOf('http') === -1)) { - this.onJSONLoaded(); - } -}; - -/** - * Invoke when JSON file is loaded - * - * @method onJSONLoaded - * @private - */ -PIXI.JsonLoader.prototype.onJSONLoaded = function () { - - if(!this.ajaxRequest.responseText ) - { - this.onError(); - return; - } - - this.json = JSON.parse(this.ajaxRequest.responseText); - - if(this.json.frames && this.json.meta && this.json.meta.image) - { - // sprite sheet - var textureUrl = this.baseUrl + this.json.meta.image; - var image = new PIXI.ImageLoader(textureUrl, this.crossorigin); - var frameData = this.json.frames; - - this.texture = image.texture.baseTexture; - image.addEventListener('loaded', this.onLoaded.bind(this)); - - for (var i in frameData) - { - var rect = frameData[i].frame; - - if (rect) - { - var textureSize = new PIXI.Rectangle(rect.x, rect.y, rect.w, rect.h); - var crop = textureSize.clone(); - var trim = null; - - // Check to see if the sprite is trimmed - if (frameData[i].trimmed) - { - var actualSize = frameData[i].sourceSize; - var realSize = frameData[i].spriteSourceSize; - trim = new PIXI.Rectangle(realSize.x, realSize.y, actualSize.w, actualSize.h); - } - PIXI.TextureCache[i] = new PIXI.Texture(this.texture, textureSize, crop, trim); - } - } - - image.load(); - - } - else if(this.json.bones) - { - /* check if the json was loaded before */ - if (PIXI.AnimCache[this.url]) - { - this.onLoaded(); - } - else - { - /* use a bit of hackery to load the atlas file, here we assume that the .json, .atlas and .png files - * that correspond to the spine file are in the same base URL and that the .json and .atlas files - * have the same name - */ - var atlasPath = this.url.substr(0, this.url.lastIndexOf('.')) + '.atlas'; - var atlasLoader = new PIXI.JsonLoader(atlasPath, this.crossorigin); - // save a copy of the current object for future reference // - var originalLoader = this; - // before loading the file, replace the "onJSONLoaded" function for our own // - atlasLoader.onJSONLoaded = function() - { - // at this point "this" points at the atlasLoader (JsonLoader) instance // - if(!this.ajaxRequest.responseText) - { - this.onError(); // FIXME: hmm, this is funny because we are not responding to errors yet - return; - } - // create a new instance of a spine texture loader for this spine object // - var textureLoader = new PIXI.SpineTextureLoader(this.url.substring(0, this.url.lastIndexOf('/'))); - // create a spine atlas using the loaded text and a spine texture loader instance // - var spineAtlas = new spine.Atlas(this.ajaxRequest.responseText, textureLoader); - // now we use an atlas attachment loader // - var attachmentLoader = new spine.AtlasAttachmentLoader(spineAtlas); - // spine animation - var spineJsonParser = new spine.SkeletonJson(attachmentLoader); - var skeletonData = spineJsonParser.readSkeletonData(originalLoader.json); - PIXI.AnimCache[originalLoader.url] = skeletonData; - originalLoader.spine = skeletonData; - originalLoader.spineAtlas = spineAtlas; - originalLoader.spineAtlasLoader = atlasLoader; - // wait for textures to finish loading if needed - if (textureLoader.loadingCount > 0) - { - textureLoader.addEventListener('loadedBaseTexture', function(evt){ - if (evt.content.content.loadingCount <= 0) - { - originalLoader.onLoaded(); - } - }); - } - else - { - originalLoader.onLoaded(); - } - }; - // start the loading // - atlasLoader.load(); - } - } - else - { - this.onLoaded(); - } -}; - -/** - * Invoke when json file loaded - * - * @method onLoaded - * @private - */ -PIXI.JsonLoader.prototype.onLoaded = function () { - this.loaded = true; - this.dispatchEvent({ - type: 'loaded', - content: this - }); -}; - -/** - * Invoke when error occured - * - * @method onError - * @private - */ -PIXI.JsonLoader.prototype.onError = function () { - - this.dispatchEvent({ - type: 'error', - content: this - }); -}; - -/** - * @author Martin Kelm http://mkelm.github.com - */ - -/** - * The atlas file loader is used to load in Texture Atlas data and parse it. When loaded this class will dispatch a 'loaded' event. If loading fails this class will dispatch an 'error' event. - * - * To generate the data you can use http://www.codeandweb.com/texturepacker and publish in the 'JSON' format. - * - * It is highly recommended to use texture atlases (also know as 'sprite sheets') as it allowed sprites to be batched and drawn together for highly increased rendering speed. - * Once the data has been loaded the frames are stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFrameId() - * - * @class AtlasLoader - * @uses EventTarget - * @constructor - * @param url {String} The url of the JSON file - * @param crossorigin {Boolean} Whether requests should be treated as crossorigin - */ -PIXI.AtlasLoader = function (url, crossorigin) { - this.url = url; - this.baseUrl = url.replace(/[^\/]*$/, ''); - this.crossorigin = crossorigin; - this.loaded = false; - -}; - -// constructor -PIXI.AtlasLoader.constructor = PIXI.AtlasLoader; - -PIXI.EventTarget.mixin(PIXI.AtlasLoader.prototype); - - /** - * Starts loading the JSON file - * - * @method load - */ -PIXI.AtlasLoader.prototype.load = function () { - this.ajaxRequest = new PIXI.AjaxRequest(); - this.ajaxRequest.onreadystatechange = this.onAtlasLoaded.bind(this); - - this.ajaxRequest.open('GET', this.url, true); - if (this.ajaxRequest.overrideMimeType) this.ajaxRequest.overrideMimeType('application/json'); - this.ajaxRequest.send(null); -}; - -/** - * Invoked when the Atlas has fully loaded. Parses the JSON and builds the texture frames. - * - * @method onAtlasLoaded - * @private - */ -PIXI.AtlasLoader.prototype.onAtlasLoaded = function () { - if (this.ajaxRequest.readyState === 4) { - if (this.ajaxRequest.status === 200 || window.location.href.indexOf('http') === -1) { - this.atlas = { - meta : { - image : [] - }, - frames : [] - }; - var result = this.ajaxRequest.responseText.split(/\r?\n/); - var lineCount = -3; - - var currentImageId = 0; - var currentFrame = null; - var nameInNextLine = false; - - var i = 0, - j = 0, - selfOnLoaded = this.onLoaded.bind(this); - - // parser without rotation support yet! - for (i = 0; i < result.length; i++) { - result[i] = result[i].replace(/^\s+|\s+$/g, ''); - if (result[i] === '') { - nameInNextLine = i+1; - } - if (result[i].length > 0) { - if (nameInNextLine === i) { - this.atlas.meta.image.push(result[i]); - currentImageId = this.atlas.meta.image.length - 1; - this.atlas.frames.push({}); - lineCount = -3; - } else if (lineCount > 0) { - if (lineCount % 7 === 1) { // frame name - if (currentFrame != null) { //jshint ignore:line - this.atlas.frames[currentImageId][currentFrame.name] = currentFrame; - } - currentFrame = { name: result[i], frame : {} }; - } else { - var text = result[i].split(' '); - if (lineCount % 7 === 3) { // position - currentFrame.frame.x = Number(text[1].replace(',', '')); - currentFrame.frame.y = Number(text[2]); - } else if (lineCount % 7 === 4) { // size - currentFrame.frame.w = Number(text[1].replace(',', '')); - currentFrame.frame.h = Number(text[2]); - } else if (lineCount % 7 === 5) { // real size - var realSize = { - x : 0, - y : 0, - w : Number(text[1].replace(',', '')), - h : Number(text[2]) - }; - - if (realSize.w > currentFrame.frame.w || realSize.h > currentFrame.frame.h) { - currentFrame.trimmed = true; - currentFrame.realSize = realSize; - } else { - currentFrame.trimmed = false; - } - } - } - } - lineCount++; - } - } - - if (currentFrame != null) { //jshint ignore:line - this.atlas.frames[currentImageId][currentFrame.name] = currentFrame; - } - - if (this.atlas.meta.image.length > 0) { - this.images = []; - for (j = 0; j < this.atlas.meta.image.length; j++) { - // sprite sheet - var textureUrl = this.baseUrl + this.atlas.meta.image[j]; - var frameData = this.atlas.frames[j]; - this.images.push(new PIXI.ImageLoader(textureUrl, this.crossorigin)); - - for (i in frameData) { - var rect = frameData[i].frame; - if (rect) { - PIXI.TextureCache[i] = new PIXI.Texture(this.images[j].texture.baseTexture, { - x: rect.x, - y: rect.y, - width: rect.w, - height: rect.h - }); - if (frameData[i].trimmed) { - PIXI.TextureCache[i].realSize = frameData[i].realSize; - // trim in pixi not supported yet, todo update trim properties if it is done ... - PIXI.TextureCache[i].trim.x = 0; - PIXI.TextureCache[i].trim.y = 0; - } - } - } - } - - this.currentImageId = 0; - for (j = 0; j < this.images.length; j++) { - this.images[j].on('loaded', selfOnLoaded); - } - this.images[this.currentImageId].load(); - - } else { - this.onLoaded(); - } - - } else { - this.onError(); - } - } -}; - -/** - * Invoked when json file has loaded. - * - * @method onLoaded - * @private - */ -PIXI.AtlasLoader.prototype.onLoaded = function () { - if (this.images.length - 1 > this.currentImageId) { - this.currentImageId++; - this.images[this.currentImageId].load(); - } else { - this.loaded = true; - this.emit('loaded', { content: this }); - } -}; - -/** - * Invoked when an error occurs. - * - * @method onError - * @private - */ -PIXI.AtlasLoader.prototype.onError = function () { - this.emit('error', { content: this }); -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * The sprite sheet loader is used to load in JSON sprite sheet data - * To generate the data you can use http://www.codeandweb.com/texturepacker and publish in the 'JSON' format - * There is a free version so thats nice, although the paid version is great value for money. - * It is highly recommended to use Sprite sheets (also know as a 'texture atlas') as it means sprites can be batched and drawn together for highly increased rendering speed. - * Once the data has been loaded the frames are stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFrameId() - * This loader will load the image file that the Spritesheet points to as well as the data. - * When loaded this class will dispatch a 'loaded' event - * - * @class SpriteSheetLoader - * @uses EventTarget - * @constructor - * @param url {String} The url of the sprite sheet JSON file - * @param crossorigin {Boolean} Whether requests should be treated as crossorigin - */ -PIXI.SpriteSheetLoader = function (url, crossorigin) { - - /** - * The url of the atlas data - * - * @property url - * @type String - */ - this.url = url; - - /** - * Whether the requests should be treated as cross origin - * - * @property crossorigin - * @type Boolean - */ - this.crossorigin = crossorigin; - - /** - * [read-only] The base url of the bitmap font data - * - * @property baseUrl - * @type String - * @readOnly - */ - this.baseUrl = url.replace(/[^\/]*$/, ''); - - /** - * The texture being loaded - * - * @property texture - * @type Texture - */ - this.texture = null; - - /** - * The frames of the sprite sheet - * - * @property frames - * @type Object - */ - this.frames = {}; -}; - -// constructor -PIXI.SpriteSheetLoader.prototype.constructor = PIXI.SpriteSheetLoader; - -PIXI.EventTarget.mixin(PIXI.SpriteSheetLoader.prototype); - -/** - * This will begin loading the JSON file - * - * @method load - */ -PIXI.SpriteSheetLoader.prototype.load = function () { - var scope = this; - var jsonLoader = new PIXI.JsonLoader(this.url, this.crossorigin); - jsonLoader.on('loaded', function (event) { - scope.json = event.data.content.json; - scope.onLoaded(); - }); - jsonLoader.load(); -}; - -/** - * Invoke when all files are loaded (json and texture) - * - * @method onLoaded - * @private - */ -PIXI.SpriteSheetLoader.prototype.onLoaded = function () { - this.emit('loaded', { - content: this - }); -}; - -/** - * @author Mat Groves http://matgroves.com/ @Doormat23 - */ - -/** - * The image loader class is responsible for loading images file formats ('jpeg', 'jpg', 'png' and 'gif') - * Once the image has been loaded it is stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrame() and PIXI.Sprite.fromFrame() - * When loaded this class will dispatch a 'loaded' event - * - * @class ImageLoader - * @uses EventTarget - * @constructor - * @param url {String} The url of the image - * @param crossorigin {Boolean} Whether requests should be treated as crossorigin - */ -PIXI.ImageLoader = function(url, crossorigin) -{ - /** - * The texture being loaded - * - * @property texture - * @type Texture - */ - this.texture = PIXI.Texture.fromImage(url, crossorigin); - - /** - * if the image is loaded with loadFramedSpriteSheet - * frames will contain the sprite sheet frames - * - * @property frames - * @type Array - * @readOnly - */ - this.frames = []; -}; - -// constructor -PIXI.ImageLoader.prototype.constructor = PIXI.ImageLoader; - -PIXI.EventTarget.mixin(PIXI.ImageLoader.prototype); - -/** - * Loads image or takes it from cache - * - * @method load - */ -PIXI.ImageLoader.prototype.load = function() -{ - if(!this.texture.baseTexture.hasLoaded) - { - this.texture.baseTexture.on('loaded', this.onLoaded.bind(this)); - } - else - { - this.onLoaded(); - } -}; - -/** - * Invoked when image file is loaded or it is already cached and ready to use - * - * @method onLoaded - * @private - */ -PIXI.ImageLoader.prototype.onLoaded = function() -{ - this.emit('loaded', { content: this }); -}; - -/** - * Loads image and split it to uniform sized frames - * - * @method loadFramedSpriteSheet - * @param frameWidth {Number} width of each frame - * @param frameHeight {Number} height of each frame - * @param textureName {String} if given, the frames will be cached in - format - */ -PIXI.ImageLoader.prototype.loadFramedSpriteSheet = function(frameWidth, frameHeight, textureName) -{ - this.frames = []; - var cols = Math.floor(this.texture.width / frameWidth); - var rows = Math.floor(this.texture.height / frameHeight); - - var i=0; - for (var y=0; y> 16) & 255; + var g = (intColor >> 8) & 255; + var b = intColor & 255; + + return 'rgb(' + r + ', ' + g + ', ' + b + ')'; +}; }, {}], 5: [function(require, module, exports) { +;(function () { + function Link () { + this.x1 = 0; + this.y1 = 0; + this.x2 = 0; + this.y2 = 0; + this.color = 0; + return this; + } + + Link.prototype.update = function (x1, y1, x2, y2, color) { + this.x1 = x1; + this.y1 = y1; + this.x2 = x2; + this.y2 = y2; + this.color = color; + return this; + }; + + if (module && module.exports) module.exports = Link; +})(); + +}, {}], +6: [function(require, module, exports) { +;(function () { + function Node () { + this.x = 0; + this.y = 0; + this.r = 10; + this.color = 0; + return this; + } + + Node.prototype.update = function (x, y, r, color) { + this.x = x; + this.y = y; + this.r = r; + this.color = color; + return this; + }; + + if (module && module.exports) module.exports = Node; +})(); + +}, {}], +7: [function(require, module, exports) { /** * Utilities * ========= @@ -21141,7 +1179,7 @@ var Utilities = module.exports = { * * A function that does nothing. */ -function noop () {}; +function noop () {} /** * each @@ -21159,7 +1197,7 @@ function each (arr, fn, ctx) { fn(arr[i], i); } return arr; -}; +} /** * eachPop @@ -21176,7 +1214,7 @@ function eachPop (arr, fn, ctx) { fn(arr.pop()); } return arr; -}; +} /** * eachKey @@ -21198,7 +1236,7 @@ function eachKey (obj, fn, ctx) { } } return obj; -}; +} /** * map @@ -21217,7 +1255,7 @@ function map (arr, fn, ctx) { mapped[i] = fn(arr[i], i); } return mapped; -}; +} /** * clean @@ -21231,7 +1269,7 @@ function map (arr, fn, ctx) { function clean (arr) { eachPop(arr, noop); return arr; -}; +} /** * range @@ -21253,7 +1291,7 @@ function range (start, end, step) { result[i] = start + (step * i); } return result; -}; +} /** * sortedIndex @@ -21272,7 +1310,7 @@ function sortedIndex (arr, n) { } return min; -}; +} /** * indexOf @@ -21287,7 +1325,7 @@ function indexOf (arr, n) { if (arr[i] === n) return i; } return i; -}; +} /** * uniqueInsert @@ -21299,7 +1337,7 @@ function indexOf (arr, n) { function uniqueInsert (arr, n) { if (indexOf(arr, n) === -1) arr.push(n); return arr; -}; +} /** * extend @@ -21317,7 +1355,7 @@ function extend (obj, source) { } } return obj; -}; +} /** * bind @@ -21329,7 +1367,7 @@ function extend (obj, source) { function bind (fn, ctx) { if (!ctx) return fn; return function () { return fn.apply(ctx, arguments); }; -}; +} /** * isUndefined @@ -21339,7 +1377,7 @@ function bind (fn, ctx) { */ function isUndefined (o) { return typeof o === 'undefined'; -}; +} /** * isFunction @@ -21349,7 +1387,7 @@ function isUndefined (o) { */ function isFunction (o) { return typeof o === 'function'; -}; +} /** * isObject @@ -21359,7 +1397,7 @@ function isFunction (o) { */ function isObject (o) { return typeof o === 'object' && !!o; -}; +} /** * isNumber @@ -21369,7 +1407,7 @@ function isObject (o) { */ function isNumber (o) { return typeof o === 'number'; -}; +} /** * isNaN @@ -21379,6 +1417,7 @@ function isNumber (o) { */ function isNaN (o) { return isNumber(o) && o !== +o; -}; +} }, {}]}, {}, {"1":""}) +); \ No newline at end of file diff --git a/component.json b/component.json index eeec5c2..044db62 100644 --- a/component.json +++ b/component.json @@ -1,9 +1,9 @@ { "name": "grapher", "repository": "ayasdi/grapher", - "description": "WebGL/Canvas Network Graphing using PIXI", - "version": "0.0.1", + "description": "WebGL/Canvas Network Graphs", + "version": "1.0.0", "dependencies": {}, - "scripts": ["index.js"], - "license": "not sure yet" -} \ No newline at end of file + "scripts": ["modules/grapher.js"], + "license": "Apache 2.0" +} diff --git a/doc/assets/behavior.js b/doc/assets/behavior.js index d5b17c1..7d52657 100644 --- a/doc/assets/behavior.js +++ b/doc/assets/behavior.js @@ -21,8 +21,8 @@ f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3 "type": "heading", "data": { "level": 1, - "title": "Ayasdi.Grapher", - "slug": "ayasdigrapher", + "title": "Grapher", + "slug": "grapher", "isFileHeader": true }, "depth": 1, @@ -35,6 +35,22 @@ f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3 "slug": "download" }, "depth": 2 + }, { + "type": "heading", + "data": { + "level": 2, + "title": "Installing", + "slug": "installing" + }, + "depth": 2 + }, { + "type": "heading", + "data": { + "level": 2, + "title": "Additional Modules", + "slug": "additional-modules" + }, + "depth": 2 }, { "type": "heading", "data": { @@ -62,7 +78,7 @@ f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3 } ] }, - "title": "Ayasdi.Grapher" + "title": "Grapher" }, "depth": 1, "outline": [ @@ -74,6 +90,22 @@ f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3 "slug": "download" }, "depth": 2 + }, { + "type": "heading", + "data": { + "level": 2, + "title": "Installing", + "slug": "installing" + }, + "depth": 2 + }, { + "type": "heading", + "data": { + "level": 2, + "title": "Additional Modules", + "slug": "additional-modules" + }, + "depth": 2 }, { "type": "heading", "data": { @@ -100,26 +132,6 @@ f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3 "depth": 2 } ] - }, { - "type": "file", - "data": { - "language": { - "nameMatchers": [".js"], - "pygmentsLexer": "javascript", - "multiLineComment": ["/*", "*", "*/"], - "singleLineComment": ["//"], - "ignorePrefix": "}", - "foldPrefix": "^", - "name": "JavaScript" - }, - "sourcePath": "/Users/CZhang/Documents/ayasdi/grapher/modules/color.js", - "projectPath": "modules/color.js", - "targetPath": "color", - "pageTitle": "color", - "title": "color" - }, - "depth": 1, - "outline": [] }, { "type": "file", "data": { @@ -144,16 +156,16 @@ f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3 "type": "heading", "data": { "level": 1, - "title": "Dependencies", - "slug": "dependencies" + "title": "Grapher", + "slug": "grapher" }, "depth": 1 }, { "type": "heading", "data": { "level": 1, - "title": "Grapher", - "slug": "grapher" + "title": "Helpers and Renderers", + "slug": "helpers-and-renderers" }, "depth": 1, "children": [ @@ -189,14 +201,6 @@ f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3 "slug": "grapheroff" }, "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "grapher.palette", - "slug": "grapherpalette" - }, - "depth": 2 }, { "type": "heading", "data": { @@ -289,64 +293,64 @@ f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3 "type": "heading", "data": { "level": 2, - "title": "grapher.center", - "slug": "graphercenter" + "title": "grapher.width", + "slug": "grapherwidth" }, "depth": 2 }, { "type": "heading", "data": { "level": 2, - "title": "grapher.transform", - "slug": "graphertransform" + "title": "grapher.height", + "slug": "grapherheight" }, "depth": 2 }, { "type": "heading", "data": { "level": 2, - "title": "grapher.scale", - "slug": "grapherscale" + "title": "grapher.transform", + "slug": "graphertransform" }, "depth": 2 }, { "type": "heading", "data": { "level": 2, - "title": "grapher.translate", - "slug": "graphertranslate" + "title": "grapher.scale", + "slug": "grapherscale" }, "depth": 2 }, { "type": "heading", "data": { "level": 2, - "title": "grapher.backgroundColor", - "slug": "grapherbackgroundcolor" + "title": "grapher.translate", + "slug": "graphertranslate" }, "depth": 2 }, { "type": "heading", "data": { "level": 2, - "title": "grapher.foregroundColor", - "slug": "grapherforegroundcolor" + "title": "grapher.color", + "slug": "graphercolor" }, "depth": 2 }, { "type": "heading", "data": { "level": 2, - "title": "grapher.lineWidth", - "slug": "grapherlinewidth" + "title": "grapher.getDataPosition", + "slug": "graphergetdataposition" }, "depth": 2 }, { "type": "heading", "data": { "level": 2, - "title": "grapher.getNodeIdAt", - "slug": "graphergetnodeidat" + "title": "grapher.getDisplayPosition", + "slug": "graphergetdisplayposition" }, "depth": 2 } @@ -361,22 +365,6 @@ f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3 "depth": 1, "children": [ { - "type": "heading", - "data": { - "level": 2, - "title": "grapher._exit", - "slug": "grapher-exit" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "grapher._enter", - "slug": "grapher-enter" - }, - "depth": 2 - }, { "type": "heading", "data": { "level": 2, @@ -420,24 +408,8 @@ f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3 "type": "heading", "data": { "level": 2, - "title": "grapher._setColor", - "slug": "grapher-setcolor" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "grapher._getBatch", - "slug": "grapher-getbatch" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "grapher._onEvent", - "slug": "grapher-onevent" + "title": "grapher._getWebGL", + "slug": "grapher-getwebgl" }, "depth": 2 }, { @@ -466,348 +438,6 @@ f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3 "slug": "grapher-static-properties" }, "depth": 1 - }, { - "type": "heading", - "data": { - "level": 1, - "title": "Grapher Static Methods", - "slug": "grapher-static-methods" - }, - "depth": 1, - "children": [ - { - "type": "heading", - "data": { - "level": 2, - "title": "Grapher.getPalette", - "slug": "graphergetpalette" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "Grapher.setPalette", - "slug": "graphersetpalette" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "Grapher.getTexture", - "slug": "graphergettexture" - }, - "depth": 2 - } - ] - } - ] - }, { - "type": "file", - "data": { - "language": { - "nameMatchers": [".js"], - "pygmentsLexer": "javascript", - "multiLineComment": ["/*", "*", "*/"], - "singleLineComment": ["//"], - "ignorePrefix": "}", - "foldPrefix": "^", - "name": "JavaScript" - }, - "sourcePath": "/Users/CZhang/Documents/ayasdi/grapher/modules/utilities.js", - "projectPath": "modules/utilities.js", - "targetPath": "utilities", - "pageTitle": "utilities", - "firstHeader": { - "type": "heading", - "data": { - "level": 1, - "title": "Utilities", - "slug": "utilities", - "isFileHeader": true - }, - "depth": 1, - "children": [ - { - "type": "heading", - "data": { - "level": 2, - "title": "noop", - "slug": "noop" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "each", - "slug": "each" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "eachPop", - "slug": "eachpop" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "eachKey", - "slug": "eachkey" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "map", - "slug": "map" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "clean", - "slug": "clean" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "range", - "slug": "range" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "sortedIndex", - "slug": "sortedindex" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "indexOf", - "slug": "indexof" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "uniqueInsert", - "slug": "uniqueinsert" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "extend", - "slug": "extend" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "bind", - "slug": "bind" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "isUndefined", - "slug": "isundefined" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "isFunction", - "slug": "isfunction" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "isObject", - "slug": "isobject" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "isNumber", - "slug": "isnumber" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "isNaN", - "slug": "isnan" - }, - "depth": 2 - } - ] - }, - "title": "Utilities" - }, - "depth": 1, - "outline": [ - { - "type": "heading", - "data": { - "level": 2, - "title": "noop", - "slug": "noop" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "each", - "slug": "each" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "eachPop", - "slug": "eachpop" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "eachKey", - "slug": "eachkey" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "map", - "slug": "map" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "clean", - "slug": "clean" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "range", - "slug": "range" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "sortedIndex", - "slug": "sortedindex" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "indexOf", - "slug": "indexof" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "uniqueInsert", - "slug": "uniqueinsert" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "extend", - "slug": "extend" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "bind", - "slug": "bind" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "isUndefined", - "slug": "isundefined" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "isFunction", - "slug": "isfunction" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "isObject", - "slug": "isobject" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "isNumber", - "slug": "isnumber" - }, - "depth": 2 - }, { - "type": "heading", - "data": { - "level": 2, - "title": "isNaN", - "slug": "isnan" - }, - "depth": 2 } ] } diff --git a/doc/color.html b/doc/color.html deleted file mode 100644 index 608fd6c..0000000 --- a/doc/color.html +++ /dev/null @@ -1,42 +0,0 @@ -color
modules/color.js

Ayasdi Inc. Copyright 2014 -Color.js may be freely distributed under the Apache 2.0 license

var Color = module.exports = { - hexToRgb: hexToRgb, - rgbToHex: rgbToHex, - interpolate: interpolate, - parse: parse -}; - -function hexToRgb (hex) { - return {r: (hex >> 16) & 0xff, g: (hex >> 8) & 0xff, b: hex & 0xff}; -}; - -function rgbToHex (r, g, b) { - return r << 16 | g << 8 | b; -}; - -function interpolate (a, b, amt) { - amt = amt === undefined ? 0.5 : amt; - var colorA = hexToRgb(a), - colorB = hexToRgb(b), - interpolated = { - r: colorA.r + (colorB.r - colorA.r) * amt, - g: colorA.g + (colorB.g - colorA.g) * amt, - b: colorA.b + (colorB.b - colorA.b) * amt - }; - return rgbToHex(interpolated.r, interpolated.g, interpolated.b); -}; - -function parse (c) { - var color = parseInt(c); - if (typeof c === 'string') { - if (c.split('#').length > 1) { // hex format '#ffffff' - color = parseInt(c.replace('#', ''), 16); - } - - else if (c.split('rgb(').length > 1) { // rgb format 'rgb(255, 255, 255)' - var rgb = c.substring(4, c.length-1).replace(/ /g, '').split(','); - color = rgbToHex(rgb[0], rgb[1], rgb[2]); - } - } - return color; -};
\ No newline at end of file diff --git a/doc/grapher.html b/doc/grapher.html index a179eea..2873514 100644 --- a/doc/grapher.html +++ b/doc/grapher.html @@ -1,50 +1,55 @@ grapher
modules/grapher.js

Ayasdi Inc. Copyright 2014 -Grapher.js may be freely distributed under the Apache 2.0 license

;(function () {

Dependencies

- -

Grapher uses PIXI.js as a dependency and uses Color and Utilities found in -modules.

var PIXI = require('./vendor/pixi.js'), - Color = require('./color.js'), - u = require('./utilities.js');

We suppress PIXI's initial console log.

PIXI.dontSayHello = true;

Grapher

+Grapher.js may be freely distributed under the Apache 2.0 license

;(function () { +

Grapher

WebGL network grapher rendering with PIXI

function Grapher () { this.initialize.apply(this, arguments); return this; - } + }

Helpers and Renderers

+ +

Load helpers and renderers.

var WebGLRenderer = Grapher.WebGLRenderer = require('./renderers/gl/renderer.js'), + CanvasRenderer = Grapher.CanvasRenderer = require('./renderers/canvas/renderer.js'), + Color = Grapher.Color = require('./helpers/color.js'), + Link = Grapher.Link = require('./helpers/link.js'), + Node = Grapher.Node = require('./helpers/node.js'), + u = Grapher.utils = require('./helpers/utilities.js'); - if (module && module.exports) module.exports = Grapher; Grapher.prototype = {};

grapher.initialize

Initialize is called when a grapher instance is created:

var grapher = new Grapher(width, height, options);
-
Grapher.prototype.initialize = function (width, height, o) {

Extend default properties with options

this.props = u.extend({ - lineWidth: 2, - foregroundColor: 0x222222, - backgroundColor: 0xffffff, - antialias: true, - resolution: typeof devicePixelRatio !== 'undefined' ? Math.max(devicePixelRatio, 1) : 1 +
Grapher.prototype.initialize = function (o) { + if (!o) o = {}; +

Extend default properties with options

this.props = u.extend({ + color: 0x222222, + scale: 1, + translate: [0, 0], + resolution: window.devicePixelRatio || 1 }, o); - this.width = width; - this.height = height;

Renderer and view

this.rendered = false; - this.renderer = PIXI.autoDetectRenderer(width, height, this.props); - this.view = this.renderer.view;

Stage and containers

this.stage = new PIXI.Stage(this.backgroundColor()); - this.network = new PIXI.DisplayObjectContainer(); - this.stage.addChild(this.network);

SpriteBatch containers

this.batches = {}; - this.batches.nodes = {}; - this.batches.links = {};

Sprite array

this.links = []; - this.nodes = [];

indices that will update

this.willUpdate = {}; + if (!o.canvas) this.props.canvas = document.createElement('canvas'); + if (!o.width) this.props.width = this.props.canvas.clientWidth; + if (!o.height) this.props.height = this.props.canvas.clientHeight; + this.canvas = this.props.canvas; + + var webGL = this._getWebGL(); + if (webGL) { + this.props.webGL = webGL; + this.props.canvas.addEventListener('webglcontextlost', function (e) { this._onContextLost(e); }.bind(this)); + this.props.canvas.addEventListener('webglcontextrestored', function (e) { this._onContextRestored(e); }.bind(this)); + }

Renderer and view

this.renderer = webGL ? new WebGLRenderer(this.props) : new CanvasRenderer(this.props); + this.rendered = false;

Initialize sizes

this.resize(this.props.width, this.props.height);

Sprite array

this.links = []; + this.nodes = []; + + this.renderer.setLinks(this.links); + this.renderer.setNodes(this.nodes);

Indices that will update

this.willUpdate = {}; this.updateAll = {}; - this._clearUpdateQueue();

Set initial transform

this.center(); - this.hasModifiedTransform = false;

Bind some updaters

this._updateLink = u.bind(this._updateLink, this); + this._clearUpdateQueue();

Bind some updaters

this._updateLink = u.bind(this._updateLink, this); this._updateNode = u.bind(this._updateNode, this); this._updateLinkByIndex = u.bind(this._updateLinkByIndex, this); this._updateNodeByIndex = u.bind(this._updateNodeByIndex, this); - this.animate = u.bind(this.animate, this);

Listeners

this.listeners = {};

Interactions

this.stage.interactive = true; - this.stage.mousedown = this._onEvent('mousedown'); - this.stage.mousemove = this._onEvent('mousemove'); - this.stage.mouseup = this._onEvent('mouseup');

Add lost context listeners

this.view.addEventListener('webglcontextlost', u.bind(this._onContextLost, this)); - this.view.addEventListener('webglcontextrestored', u.bind(this._onContextRestored, this));

Do any additional setup

u.eachKey(o, this.set, this); + this.animate = u.bind(this.animate, this);

Event Handlers

this.handlers = {};

Do any additional setup

u.eachKey(o, this.set, this); };

grapher.set

General setter for a grapher's properties.

@@ -64,21 +69,16 @@
  • mouseover
  • mouseup
  • Grapher.prototype.on = function (event, fn) { - if (u.isFunction(fn)) this.listeners[event] = fn; + this.handlers[event] = this.handlers[event] || []; + this.handlers[event].push(fn); + this.canvas.addEventListener(event, fn, false); return this; };

    grapher.off

    -

    Remove a listener from an event.

    Grapher.prototype.off = function (event) { - if (event in this.listeners) this.listeners[event] = u.noop; - return this; - };

    grapher.palette

    - -

    Set a grapher to use a pre-defined palette. Palettes can be pre-defined -with the static function Grapher.setPalette.

    Grapher.prototype.palette = function (name) { - if (u.isUndefined(name)) return this.props.palette; - - this.props.palette = Grapher.getPalette(name); - this.update(); +

    Remove a listener from an event.

    Grapher.prototype.off = function (event, fn) { + var i = u.indexOf(this.handlers[event], fn); + if (i > -1) this.handlers[event].splice(i, 1); + this.canvas.removeEventListener(event, fn, false); return this; };

    grapher.data

    @@ -96,21 +96,22 @@ this.enter(); this.update(); - if (!this.hasModifiedTransform) this.center(); return this; };

    grapher.enter

    Creates node and link sprites to match the number of nodes and links in the data.

    Grapher.prototype.enter = function () { - var data = this.data(), - entering = []; + var data = this.data(); + if (this.links.length < data.links.length) { + var links = data.links.slice(this.links.length, data.links.length); + u.eachPop(links, u.bind(function () { this.links.push(new Link()); }, this)); + } - if (this.links.length < data.links.length) - entering = entering.concat(data.links.slice(this.links.length, data.links.length - this.links.length)); - if (this.nodes.length < data.nodes.length) - entering = entering.concat(data.nodes.slice(this.nodes.length, data.nodes.length - this.nodes.length)); + if (this.nodes.length < data.nodes.length) { + var nodes = data.nodes.slice(this.nodes.length, data.nodes.length); + u.eachPop(nodes, u.bind(function () { this.nodes.push(new Node()); }, this)); + } - u.each(entering, this._enter, this); return this; };

    grapher.exit

    @@ -119,12 +120,13 @@ var data = this.data(), exiting = []; - if (data.links.length < this.links.length) - exiting = exiting.concat(this.links.splice(data.links.length, this.links.length - data.links.length)); - if (data.nodes.length < this.nodes.length) - exiting = exiting.concat(this.nodes.splice(data.nodes.length, this.nodes.length - data.nodes.length)); + if (data.links.length < this.links.length) { + this.links.splice(data.links.length, this.links.length - data.links.length); + } + if (data.nodes.length < this.nodes.length) { + this.nodes.splice(data.nodes.length, this.nodes.length - data.nodes.length); + } - u.each(exiting, this._exit); return this; };

    grapher.update

    @@ -166,7 +168,7 @@

    Updates each sprite and renders the network.

    Grapher.prototype.render = function () { this.rendered = true; this._update(); - this.renderer.render(this.stage); + this.renderer.render(); return this; };

    grapher.animate

    @@ -187,44 +189,23 @@ };

    grapher.resize

    Resize the grapher view.

    Grapher.prototype.resize = function (width, height) { - this.width = width; - this.height = height; + this.props.width = width; + this.props.height = height; this.renderer.resize(width, height); return this; - };

    grapher.center

    - -

    Center the network in the view. This function modifies the scale and translate.

    Grapher.prototype.center = function () { - var x = 0, - y = 0, - scale = 1, - nodes = this.data() ? this.data().nodes : null, - numNodes = nodes ? nodes.length : 0; - - if (numNodes) { // get initial transform - var minX = Infinity, maxX = -Infinity, - minY = Infinity, maxY = -Infinity, - width = this.renderer.width / this.renderer.resolution, - height = this.renderer.height / this.renderer.resolution, - pad = 1.1, - i; - - for (i = 0; i < numNodes; i++) { - if (nodes[i].x < minX) minX = nodes[i].x; - if (nodes[i].x > maxX) maxX = nodes[i].x; - if (nodes[i].y < minY) minY = nodes[i].y; - if (nodes[i].y > maxY) maxY = nodes[i].y; - } - - var dX = maxX - minX, - dY = maxY - minY; + };

    grapher.width

    - scale = Math.min(width / dX, height / dY, 2) / pad; - x = (width - dX * scale) / 2 - minX * scale; - y = (height - dY * scale) / 2 - minY * scale; - } +

    Specify or retrieve the width.

    Grapher.prototype.width = function (width) { + if (u.isUndefined(width)) return this.props.width; + this.resize(width, this.props.height); + return this; + };

    grapher.height

    - return this.scale(scale).translate([x, y]); +

    Specify or retrieve the height.

    Grapher.prototype.height = function (height) { + if (u.isUndefined(height)) return this.props.height; + this.resize(this.props.width, height); + return this; };

    grapher.transform

    Set the scale and translate as an object. @@ -242,7 +223,6 @@ if (u.isUndefined(scale)) return this.props.scale; if (u.isNumber(scale)) this.props.scale = scale; this.updateTransform = true; - this.hasModifiedTransform = true; return this; };

    grapher.translate

    @@ -251,61 +231,33 @@ if (u.isUndefined(translate)) return this.props.translate; if (u.isArray(translate)) this.props.translate = translate; this.updateTransform = true; - this.hasModifiedTransform = true; return this; - };

    grapher.backgroundColor

    + };

    grapher.color

    -

    Set the backgroundColor. This is the color of the background. -If no arguments are passed in, returns the current backgroundColor.

    Grapher.prototype.backgroundColor = function (color) { - if (u.isUndefined(color)) return this.props.backgroundColor; - this.props.backgroundColor = Color.parse(color); - this.stage.setBackgroundColor(this.props.backgroundColor); +

    Set the default color of nodes and links. +If no arguments are passed in, returns the current default color.

    Grapher.prototype.color = function (color) { + if (u.isUndefined(color)) return this.props.color; + this.props.color = Color.parse(color); return this; - };

    grapher.foregroundColor

    - -

    Set the foregroundColor. This is the default color of nodes and links. -If no arguments are passed in, returns the current foregroundColor.

    Grapher.prototype.foregroundColor = function (color) { - if (u.isUndefined(color)) return this.props.foregroundColor; - this.props.foregroundColor = Color.parse(color); - return this; - };

    grapher.lineWidth

    - -

    Set the lineWidth. This is the line width of the links. -If no arguments are passed in, returns the current lineWidth.

    Grapher.prototype.lineWidth = function (size) { - if (u.isUndefined(size)) return this.props.lineWidth; - this.props.lineWidth = size; - return this; - };

    grapher.getNodeIdAt

    - -

    Search for a node index at a certain point.

    - -
    var point = {x: 10, y: 10};
    -var foundNode = grapher.getNodeIdAt(point);
    -
    - -

    Returns -1 if no node is found.

    Grapher.prototype.getNodeIdAt = function (point) { - var node = -1, - x = point.x, y = point.y; - - this.nodes.every(function (n, i) { // we'll want to look for ways to optimize this - var inX = x <= n.position.x + n.width && x >= n.position.x, - inY = y <= n.position.y + n.height && y >= n.position.y, - found = inX && inY; - if (found) node = i; - return !found; - }); - - return node; - };

    Private Functions

    grapher._exit

    - -

    Remove a sprite from it's parent.

    Grapher.prototype._exit = function (sprite) { return sprite.parent.removeChild(sprite); };

    grapher._enter

    - -

    Create a new sprite from the node or link data provided.

    Grapher.prototype._enter = function (data) { - var type = u.isUndefined(data.from) ? NODES : LINKS, - spriteSet = type === NODES ? this.nodes : this.links; - sprite = new PIXI.Sprite(Grapher.getTexture(type, this.foregroundColor())); - spriteSet.push(sprite); - };

    grapher._addToUpdateQueue

    + };

    grapher.getDataPosition

    + +

    Returns data space coordinates given display coordinates. +If a single argument passed in, function considers first argument an object with x and y props.

    Grapher.prototype.getDataPosition = function (x, y) { + var xCoord = u.isUndefined(y) ? x.x : x; + var yCoord = u.isUndefined(y) ? x.y : y; + x = this.renderer.untransformX(xCoord); + y = this.renderer.untransformY(yCoord); + return {x: x, y: y}; + };

    grapher.getDisplayPosition

    + +

    Returns display space coordinates given data coordinates. +If a single argument passed in, function considers first argument an object with x and y props.

    Grapher.prototype.getDisplayPosition = function (x, y) { + var xCoord = u.isUndefined(y) ? x.x : x; + var yCoord = u.isUndefined(y) ? x.y : y; + x = this.renderer.transformX(xCoord); + y = this.renderer.transformY(yCoord); + return {x: x, y: y}; + };

    Private Functions

    grapher._addToUpdateQueue

    Add indices to the nodes or links update queue.

    Grapher.prototype._addToUpdateQueue = function (type, indices) { var willUpdate = type === NODES ? this.willUpdate.nodes : this.willUpdate.links, @@ -341,8 +293,8 @@ else if (updatingNodes && updatingNodes.length) u.eachPop(updatingNodes, this._updateNodeByIndex); if (this.updateTransform) { - this.network.scale.set(this.props.scale); - this.network.position.set.apply(this.network, this.props.translate); + this.renderer.setScale(this.props.scale); + this.renderer.setTranslate(this.props.translate); } this._clearUpdateQueue(); @@ -350,31 +302,19 @@ Grapher.prototype._updateLink = function (link, i) { var data = this.data(), - lw = this.lineWidth(), l = data.links[i], from = data.nodes[l.from], - to = data.nodes[l.to], - leftMost = from.x <= to.x ? from : to; - - link.width = Math.sqrt(Math.pow(to.x - from.x, 2) + Math.pow(to.y - from.y, 2)); - link.height = lw; - link.position.set(leftMost.x, leftMost.y - lw / 2); - link.pivot.set(0, lw / 2); - link.rotation = Math.atan((to.y - from.y) / (to.x - from.x)); + to = data.nodes[l.to]; var color = !u.isUndefined(l.color) ? this._findColor(l.color) : Color.interpolate(this._findColor(from.color), this._findColor(to.color)); - this._setColor(LINKS, link, color); + link.update(from.x, from.y, to.x, to.y, color); }; Grapher.prototype._updateNode = function (node, i) { var n = this.data().nodes[i]; - node.width = n.r * 2; - node.height = n.r * 2; - node.position.set(n.x - n.r, n.y - n.r); - - this._setColor(NODES, node, this._findColor(n.color)); + node.update(n.x, n.y, n.r, this._findColor(n.color)); }; Grapher.prototype._updateNodeByIndex = function (i) { this._updateNode(this.nodes[i], i); }; @@ -409,41 +349,15 @@

    Search for a color whether it's defined by palette index, string, integer.

    Grapher.prototype._findColor = function (c) { - var color = NaN, - palette = this.palette(); - - if (palette && palette[c]) color = palette[c]; - else color = Color.parse(c);

    if color is still not set, use the default

    if (u.isNaN(color)) color = this.foregroundColor(); + var color = Color.parse(c);

    if color is still not set, use the default

    if (u.isNaN(color)) color = this.color(); return color; - };

    grapher._setColor

    - -

    Set color on a sprite. This function moves the sprite into the appropriate -sprite batch.

    Grapher.prototype._setColor = function (type, sprite, color) { - var texture = Grapher.getTexture(type, color); - - sprite.setTexture(texture); - if (sprite.parent) this._exit(sprite); - this._getBatch(type, color).addChild(sprite); - };

    grapher._getBatch

    - -

    Get the sprite batch for the sprite type and color.

    Grapher.prototype._getBatch = function (type, color) { - var batchSet = type === NODES ? this.batches.nodes : this.batches.links; - if (!batchSet[color]) { - var batch = new PIXI.SpriteBatch(); - if (type === LINKS) this.network.addChildAt(batch, 0); - else this.network.addChild(batch); - batchSet[color] = batch; - } - return batchSet[color]; - };

    grapher._onEvent

    - -

    Wraps listeners.

    Grapher.prototype._onEvent = function (event) { - return function (e) { - var callback = this.listeners[event] ? this.listeners[event] : u.noop; - e.offset = e.getLocalPosition(this.stage); - e.offsetData = e.getLocalPosition(this.network); - callback(e); - }.bind(this); + };

    grapher._getWebGL

    + +

    *get webGL context if available

    Grapher.prototype._getWebGL = function () { + var gl = null; + try { gl = this.canvas.getContext("webgl") || this.canvas.getContext("experimental-webgl"); } + catch (x) { gl = null; } + return gl; };

    grapher._onContextLost

    Handle context lost.

    Grapher.prototype._onContextLost = function (e) { @@ -452,76 +366,12 @@ };

    grapher._onContextRestored

    Handle context restored.

    Grapher.prototype._onContextRestored = function (e) { - if (this.renderer.contextLost) this.renderer.initContext(); - var gl = this.renderer.gl; - - var restoreBatch = function (batch) { - if (batch.fastSpriteBatch) batch.fastSpriteBatch.setContext(gl); - }; - - if (gl) {

    Restore each spriteBatch manually.

    u.eachKey(this.batches.nodes, restoreBatch); - u.eachKey(this.batches.links, restoreBatch);

    Re-enable vertex attrib array indices. -PIXI remembers to re-enable 4 and 5, but not 0-3.

    gl.enableVertexAttribArray(0); - gl.enableVertexAttribArray(1); - gl.enableVertexAttribArray(2); - gl.enableVertexAttribArray(3);

    Reset the blend mode (to PIXI.blendModesWebGL[PIXI.blendModes.NORMAL]).

    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - } - - this.resize(this.width, this.height); - + var webGL = this._getWebGL(); + this.renderer.initGL(webGL); if (this.currentFrame) this.play(); // Play the graph if it was running. else if (this.rendered) this.render(); };

    Grapher Static Properties

    var NODES = Grapher.NODES = 'nodes'; var LINKS = Grapher.LINKS = 'links'; - Grapher.palettes = {}; // Store palettes and textures staticly. - Grapher.textures = {}; - Grapher.textures.nodes = {}; - Grapher.textures.links = {};

    Grapher Static Methods

    Grapher.getPalette

    - -

    Get a palette that has been defined.

    Grapher.getPalette = function (name) { return this.palettes[name]; };

    Grapher.setPalette

    - -

    Define a palette with a name and an array of color swatches.

    Grapher.setPalette = function (name, swatches) { - var palette = this.palettes[name] = {}; - swatches = u.map(swatches, Color.parse); - - u.each(swatches, function (swatch, i) { - this.getTexture(LINKS, swatch); - this.getTexture(NODES, swatch); - palette[i] = swatch; - - var j; - for (j = 0; j < i; j++) { // Interpolate 'in-between' link colors 50% between node colors. - var color = Color.interpolate(swatches[j], swatch, 0.5); - this.getTexture(LINKS, color); - palette[j + '-' + i] = color; - } - }, this); - return this; - };

    Grapher.getTexture

    - -

    Get a texture by 'nodes' or 'links' and a color.

    Grapher.getTexture = function (type, color) { - var textureSet = type === NODES ? this.textures.nodes : this.textures.links; - if (!textureSet[color]) {

    Generate the textures from Canvas

    var isNode = type === NODES, - size = isNode ? 100 : 1, - renderer = new PIXI.CanvasRenderer(size, size, {transparent: isNode, resolution: 1}), - stage = new PIXI.Stage(color), - graphics = null, - img = ''; - - if (isNode) { - graphics = new PIXI.Graphics(); - graphics.beginFill(color); - graphics.drawCircle(size / 2, size / 2, size / 2); - graphics.endFill(); - - stage.addChild(graphics); - } - - renderer.render(stage); - img = renderer.view.toDataURL(); - textureSet[color] = PIXI.Texture.fromImage(img); - } - return textureSet[color]; - }; + if (module && module.exports) module.exports = Grapher; })();
    \ No newline at end of file diff --git a/doc/index.html b/doc/index.html index 8ba4592..daab13e 100644 --- a/doc/index.html +++ b/doc/index.html @@ -1,19 +1,37 @@ -index
    README.md

    Ayasdi.Grapher

    +index
    README.md

    Grapher

    -

    WebGL/Canvas Network Graphing using PIXI.js.

    +

    Draw network graphs using WebGL and Canvas backup.

    Download

    -

    Grapher comes bundled with PIXI.js.

    - -

    An unbundled version can be found in the modules folder. -This version is useful if you are already using some of the dependencies or -are using a package management system.

    +

    Installing

    + +

    You can import grapher in a script tag, or build grapher into your application +using Duo:

    + +
    var Grapher = require('ayasdi/grapher');
    +
    + +

    Additional Modules

    + +

    Need a specific feature? Create your own modules for Grapher! +Here are some modules we've made:

    + +
      +
    • center: +Helpful functions for centering the graph.
    • +
    • palette: +Set up custom palettes and set colors by palette indices.
    • +
    • target: +Target a node or link, or find the nearest nodes and links.
    • +
    • zoom: +Zoom adjusts Grapher's scale by ratio. Optionally target a point as the zoom center.
    • +

    Examples

    diff --git a/doc/modules/color.html b/doc/modules/color.html deleted file mode 100644 index f4580db..0000000 --- a/doc/modules/color.html +++ /dev/null @@ -1,42 +0,0 @@ -modules/color
    modules/color.js

    Ayasdi Inc. Copyright 2014 -Color.js may be freely distributed under the Apache 2.0 license

    var Color = module.exports = { - hexToRgb: hexToRgb, - rgbToHex: rgbToHex, - interpolate: interpolate, - parse: parse -}; - -function hexToRgb (hex) { - return {r: (hex >> 16) & 0xff, g: (hex >> 8) & 0xff, b: hex & 0xff}; -}; - -function rgbToHex (r, g, b) { - return r << 16 | g << 8 | b; -}; - -function interpolate (a, b, amt) { - amt = amt === undefined ? 0.5 : amt; - var colorA = hexToRgb(a), - colorB = hexToRgb(b), - interpolated = { - r: colorA.r + (colorB.r - colorA.r) * amt, - g: colorA.g + (colorB.g - colorA.g) * amt, - b: colorA.b + (colorB.b - colorA.b) * amt - }; - return rgbToHex(interpolated.r, interpolated.g, interpolated.b); -}; - -function parse (c) { - var color = parseInt(c); - if (typeof c === 'string') { - if (c.split('#').length > 1) { // hex format '#ffffff' - color = parseInt(c.replace('#', ''), 16); - } - - else if (c.split('rgb(').length > 1) { // rgb format 'rgb(255, 255, 255)' - var rgb = c.substring(4, c.length-1).replace(/ /g, '').split(','); - color = rgbToHex(rgb[0], rgb[1], rgb[2]); - } - } - return color; -};
    \ No newline at end of file diff --git a/doc/modules/grapher.html b/doc/modules/grapher.html deleted file mode 100644 index 971c4c7..0000000 --- a/doc/modules/grapher.html +++ /dev/null @@ -1,419 +0,0 @@ -modules/grapher
    modules/grapher.js

    Ayasdi Inc. Copyright 2014 -Grapher.js may be freely distributed under the Apache 2.0 license

    Grapher: WebGL network graph rendering with PIXI

    function Grapher () { - this.initialize.apply(this, arguments); - return this; -}

    Helpers

    var PIXI = require('./vendor/pixi.js'), - Color = require('./color.js'), - u = require('./utilities.js'); - -PIXI.dontSayHello = true;

    Static

    var NODES = Grapher.NODES = 'nodes'; -var LINKS = Grapher.LINKS = 'links'; -Grapher.palettes = {}; // store palettes and textures staticly -Grapher.textures = {}; -Grapher.textures[NODES] = {}; -Grapher.textures[LINKS] = {}; - -Grapher.getPalette = function (name) { return this.palettes[name]; }; - -Grapher.setPalette = function (name, swatches) { - var palette = this.palettes[name] = {}; - swatches = u.map(swatches, Color.parse); - - u.each(swatches, function (swatch, i) { - this.getTexture(LINKS, swatch); - this.getTexture(NODES, swatch); - palette[i] = swatch; - - var j; - for (j = 0; j < i; j++) { // interpolate 'in-between' link colors 50% between node colors - var color = Color.interpolate(swatches[j], swatch, 0.5); - this.getTexture(LINKS, color); - palette[j + '-' + i] = color; - } - }.bind(this)); - return this; -}; - -Grapher.getTexture = function (type, color) { - if (!this.textures[type][color]) {

    generate the textures from Canvas

    var isNode = type === NODES, - size = isNode ? 100 : 1, - renderer = new PIXI.CanvasRenderer(size, size, {transparent: isNode, resolution: 1}), - stage = new PIXI.Stage(color); - - if (isNode) { - graphics = new PIXI.Graphics(); - graphics.beginFill(color); - graphics.drawCircle(size / 2, size / 2, size / 2); - graphics.endFill(); - - stage.addChild(graphics); - } - - renderer.render(stage); - - this.textures[type][color] = new PIXI.Texture.fromCanvas(renderer.view); - } - return this.textures[type][color]; -};

    Grapher instances

    Grapher.prototype = { - _lineWidth: 2, - _foregroundColor: 0x222222, - _backgroundColor: 0xffffff, - - initialize: function (width, height, o) {

    Extend default options

    var options = u.extend({ - antialias: true, - resolution: typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1 - }, o);

    Renderer and view

    this.renderer = PIXI.autoDetectRenderer(width, height, options); - this.view = this.renderer.view;

    Stage and containers

    this.stage = new PIXI.Stage(this.backgroundColor()); - this.network = new PIXI.DisplayObjectContainer(); - this.stage.addChild(this.network);

    SpriteBatch containers

    this.batches = {}; - this.batches[NODES] = {}; - this.batches[LINKS] = {};

    Sprite array

    this[LINKS] = []; - this[NODES] = [];

    indices that will update

    this.willUpdate = {}; - this.updateAll = {}; - this._clearUpdateQueue();

    Listeners

    this.listeners = {};

    Set initial transform

    this.center(); - this._hasModifiedTransform = false;

    Bind some updaters

    this._updateLink = this._updateLink.bind(this); - this._updateNode = this._updateNode.bind(this); - this._updateLinkByIndex = this._updateLinkByIndex.bind(this); - this._updateNodeByIndex = this._updateNodeByIndex.bind(this); - this.animate = this.animate.bind(this);

    Interactions

    this.stage.interactive = true; - this.stage.mousedown = this._onEvent('mousedown'); - this.stage.mousemove = this._onEvent('mousemove'); - this.stage.mouseup = this._onEvent('mouseup'); - },

    ex. grapher.on('mousedown', function () {...});

    on: function (event, fn) { - if (u.isFunction(fn)) this.listeners[event] = fn; - return this; - }, - - off: function (event) { - if (event in this.listeners) this.listeners[event] = u.noop; - return this; - }, - - palette: function (name) { - if (u.isUndefined(name)) return this._palette; - - this._palette = Grapher.getPalette(name); - this.update(); - return this; - },

    Accepts network data in the form: -{ - nodes: [{x: 0, y: 0, r: 20, color: (swatch or hex/rgb)}, ... ]. - links: [{from: 0, to: 1, color: (swatch or hex/rgb)}, ... ]

    // - data: function (data) { - if (u.isUndefined(data)) return this._data; - - this._data = data; - this.exit(); - this.enter(); - this.update(); - - if (!this._hasModifiedTransform) this.center(); - return this; - }, - - enter: function () { - var data = this.data(), - entering = []; - - if (this[LINKS].length < data[LINKS].length) - entering = entering.concat(data[LINKS].slice(this[LINKS].length, data[LINKS].length - this[LINKS].length)); - if (this[NODES].length < data[NODES].length) - entering = entering.concat(data[NODES].slice(this[NODES].length, data[NODES].length - this[NODES].length)); - - u.each(entering, this._enter.bind(this)); - return this; - }, - - exit: function () { - var data = this.data(), - exiting = []; - - if (data[LINKS].length < this[LINKS].length) - exiting = exiting.concat(this[LINKS].splice(data[LINKS].length, this[LINKS].length - data[LINKS].length)); - if (data[NODES].length < this[NODES].length) - exiting = exiting.concat(this[NODES].splice(data[NODES].length, this[NODES].length - data[NODES].length)); - - u.each(exiting, this._exit.bind(this)); - return this; - },

    ex. grapher.update(); // updates all nodes and links - grapher.update('links'); // updates only links - grapher.update('nodes', 0, 4); // updates nodes indices 0 to 3 (4 is not inclusive) - grapher.update('links', [0, 1, 2, 6, 32]); // updates links indexed by the indices

    update: function (type, start, end) { - var indices; - if (u.isArray(start)) indices = start; - else if (u.isNumber(start) && u.isNumber(end)) indices = u.range(start, end); - - if (u.isArray(indices)) { - this._addToUpdateQueue(type, indices); - if (type === NODES) this._addToUpdateQueue(LINKS, this._findLinks(indices)); - } else { - this.updateAll[LINKS] = this.updateAll[LINKS] || type !== NODES; - this.updateAll[NODES] = this.updateAll[NODES] || type !== LINKS; - } - return this; - },

    Update an individual node by index.

    updateNode: function (index, willUpdateLinks) { - this._addToUpdateQueue(NODES, [index]); - if (willUpdateLinks) this._addToUpdateQueue(LINKS, this._findLinks([index])); - return this; - },

    Update an individual link by index.

    updateLink: function (index) { - this._addToUpdateQueue(LINKS, [index]); - return this; - }, - - render: function () { - this._update(); - this.renderer.render(this.stage); - return this; - }, - - animate: function (time) { - this.render(); - this._frame = requestAnimationFrame(this.animate); - }, - - play: function () { - requestAnimationFrame(this.animate); - return this; - }, - - pause: function () { - if (this._frame) cancelAnimationFrame(this._frame); - return this; - }, - - resize: function (width, height) { - this.renderer.resize(width, height); - return this; - }, - - center: function () { - var x = y = 0, - scale = 1, - nodes = this.data() ? this.data()[NODES] : null, - numNodes = nodes ? nodes.length : 0; - - if (numNodes) { // get initial transform - var minX = Infinity, maxX = -Infinity, - minY = Infinity, maxY = -Infinity, - width = this.renderer.width / this.renderer.resolution, - height = this.renderer.height / this.renderer.resolution, - pad = 1.1, - i; - - for (i = 0; i < numNodes; i++) { - if (nodes[i].x < minX) minX = nodes[i].x; - if (nodes[i].x > maxX) maxX = nodes[i].x; - if (nodes[i].y < minY) minY = nodes[i].y; - if (nodes[i].y > maxY) maxY = nodes[i].y; - } - - var dX = maxX - minX, - dY = maxY - minY; - - scale = Math.min(width / dX, height / dY, 2) / pad; - x = (width - dX * scale) / 2 - minX * scale; - y = (height - dY * scale) / 2 - minY * scale; - } - - return this.scale(scale).translate([x, y]); - }, - - transform: function (transform) { - if (u.isUndefined(transform)) return {scale: this._scale, translate: this._translate}; - - this.scale(transform.scale); - this.translate(transform.translate); - return this; - }, - - scale: function (scale) { - if (u.isUndefined(scale)) return this._scale; - if (u.isNumber(scale)) this._scale = scale; - this.updateTransform = true; - this._hasModifiedTransform = true; - return this; - }, - - translate: function (translate) { - if (u.isUndefined(translate)) return this._translate; - if (u.isArray(translate)) this._translate = translate; - this.updateTransform = true; - this._hasModifiedTransform = true; - return this; - }, - - backgroundColor: function (color) { - if (u.isUndefined(color)) return this._backgroundColor; - - this._backgroundColor = Color.parse(color); - this.stage.setBackgroundColor(this._backgroundColor); - return this; - }, - - foregroundColor: function (color) { - if (u.isUndefined(color)) return this._foregroundColor; - - this._foregroundColor = Color.parse(color); - return this; - }, - - lineWidth: function (size) { - if (u.isUndefined(size)) return this._lineWidth; - - this._lineWidth = size; - return this; - }, - - getNodeIdAt: function (point) { - var node = -1, - x = point.x, y = point.y; - - this[NODES].every(function (n, i) { // we'll want to look for ways to optimize this - var inX = x <= n.position.x + n.width && x >= n.position.x, - inY = y <= n.position.y + n.height && y >= n.position.y, - found = inX && inY; - if (found) node = i; - return !found; - }); - - return node; - }, - - _exit: function (sprite) { return sprite.parent.removeChild(sprite); }, - _enter: function (data) { - var type = u.isUndefined(data.from) ? NODES : LINKS, - sprite = new PIXI.Sprite(Grapher.getTexture(type, this.foregroundColor())); - this[type].push(sprite); - }, - - _addToUpdateQueue: function (type, indices) { - var insertIntoQueue = function (i) { - var atIndex = u.sortedIndex(this.willUpdate[type], i); - if (this.willUpdate[type][atIndex] !== i) - this.willUpdate[type].splice(atIndex, 0, i); - }.bind(this); - - if (!this.updateAll[type] && u.isArray(indices)) u.each(indices, insertIntoQueue); - this.updateAll[type] = this.updateAll[type] || this.willUpdate[type].length >= this[type].length; - }, - - _clearUpdateQueue: function () { - this.willUpdate[LINKS] = []; - this.willUpdate[NODES] = []; - this.updateAll[LINKS] = false; - this.updateAll[NODES] = false; - this.updateTransform = false; - }, - - _update: function () { - var updatingLinks = this.willUpdate[LINKS], - updatingNodes = this.willUpdate[NODES], - i; - - if (this.updateAll[LINKS]) u.each(this[LINKS], this._updateLink); - else if (updatingLinks && updatingLinks.length) u.eachPop(updatingLinks, this._updateLinkByIndex); - - if (this.updateAll[NODES]) u.each(this[NODES], this._updateNode); - else if (updatingNodes && updatingNodes.length) u.eachPop(updatingNodes, this._updateNodeByIndex); - - if (this.updateTransform) { - this.network.scale.set(this._scale); - this.network.position.set.apply(this.network, this._translate); - } - - this._clearUpdateQueue(); - }, - - _updateLink: function (link, i) { - var data = this.data(), - lw = this.lineWidth(), - l = data[LINKS][i], - from = data[NODES][l.from], - to = data[NODES][l.to], - leftMost = from.x <= to.x ? from : to; - - link.width = Math.sqrt(Math.pow(to.x - from.x, 2) + Math.pow(to.y - from.y, 2)); - link.height = lw; - link.position.set(leftMost.x, leftMost.y - lw / 2); - link.pivot.set(0, lw / 2); - link.rotation = Math.atan((to.y - from.y) / (to.x - from.x)); - - var color = !u.isUndefined(l.color) ? this._findColor(l.color) : - Color.interpolate(this._findColor(from.color), this._findColor(to.color)); - - this._setColor(LINKS, link, color); - }, - - _updateNode: function (node, i) { - var n = this.data()[NODES][i]; - node.width = n.r * 2; - node.height = n.r * 2; - node.position.set(n.x - n.r, n.y - n.r); - - this._setColor(NODES, node, this._findColor(n.color)); - }, - - _updateNodeByIndex: function (i) { this._updateNode(this[NODES][i], i); }, - - _updateLinkByIndex: function (i) { this._updateLink(this[LINKS][i], i); }, - - _findLinks: function (indices) { - var isLinked = function (indices, l) { - var i, len = indices.length, flag = false; - for (i = 0; i < len; i++) { - if (l.to == indices[i] || l.from == indices[i]) { // loose equivalience is sufficient - flag = true; - break; - } - } - return flag; - }, - links = this.data()[LINKS], - i, numLinks = links.length, - updatingLinks = []; - - for (i = 0; i < numLinks; i++) { - if (isLinked(indices, links[i])) updatingLinks.push(i); - } - - return updatingLinks; - }, - - _findColor: function (c) { - var color = NaN, - palette = this.palette(); - - if (palette && palette[c]) color = palette[c]; - else color = Color.parse(c);

    if color is still not set, use the default

    if (u.isNaN(color)) color = this.foregroundColor(); - return color; - }, - - _setColor: function (type, sprite, color) { - var texture = Grapher.getTexture(type, color); - - sprite.setTexture(texture); - if (sprite.parent) this._exit(sprite); - this._getBatch(type, color).addChild(sprite); - }, - - _getBatch: function (type, color) { - if (!this.batches[type][color]) { - var batch = new PIXI.SpriteBatch(); - if (type === LINKS) this.network.addChildAt(batch, 0); - else this.network.addChild(batch); - this.batches[type][color] = batch; - } - return this.batches[type][color]; - }, - - _onEvent: function (event) { - return function (e) { - var callback = this.listeners[event] ? this.listeners[event] : u.noop; - e.offset = e.getLocalPosition(this.stage); - e.offsetData = e.getLocalPosition(this.network); - callback(e); - }.bind(this); - } -}; - -if (module && module.exports) module.exports = Grapher; // export with module
    \ No newline at end of file diff --git a/doc/utilities.html b/doc/utilities.html deleted file mode 100644 index 7dc6070..0000000 --- a/doc/utilities.html +++ /dev/null @@ -1,171 +0,0 @@ -utilities
    modules/utilities.js

    Utilities

    - -

    Various utility functions

    var Utilities = module.exports = { - each: each, - eachPop: eachPop, - eachKey: eachKey, - map: map, - clean: clean, - range: range, - sortedIndex: sortedIndex, - indexOf: indexOf, - uniqueInsert: uniqueInsert, - extend: extend, - bind: bind, - noop: noop, - isUndefined: isUndefined, - isFunction: isFunction, - isObject: isObject, - isArray: Array.isArray, - isNumber: isNumber, - isNaN: isNaN -};

    noop

    - -

    A function that does nothing.

    function noop () {};

    each

    - -

    Perform an operation on each element in an array.

    - -
    var arr = [1, 2, 3];
    -u.each(arr, fn);
    -
    function each (arr, fn, ctx) { - fn = bind(fn, ctx); - var i = arr.length; - while (--i > -1) { - fn(arr[i], i); - } - return arr; -};

    eachPop

    - -

    Perform a function on each element in an array. Faster than each, but won't pass index and the -array will be cleared.

    - -
    u.eachPop([1, 2, 3], fn);
    -
    function eachPop (arr, fn, ctx) { - fn = bind(fn, ctx); - while (arr.length) { - fn(arr.pop()); - } - return arr; -};

    eachKey

    - -

    Perform a function on each property in an object.

    - -
    var obj = {foo: 0, bar: 0};
    -u.eachKey(obj, fn);
    -
    function eachKey (obj, fn, ctx) { - fn = bind(fn, ctx); - if (isObject(obj)) { - var keys = Object.keys(obj); - - while (keys.length) { - var key = keys.pop(); - fn(obj[key], key); - } - } - return obj; -};

    map

    - -

    Get a new array with values calculated from original array.

    - -
    var arr = [1, 2, 3];
    -var newArr = u.map(arr, fn);
    -
    function map (arr, fn, ctx) { - fn = bind(fn, ctx); - var i = arr.length, - mapped = new Array(i); - while (--i > -1) { - mapped[i] = fn(arr[i], i); - } - return mapped; -};

    clean

    - -

    Clean an array by reference.

    - -
    var arr = [1, 2, 3];
    -u.clean(arr); // arr = []
    -
    function clean (arr) { - eachPop(arr, noop); - return arr; -};

    range

    - -

    Create an array of numbers from start to end, incremented by step.

    function range (start, end, step) { - step = isNumber(step) ? step : 1; - if (isUndefined(end)) { - end = start; - start = 0; - } - - var i = Math.max(Math.ceil((end - start) / step), 0), - result = new Array(i); - - while (--i > -1) { - result[i] = start + (step * i); - } - return result; -};

    sortedIndex

    - -

    Finds the sorted position of a number in an Array of numbers.

    function sortedIndex (arr, n) { - var min = 0, - max = arr.length; - - while (min < max) { - var mid = min + max >>> 1; - if (n < mid) max = mid; - else min = mid + 1; - } - - return min; -};

    indexOf

    - -

    Finds the index of a variable in an array. -Returns -1 if not found.

    function indexOf (arr, n) { - var i = arr.length; - while (--i > -1) { - if (arr[i] === n) return i; - } - return i; -};

    uniqueInsert

    - -

    Inserts a value into an array only if it does not already exist -in the array.

    function uniqueInsert (arr, n) { - if (indexOf(arr, n) === -1) arr.push(n); - return arr; -};

    extend

    - -

    Extend an object with the properties of one other objects

    function extend (obj, source) { - if (isObject(obj) && isObject(source)) { - var props = Object.getOwnPropertyNames(source), - i = props.length; - while (--i > -1) { - var prop = props[i]; - obj[prop] = source[prop]; - } - } - return obj; -};

    bind

    - -

    Bind a function to a context. Optionally pass in the number of arguments -which will use the faster fn.call if the number of arguments is 0, 1, or 2.

    function bind (fn, ctx) { - if (!ctx) return fn; - return function () { return fn.apply(ctx, arguments); }; -};

    isUndefined

    - -

    Checks if a variable is undefined.

    function isUndefined (o) { - return typeof o === 'undefined'; -};

    isFunction

    - -

    Checks if a variable is a function.

    function isFunction (o) { - return typeof o === 'function'; -};

    isObject

    - -

    Checks if a variable is an object.

    function isObject (o) { - return typeof o === 'object' && !!o; -};

    isNumber

    - -

    Checks if a variable is a number.

    function isNumber (o) { - return typeof o === 'number'; -};

    isNaN

    - -

    Checks if a variable is NaN.

    function isNaN (o) { - return isNumber(o) && o !== +o; -};
    \ No newline at end of file diff --git a/examples/1-simple.html b/examples/1-simple.html index 47e4a08..70acc58 100644 --- a/examples/1-simple.html +++ b/examples/1-simple.html @@ -2,6 +2,7 @@ Simple + + diff --git a/examples/2-transforms.html b/examples/2-transforms.html index 0584a1d..900aae9 100644 --- a/examples/2-transforms.html +++ b/examples/2-transforms.html @@ -2,6 +2,7 @@ Transforms + + diff --git a/examples/3-colors.html b/examples/3-colors.html index a60dda9..78ebc79 100644 --- a/examples/3-colors.html +++ b/examples/3-colors.html @@ -2,6 +2,7 @@ Colors + + diff --git a/examples/4-drag.html b/examples/4-drag.html index 244f3ee..18fe8e0 100644 --- a/examples/4-drag.html +++ b/examples/4-drag.html @@ -2,6 +2,7 @@ Drag +