From ccf02ac9bd67d4d49ad8264ae7fc6098e361ff4b Mon Sep 17 00:00:00 2001 From: "apatrick@google.com" <apatrick@google.com@0039d316-1c4b-4281-b951-d872f2087c98> Date: Thu, 12 Nov 2009 19:56:24 +0000 Subject: Made all line endings consistently LF and added svn:eol-style=LF property to files with these names / extensions. c cc h mm txt idl py js html css gyp gypi xml shader json htm README DEPS git-svn-id: svn://svn.chromium.org/chrome/trunk/src@31811 0039d316-1c4b-4281-b951-d872f2087c98 --- o3d/samples/GoogleIO-2009/assets/style.css | 16 +- o3d/samples/bitmap-draw-image.html | 492 ++-- o3d/samples/box2d-3d/third_party/box2d/box2d.js | 2074 ++++++++-------- o3d/samples/manipulators/translate1.html | 546 ++--- o3d/samples/o3djs/manipulators.js | 1924 +++++++-------- o3d/samples/o3djs/performance.js | 404 +-- o3d/samples/o3djs/texture.js | 478 ++-- o3d/samples/siteswap/animation.js | 396 +-- o3d/samples/siteswap/math.js | 2984 +++++++++++------------ o3d/samples/siteswap/siteswap.html | 646 ++--- o3d/samples/siteswap/siteswap.js | 830 +++---- 11 files changed, 5395 insertions(+), 5395 deletions(-) (limited to 'o3d/samples') diff --git a/o3d/samples/GoogleIO-2009/assets/style.css b/o3d/samples/GoogleIO-2009/assets/style.css index 71fc441..2c4a145 100644 --- a/o3d/samples/GoogleIO-2009/assets/style.css +++ b/o3d/samples/GoogleIO-2009/assets/style.css @@ -1,8 +1,8 @@ -html, body { - height: 100%; - margin: 0; - padding: 0; - border: none; - font-family: Arial, sans-serif; -} - +html, body { + height: 100%; + margin: 0; + padding: 0; + border: none; + font-family: Arial, sans-serif; +} + diff --git a/o3d/samples/bitmap-draw-image.html b/o3d/samples/bitmap-draw-image.html index 0a05b73..e4daea6 100644 --- a/o3d/samples/bitmap-draw-image.html +++ b/o3d/samples/bitmap-draw-image.html @@ -1,247 +1,247 @@ -<!-- -Copyright 2009, Google Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"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 THE COPYRIGHT -OWNER OR CONTRIBUTORS 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. ---> - -<!-- -In this tutorial, we show how to create bitmaps and how to draw -images on both bitmaps and textures. ---> -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" - "http://www.w3.org/TR/html4/loose.dtd"> -<html> -<head> -<meta http-equiv="content-type" content="text/html; charset=UTF-8"> -<title> -Bitmap Draw Image Demo -</title> -<script type="text/javascript" src="o3djs/base.js"></script> -<script type="text/javascript" id="o3dscript"> -o3djs.require('o3djs.util'); -o3djs.require('o3djs.math'); -o3djs.require('o3djs.loader'); -o3djs.require('o3djs.rendergraph'); -o3djs.require('o3djs.primitives'); -o3djs.require('o3djs.material'); - -// Events -// Run the init() once the page has finished loading. -window.onload = init; - -// global variables -var g_o3d; -var g_math; -var g_client; -var g_pack; -var g_viewInfo; -var g_finished = false; // for selenium testing -var g_eye; -var g_target; -var g_up; -var g_bitmaps = []; // bitmaps by URL. - -function makeShape(texture) { - // Create a material. - var myMaterial = o3djs.material.createMaterialFromFile( - g_pack, - 'shaders/texture-only.shader', - g_viewInfo.performanceDrawList); - - // Creates a quad. - var myShape = o3djs.primitives.createPlane(g_pack, - myMaterial, - 3, // width - 3, // height - 1, // quads across - 1); // quads down - - // Get the material's sampler parameter, get the sampler on it and set its - // texture. - var sampler_param = myMaterial.getParam('texSampler0'); - var sampler = sampler_param.value; - - // Set the texture to use. - sampler.texture = texture; - - // adjust the scale of our transform to match the aspect ratio of - // the texture. Of course we could also have waited until now to build - // our plane and set its width and height to match instead of scaling - // here. - var textureWidth = texture.width; - var textureHeight = texture.height; - var hScale = 1; - var vScale = 1; - if (textureWidth > textureHeight) { - vScale = textureHeight / textureWidth; - } else if (textureHeight > textureWidth) { - hScale = textureWidth / textureHeight; - } - // We now attach our quad to the root of the transform graph. - // We do this after the texture has loaded, otherwise we'd be attempting - // to display something invalid. - - // Make a transform for each quad. - var transform = g_pack.createObject('Transform'); - transform.scale(hScale, 1, vScale); - transform.addShape(myShape); - transform.parent = g_client.root; - g_finished = true; - return myShape; -} - -function loadBitmap(loader, url) { - loader.loadBitmaps(g_pack, o3djs.util.getAbsoluteURI('assets/' + url), - function(bitmaps, exception) { - if (!exception) { - // We know we are only loading 2D images so there will only be 1 bitmap. - g_bitmaps[url] = bitmaps[0]; - } else { - alert(exception); - } - }); - -} - -/** - * Creates the client area. - */ -function init() { - o3djs.util.makeClients(initStep2, 'NotAntiAliased'); -} - -/** - * Initializes O3D, loads the effect, and loads a tar.gz archive containing - * a bunch of image files. We'll create bitmaps from them. - * And use drawImage function to create texture as well as mipmaps. - */ -function initStep2(clientElements) { - // Initialize global variables and libraries. - var o3dElement = clientElements[0]; - g_o3d = o3dElement.o3d; - g_math = o3djs.math; - g_client = o3dElement.client; - - // Create a pack to manage our resources/assets - g_pack = g_client.createPack(); - - // Create the render graph for a view. - g_viewInfo = o3djs.rendergraph.createBasicView( - g_pack, - g_client.root, - g_client.renderGraphRoot); - - // Set up an perspective projection. - var proj_matrix = g_math.matrix4.perspective( - g_math.degToRad(90), - g_client.width / g_client.height, - 0.1, - 100); - - // Create the view matrix which tells the camera which way to point to. - g_eye = [0, 1.5, 0]; - g_target = [0, 0, 0]; - g_up = [0, 0, -1]; - var view_matrix = g_math.matrix4.lookAt(g_eye, g_target, g_up); - - g_viewInfo.drawContext.view = view_matrix; - g_viewInfo.drawContext.projection = proj_matrix; - - var loader = o3djs.loader.createLoader(callback); - loadBitmap(loader, 'shaving_cream_300x300.jpg'); - loadBitmap(loader, 'four_pixel.png'); - loadBitmap(loader, 'hi.jpg'); - loader.finish(); - - // Callback that happens when loader.finish() is called and all of - // our textures have finished loading. - function callback() { - // Because bitmaps have their origin at the upper left, and in - // this sample our plane is oriented with the texture coordinate - // origin on the lower left, we need to flip all the bitmaps - // vertically to match coordinate systems. - var kids = g_bitmaps['shaving_cream_300x300.jpg']; - kids.flipVertically(); - var four_square = g_bitmaps['four_pixel.png']; - four_square.flipVertically(); - var label = g_bitmaps['hi.jpg']; - label.flipVertically(); - - var texture = g_pack.createTexture2D(300, 300, g_o3d.Texture.XRGB8, 0, - false); - // Draw bitmaps on the texture. - // Scale down entire image into on lower left corner. - texture.drawImage(kids, 0, 0, 0, 300, 300, 0, 0, 0, 150, 150); - - // Crop and scale up into the lower right corner. - texture.drawImage(kids, 0, 0, 156, 100, 100, 0, 150, 0, 150, 150); - - // Flip and draw part of the image in the top left corner (and - // texture coords are not inclusive -- they go from 0-299 for a - // 300 pixel texture). - texture.drawImage(kids, 0, 150, 150, 150, 150, 0, 149, 299, -150, -150); - - // Crop and draw cropped area in upper right corner, but we're - // using a width and height that go beyond the edges of the image. - texture.drawImage(kids, 0, 150, 150, 400, 400, 0, 150, 150, 400, 400); - - // Draw "O3D" label. - texture.drawImage(label, 0, 0, 0, 100, 50, 0, 100, 125, 100, 50); - - // Fill in different mip-map levels with images that show at - // different distances. (Scroll the mouse wheel to see). - - // Fill in medium level with whole image. - texture.drawImage(kids, 0, 0, 0, 300, 300, 1, 0, 0, 150, 150); - - // Fill in smaller level with four pixel image. - texture.drawImage(four_square, 0, 0, 0, 2, 2, 2, 0, 0, 75, 75); - - makeShape(texture); - } - o3djs.event.addEventListener(o3dElement, 'wheel', scrollMe); -} - -function scrollMe(e) { - g_eye = g_math.mulScalarVector((e.deltaY < 0 ? 11 : 13) / 12, g_eye); - g_viewInfo.drawContext.view = g_math.matrix4.lookAt(g_eye, g_target, g_up); -} -</script> -</head> -<body> -<h1>Bitmap Draw Image Demo</h1> -This tutorial shows how to create bitmaps and how to draw images -on both bitmaps and texture mipmaps. -<br/> -Scroll wheel to see different mipmaps. -<br/> -<!-- Start of O3D plugin --> -<div id="o3d" style="width: 600px; height: 600px"></div> -<!-- End of O3D plugin --> -</body> +<!-- +Copyright 2009, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 THE COPYRIGHT +OWNER OR CONTRIBUTORS 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. +--> + +<!-- +In this tutorial, we show how to create bitmaps and how to draw +images on both bitmaps and textures. +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> +Bitmap Draw Image Demo +</title> +<script type="text/javascript" src="o3djs/base.js"></script> +<script type="text/javascript" id="o3dscript"> +o3djs.require('o3djs.util'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.loader'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.primitives'); +o3djs.require('o3djs.material'); + +// Events +// Run the init() once the page has finished loading. +window.onload = init; + +// global variables +var g_o3d; +var g_math; +var g_client; +var g_pack; +var g_viewInfo; +var g_finished = false; // for selenium testing +var g_eye; +var g_target; +var g_up; +var g_bitmaps = []; // bitmaps by URL. + +function makeShape(texture) { + // Create a material. + var myMaterial = o3djs.material.createMaterialFromFile( + g_pack, + 'shaders/texture-only.shader', + g_viewInfo.performanceDrawList); + + // Creates a quad. + var myShape = o3djs.primitives.createPlane(g_pack, + myMaterial, + 3, // width + 3, // height + 1, // quads across + 1); // quads down + + // Get the material's sampler parameter, get the sampler on it and set its + // texture. + var sampler_param = myMaterial.getParam('texSampler0'); + var sampler = sampler_param.value; + + // Set the texture to use. + sampler.texture = texture; + + // adjust the scale of our transform to match the aspect ratio of + // the texture. Of course we could also have waited until now to build + // our plane and set its width and height to match instead of scaling + // here. + var textureWidth = texture.width; + var textureHeight = texture.height; + var hScale = 1; + var vScale = 1; + if (textureWidth > textureHeight) { + vScale = textureHeight / textureWidth; + } else if (textureHeight > textureWidth) { + hScale = textureWidth / textureHeight; + } + // We now attach our quad to the root of the transform graph. + // We do this after the texture has loaded, otherwise we'd be attempting + // to display something invalid. + + // Make a transform for each quad. + var transform = g_pack.createObject('Transform'); + transform.scale(hScale, 1, vScale); + transform.addShape(myShape); + transform.parent = g_client.root; + g_finished = true; + return myShape; +} + +function loadBitmap(loader, url) { + loader.loadBitmaps(g_pack, o3djs.util.getAbsoluteURI('assets/' + url), + function(bitmaps, exception) { + if (!exception) { + // We know we are only loading 2D images so there will only be 1 bitmap. + g_bitmaps[url] = bitmaps[0]; + } else { + alert(exception); + } + }); + +} + +/** + * Creates the client area. + */ +function init() { + o3djs.util.makeClients(initStep2, 'NotAntiAliased'); +} + +/** + * Initializes O3D, loads the effect, and loads a tar.gz archive containing + * a bunch of image files. We'll create bitmaps from them. + * And use drawImage function to create texture as well as mipmaps. + */ +function initStep2(clientElements) { + // Initialize global variables and libraries. + var o3dElement = clientElements[0]; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + g_client = o3dElement.client; + + // Create a pack to manage our resources/assets + g_pack = g_client.createPack(); + + // Create the render graph for a view. + g_viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + // Set up an perspective projection. + var proj_matrix = g_math.matrix4.perspective( + g_math.degToRad(90), + g_client.width / g_client.height, + 0.1, + 100); + + // Create the view matrix which tells the camera which way to point to. + g_eye = [0, 1.5, 0]; + g_target = [0, 0, 0]; + g_up = [0, 0, -1]; + var view_matrix = g_math.matrix4.lookAt(g_eye, g_target, g_up); + + g_viewInfo.drawContext.view = view_matrix; + g_viewInfo.drawContext.projection = proj_matrix; + + var loader = o3djs.loader.createLoader(callback); + loadBitmap(loader, 'shaving_cream_300x300.jpg'); + loadBitmap(loader, 'four_pixel.png'); + loadBitmap(loader, 'hi.jpg'); + loader.finish(); + + // Callback that happens when loader.finish() is called and all of + // our textures have finished loading. + function callback() { + // Because bitmaps have their origin at the upper left, and in + // this sample our plane is oriented with the texture coordinate + // origin on the lower left, we need to flip all the bitmaps + // vertically to match coordinate systems. + var kids = g_bitmaps['shaving_cream_300x300.jpg']; + kids.flipVertically(); + var four_square = g_bitmaps['four_pixel.png']; + four_square.flipVertically(); + var label = g_bitmaps['hi.jpg']; + label.flipVertically(); + + var texture = g_pack.createTexture2D(300, 300, g_o3d.Texture.XRGB8, 0, + false); + // Draw bitmaps on the texture. + // Scale down entire image into on lower left corner. + texture.drawImage(kids, 0, 0, 0, 300, 300, 0, 0, 0, 150, 150); + + // Crop and scale up into the lower right corner. + texture.drawImage(kids, 0, 0, 156, 100, 100, 0, 150, 0, 150, 150); + + // Flip and draw part of the image in the top left corner (and + // texture coords are not inclusive -- they go from 0-299 for a + // 300 pixel texture). + texture.drawImage(kids, 0, 150, 150, 150, 150, 0, 149, 299, -150, -150); + + // Crop and draw cropped area in upper right corner, but we're + // using a width and height that go beyond the edges of the image. + texture.drawImage(kids, 0, 150, 150, 400, 400, 0, 150, 150, 400, 400); + + // Draw "O3D" label. + texture.drawImage(label, 0, 0, 0, 100, 50, 0, 100, 125, 100, 50); + + // Fill in different mip-map levels with images that show at + // different distances. (Scroll the mouse wheel to see). + + // Fill in medium level with whole image. + texture.drawImage(kids, 0, 0, 0, 300, 300, 1, 0, 0, 150, 150); + + // Fill in smaller level with four pixel image. + texture.drawImage(four_square, 0, 0, 0, 2, 2, 2, 0, 0, 75, 75); + + makeShape(texture); + } + o3djs.event.addEventListener(o3dElement, 'wheel', scrollMe); +} + +function scrollMe(e) { + g_eye = g_math.mulScalarVector((e.deltaY < 0 ? 11 : 13) / 12, g_eye); + g_viewInfo.drawContext.view = g_math.matrix4.lookAt(g_eye, g_target, g_up); +} +</script> +</head> +<body> +<h1>Bitmap Draw Image Demo</h1> +This tutorial shows how to create bitmaps and how to draw images +on both bitmaps and texture mipmaps. +<br/> +Scroll wheel to see different mipmaps. +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 600px; height: 600px"></div> +<!-- End of O3D plugin --> +</body> </html> \ No newline at end of file diff --git a/o3d/samples/box2d-3d/third_party/box2d/box2d.js b/o3d/samples/box2d-3d/third_party/box2d/box2d.js index e55e449..8b84f83 100644 --- a/o3d/samples/box2d-3d/third_party/box2d/box2d.js +++ b/o3d/samples/box2d-3d/third_party/box2d/box2d.js @@ -1,1037 +1,1037 @@ -/* - * Box2Djs (port of Box2DFlash 1.4.3.1) - http://box2d-js.sourceforge.net/ - * Single-filed and jsmined ( http://code.google.com/p/jsmin-php/ ) by Mr.doob - */ - -var b2Settings=Class.create();b2Settings.prototype={initialize:function(){}} -b2Settings.USHRT_MAX=0x0000ffff;b2Settings.b2_pi=Math.PI;b2Settings.b2_massUnitsPerKilogram=1.0;b2Settings.b2_timeUnitsPerSecond=1.0;b2Settings.b2_lengthUnitsPerMeter=30.0;b2Settings.b2_maxManifoldPoints=2;b2Settings.b2_maxShapesPerBody=64;b2Settings.b2_maxPolyVertices=8;b2Settings.b2_maxProxies=1024;b2Settings.b2_maxPairs=8*b2Settings.b2_maxProxies;b2Settings.b2_linearSlop=0.005*b2Settings.b2_lengthUnitsPerMeter;b2Settings.b2_angularSlop=2.0/180.0*b2Settings.b2_pi;b2Settings.b2_velocityThreshold=1.0*b2Settings.b2_lengthUnitsPerMeter/b2Settings.b2_timeUnitsPerSecond;b2Settings.b2_maxLinearCorrection=0.2*b2Settings.b2_lengthUnitsPerMeter;b2Settings.b2_maxAngularCorrection=8.0/180.0*b2Settings.b2_pi;b2Settings.b2_contactBaumgarte=0.2;b2Settings.b2_timeToSleep=0.5*b2Settings.b2_timeUnitsPerSecond;b2Settings.b2_linearSleepTolerance=0.01*b2Settings.b2_lengthUnitsPerMeter/b2Settings.b2_timeUnitsPerSecond;b2Settings.b2_angularSleepTolerance=2.0/180.0/b2Settings.b2_timeUnitsPerSecond;b2Settings.b2Assert=function(a) -{if(!a){var nullVec;nullVec.x++;}}; -var b2Vec2=Class.create();b2Vec2.prototype={initialize:function(x_,y_){this.x=x_;this.y=y_;},SetZero:function(){this.x=0.0;this.y=0.0;},Set:function(x_,y_){this.x=x_;this.y=y_;},SetV:function(v){this.x=v.x;this.y=v.y;},Negative:function(){return new b2Vec2(-this.x,-this.y);},Copy:function(){return new b2Vec2(this.x,this.y);},Add:function(v) -{this.x+=v.x;this.y+=v.y;},Subtract:function(v) -{this.x-=v.x;this.y-=v.y;},Multiply:function(a) -{this.x*=a;this.y*=a;},MulM:function(A) -{var tX=this.x;this.x=A.col1.x*tX+A.col2.x*this.y;this.y=A.col1.y*tX+A.col2.y*this.y;},MulTM:function(A) -{var tX=b2Math.b2Dot(this,A.col1);this.y=b2Math.b2Dot(this,A.col2);this.x=tX;},CrossVF:function(s) -{var tX=this.x;this.x=s*this.y;this.y=-s*tX;},CrossFV:function(s) -{var tX=this.x;this.x=-s*this.y;this.y=s*tX;},MinV:function(b) -{this.x=this.x<b.x?this.x:b.x;this.y=this.y<b.y?this.y:b.y;},MaxV:function(b) -{this.x=this.x>b.x?this.x:b.x;this.y=this.y>b.y?this.y:b.y;},Abs:function() -{this.x=Math.abs(this.x);this.y=Math.abs(this.y);},Length:function() -{return Math.sqrt(this.x*this.x+this.y*this.y);},Normalize:function() -{var length=this.Length();if(length<Number.MIN_VALUE) -{return 0.0;} -var invLength=1.0/length;this.x*=invLength;this.y*=invLength;return length;},IsValid:function() -{return b2Math.b2IsValid(this.x)&&b2Math.b2IsValid(this.y);},x:null,y:null};b2Vec2.Make=function(x_,y_) -{return new b2Vec2(x_,y_);}; -var b2Mat22=Class.create();b2Mat22.prototype={initialize:function(angle,c1,c2) -{if(angle==null)angle=0;this.col1=new b2Vec2();this.col2=new b2Vec2();if(c1!=null&&c2!=null){this.col1.SetV(c1);this.col2.SetV(c2);} -else{var c=Math.cos(angle);var s=Math.sin(angle);this.col1.x=c;this.col2.x=-s;this.col1.y=s;this.col2.y=c;}},Set:function(angle) -{var c=Math.cos(angle);var s=Math.sin(angle);this.col1.x=c;this.col2.x=-s;this.col1.y=s;this.col2.y=c;},SetVV:function(c1,c2) -{this.col1.SetV(c1);this.col2.SetV(c2);},Copy:function(){return new b2Mat22(0,this.col1,this.col2);},SetM:function(m) -{this.col1.SetV(m.col1);this.col2.SetV(m.col2);},AddM:function(m) -{this.col1.x+=m.col1.x;this.col1.y+=m.col1.y;this.col2.x+=m.col2.x;this.col2.y+=m.col2.y;},SetIdentity:function() -{this.col1.x=1.0;this.col2.x=0.0;this.col1.y=0.0;this.col2.y=1.0;},SetZero:function() -{this.col1.x=0.0;this.col2.x=0.0;this.col1.y=0.0;this.col2.y=0.0;},Invert:function(out) -{var a=this.col1.x;var b=this.col2.x;var c=this.col1.y;var d=this.col2.y;var det=a*d-b*c;det=1.0/det;out.col1.x=det*d;out.col2.x=-det*b;out.col1.y=-det*c;out.col2.y=det*a;return out;},Solve:function(out,bX,bY) -{var a11=this.col1.x;var a12=this.col2.x;var a21=this.col1.y;var a22=this.col2.y;var det=a11*a22-a12*a21;det=1.0/det;out.x=det*(a22*bX-a12*bY);out.y=det*(a11*bY-a21*bX);return out;},Abs:function() -{this.col1.Abs();this.col2.Abs();},col1:new b2Vec2(),col2:new b2Vec2()}; -var b2Math=Class.create();b2Math.prototype={initialize:function(){}} -b2Math.b2IsValid=function(x) -{return isFinite(x);};b2Math.b2Dot=function(a,b) -{return a.x*b.x+a.y*b.y;};b2Math.b2CrossVV=function(a,b) -{return a.x*b.y-a.y*b.x;};b2Math.b2CrossVF=function(a,s) -{var v=new b2Vec2(s*a.y,-s*a.x);return v;};b2Math.b2CrossFV=function(s,a) -{var v=new b2Vec2(-s*a.y,s*a.x);return v;};b2Math.b2MulMV=function(A,v) -{var u=new b2Vec2(A.col1.x*v.x+A.col2.x*v.y,A.col1.y*v.x+A.col2.y*v.y);return u;};b2Math.b2MulTMV=function(A,v) -{var u=new b2Vec2(b2Math.b2Dot(v,A.col1),b2Math.b2Dot(v,A.col2));return u;};b2Math.AddVV=function(a,b) -{var v=new b2Vec2(a.x+b.x,a.y+b.y);return v;};b2Math.SubtractVV=function(a,b) -{var v=new b2Vec2(a.x-b.x,a.y-b.y);return v;};b2Math.MulFV=function(s,a) -{var v=new b2Vec2(s*a.x,s*a.y);return v;};b2Math.AddMM=function(A,B) -{var C=new b2Mat22(0,b2Math.AddVV(A.col1,B.col1),b2Math.AddVV(A.col2,B.col2));return C;};b2Math.b2MulMM=function(A,B) -{var C=new b2Mat22(0,b2Math.b2MulMV(A,B.col1),b2Math.b2MulMV(A,B.col2));return C;};b2Math.b2MulTMM=function(A,B) -{var c1=new b2Vec2(b2Math.b2Dot(A.col1,B.col1),b2Math.b2Dot(A.col2,B.col1));var c2=new b2Vec2(b2Math.b2Dot(A.col1,B.col2),b2Math.b2Dot(A.col2,B.col2));var C=new b2Mat22(0,c1,c2);return C;};b2Math.b2Abs=function(a) -{return a>0.0?a:-a;};b2Math.b2AbsV=function(a) -{var b=new b2Vec2(b2Math.b2Abs(a.x),b2Math.b2Abs(a.y));return b;};b2Math.b2AbsM=function(A) -{var B=new b2Mat22(0,b2Math.b2AbsV(A.col1),b2Math.b2AbsV(A.col2));return B;};b2Math.b2Min=function(a,b) -{return a<b?a:b;};b2Math.b2MinV=function(a,b) -{var c=new b2Vec2(b2Math.b2Min(a.x,b.x),b2Math.b2Min(a.y,b.y));return c;};b2Math.b2Max=function(a,b) -{return a>b?a:b;};b2Math.b2MaxV=function(a,b) -{var c=new b2Vec2(b2Math.b2Max(a.x,b.x),b2Math.b2Max(a.y,b.y));return c;};b2Math.b2Clamp=function(a,low,high) -{return b2Math.b2Max(low,b2Math.b2Min(a,high));};b2Math.b2ClampV=function(a,low,high) -{return b2Math.b2MaxV(low,b2Math.b2MinV(a,high));};b2Math.b2Swap=function(a,b) -{var tmp=a[0];a[0]=b[0];b[0]=tmp;};b2Math.b2Random=function() -{return Math.random()*2-1;};b2Math.b2NextPowerOfTwo=function(x) -{x|=(x>>1)&0x7FFFFFFF;x|=(x>>2)&0x3FFFFFFF;x|=(x>>4)&0x0FFFFFFF;x|=(x>>8)&0x00FFFFFF;x|=(x>>16)&0x0000FFFF;return x+1;};b2Math.b2IsPowerOfTwo=function(x) -{var result=x>0&&(x&(x-1))==0;return result;};b2Math.tempVec2=new b2Vec2();b2Math.tempVec3=new b2Vec2();b2Math.tempVec4=new b2Vec2();b2Math.tempVec5=new b2Vec2();b2Math.tempMat=new b2Mat22(); -var b2AABB=Class.create();b2AABB.prototype={IsValid:function(){var dX=this.maxVertex.x;var dY=this.maxVertex.y;dX=this.maxVertex.x;dY=this.maxVertex.y;dX-=this.minVertex.x;dY-=this.minVertex.y;var valid=dX>=0.0&&dY>=0.0;valid=valid&&this.minVertex.IsValid()&&this.maxVertex.IsValid();return valid;},minVertex:new b2Vec2(),maxVertex:new b2Vec2(),initialize:function(){this.minVertex=new b2Vec2();this.maxVertex=new b2Vec2();}}; -var b2Bound=Class.create();b2Bound.prototype={IsLower:function(){return(this.value&1)==0;},IsUpper:function(){return(this.value&1)==1;},Swap:function(b){var tempValue=this.value;var tempProxyId=this.proxyId;var tempStabbingCount=this.stabbingCount;this.value=b.value;this.proxyId=b.proxyId;this.stabbingCount=b.stabbingCount;b.value=tempValue;b.proxyId=tempProxyId;b.stabbingCount=tempStabbingCount;},value:0,proxyId:0,stabbingCount:0,initialize:function(){}} - -var b2BoundValues=Class.create();b2BoundValues.prototype={lowerValues:[0,0],upperValues:[0,0],initialize:function(){this.lowerValues=[0,0];this.upperValues=[0,0];}} - -var b2Pair=Class.create();b2Pair.prototype={SetBuffered:function(){this.status|=b2Pair.e_pairBuffered;},ClearBuffered:function(){this.status&=~b2Pair.e_pairBuffered;},IsBuffered:function(){return(this.status&b2Pair.e_pairBuffered)==b2Pair.e_pairBuffered;},SetRemoved:function(){this.status|=b2Pair.e_pairRemoved;},ClearRemoved:function(){this.status&=~b2Pair.e_pairRemoved;},IsRemoved:function(){return(this.status&b2Pair.e_pairRemoved)==b2Pair.e_pairRemoved;},SetFinal:function(){this.status|=b2Pair.e_pairFinal;},IsFinal:function(){return(this.status&b2Pair.e_pairFinal)==b2Pair.e_pairFinal;},userData:null,proxyId1:0,proxyId2:0,next:0,status:0,initialize:function(){}};b2Pair.b2_nullPair=b2Settings.USHRT_MAX;b2Pair.b2_nullProxy=b2Settings.USHRT_MAX;b2Pair.b2_tableCapacity=b2Settings.b2_maxPairs;b2Pair.b2_tableMask=b2Pair.b2_tableCapacity-1;b2Pair.e_pairBuffered=0x0001;b2Pair.e_pairRemoved=0x0002;b2Pair.e_pairFinal=0x0004; -var b2PairCallback=Class.create();b2PairCallback.prototype={PairAdded:function(proxyUserData1,proxyUserData2){return null},PairRemoved:function(proxyUserData1,proxyUserData2,pairUserData){},initialize:function(){}}; -var b2BufferedPair=Class.create();b2BufferedPair.prototype={proxyId1:0,proxyId2:0,initialize:function(){}} - -var b2PairManager=Class.create();b2PairManager.prototype={initialize:function(){var i=0;this.m_hashTable=new Array(b2Pair.b2_tableCapacity);for(i=0;i<b2Pair.b2_tableCapacity;++i) -{this.m_hashTable[i]=b2Pair.b2_nullPair;} -this.m_pairs=new Array(b2Settings.b2_maxPairs);for(i=0;i<b2Settings.b2_maxPairs;++i) -{this.m_pairs[i]=new b2Pair();} -this.m_pairBuffer=new Array(b2Settings.b2_maxPairs);for(i=0;i<b2Settings.b2_maxPairs;++i) -{this.m_pairBuffer[i]=new b2BufferedPair();} -for(i=0;i<b2Settings.b2_maxPairs;++i) -{this.m_pairs[i].proxyId1=b2Pair.b2_nullProxy;this.m_pairs[i].proxyId2=b2Pair.b2_nullProxy;this.m_pairs[i].userData=null;this.m_pairs[i].status=0;this.m_pairs[i].next=(i+1);} -this.m_pairs[b2Settings.b2_maxPairs-1].next=b2Pair.b2_nullPair;this.m_pairCount=0;},Initialize:function(broadPhase,callback){this.m_broadPhase=broadPhase;this.m_callback=callback;},AddBufferedPair:function(proxyId1,proxyId2){var pair=this.AddPair(proxyId1,proxyId2);if(pair.IsBuffered()==false) -{pair.SetBuffered();this.m_pairBuffer[this.m_pairBufferCount].proxyId1=pair.proxyId1;this.m_pairBuffer[this.m_pairBufferCount].proxyId2=pair.proxyId2;++this.m_pairBufferCount;} -pair.ClearRemoved();if(b2BroadPhase.s_validate) -{this.ValidateBuffer();}},RemoveBufferedPair:function(proxyId1,proxyId2){var pair=this.Find(proxyId1,proxyId2);if(pair==null) -{return;} -if(pair.IsBuffered()==false) -{pair.SetBuffered();this.m_pairBuffer[this.m_pairBufferCount].proxyId1=pair.proxyId1;this.m_pairBuffer[this.m_pairBufferCount].proxyId2=pair.proxyId2;++this.m_pairBufferCount;} -pair.SetRemoved();if(b2BroadPhase.s_validate) -{this.ValidateBuffer();}},Commit:function(){var i=0;var removeCount=0;var proxies=this.m_broadPhase.m_proxyPool;for(i=0;i<this.m_pairBufferCount;++i) -{var pair=this.Find(this.m_pairBuffer[i].proxyId1,this.m_pairBuffer[i].proxyId2);pair.ClearBuffered();var proxy1=proxies[pair.proxyId1];var proxy2=proxies[pair.proxyId2];if(pair.IsRemoved()) -{if(pair.IsFinal()==true) -{this.m_callback.PairRemoved(proxy1.userData,proxy2.userData,pair.userData);} -this.m_pairBuffer[removeCount].proxyId1=pair.proxyId1;this.m_pairBuffer[removeCount].proxyId2=pair.proxyId2;++removeCount;} -else -{if(pair.IsFinal()==false) -{pair.userData=this.m_callback.PairAdded(proxy1.userData,proxy2.userData);pair.SetFinal();}}} -for(i=0;i<removeCount;++i) -{this.RemovePair(this.m_pairBuffer[i].proxyId1,this.m_pairBuffer[i].proxyId2);} -this.m_pairBufferCount=0;if(b2BroadPhase.s_validate) -{this.ValidateTable();}},AddPair:function(proxyId1,proxyId2){if(proxyId1>proxyId2){var temp=proxyId1;proxyId1=proxyId2;proxyId2=temp;} -var hash=b2PairManager.Hash(proxyId1,proxyId2)&b2Pair.b2_tableMask;var pair=pair=this.FindHash(proxyId1,proxyId2,hash);if(pair!=null) -{return pair;} -var pIndex=this.m_freePair;pair=this.m_pairs[pIndex];this.m_freePair=pair.next;pair.proxyId1=proxyId1;pair.proxyId2=proxyId2;pair.status=0;pair.userData=null;pair.next=this.m_hashTable[hash];this.m_hashTable[hash]=pIndex;++this.m_pairCount;return pair;},RemovePair:function(proxyId1,proxyId2){if(proxyId1>proxyId2){var temp=proxyId1;proxyId1=proxyId2;proxyId2=temp;} -var hash=b2PairManager.Hash(proxyId1,proxyId2)&b2Pair.b2_tableMask;var node=this.m_hashTable[hash];var pNode=null;while(node!=b2Pair.b2_nullPair) -{if(b2PairManager.Equals(this.m_pairs[node],proxyId1,proxyId2)) -{var index=node;if(pNode){pNode.next=this.m_pairs[node].next;} -else{this.m_hashTable[hash]=this.m_pairs[node].next;} -var pair=this.m_pairs[index];var userData=pair.userData;pair.next=this.m_freePair;pair.proxyId1=b2Pair.b2_nullProxy;pair.proxyId2=b2Pair.b2_nullProxy;pair.userData=null;pair.status=0;this.m_freePair=index;--this.m_pairCount;return userData;} -else -{pNode=this.m_pairs[node];node=pNode.next;}} -return null;},Find:function(proxyId1,proxyId2){if(proxyId1>proxyId2){var temp=proxyId1;proxyId1=proxyId2;proxyId2=temp;} -var hash=b2PairManager.Hash(proxyId1,proxyId2)&b2Pair.b2_tableMask;return this.FindHash(proxyId1,proxyId2,hash);},FindHash:function(proxyId1,proxyId2,hash){var index=this.m_hashTable[hash];while(index!=b2Pair.b2_nullPair&&b2PairManager.Equals(this.m_pairs[index],proxyId1,proxyId2)==false) -{index=this.m_pairs[index].next;} -if(index==b2Pair.b2_nullPair) -{return null;} -return this.m_pairs[index];},ValidateBuffer:function(){},ValidateTable:function(){},m_broadPhase:null,m_callback:null,m_pairs:null,m_freePair:0,m_pairCount:0,m_pairBuffer:null,m_pairBufferCount:0,m_hashTable:null};b2PairManager.Hash=function(proxyId1,proxyId2) -{var key=((proxyId2<<16)&0xffff0000)|proxyId1;key=~key+((key<<15)&0xFFFF8000);key=key^((key>>12)&0x000fffff);key=key+((key<<2)&0xFFFFFFFC);key=key^((key>>4)&0x0fffffff);key=key*2057;key=key^((key>>16)&0x0000ffff);return key;};b2PairManager.Equals=function(pair,proxyId1,proxyId2) -{return(pair.proxyId1==proxyId1&&pair.proxyId2==proxyId2);};b2PairManager.EqualsPair=function(pair1,pair2) -{return pair1.proxyId1==pair2.proxyId1&&pair1.proxyId2==pair2.proxyId2;}; -var b2BroadPhase=Class.create();b2BroadPhase.prototype={initialize:function(worldAABB,callback){this.m_pairManager=new b2PairManager();this.m_proxyPool=new Array(b2Settings.b2_maxPairs);this.m_bounds=new Array(2*b2Settings.b2_maxProxies);this.m_queryResults=new Array(b2Settings.b2_maxProxies);this.m_quantizationFactor=new b2Vec2();var i=0;this.m_pairManager.Initialize(this,callback);this.m_worldAABB=worldAABB;this.m_proxyCount=0;for(i=0;i<b2Settings.b2_maxProxies;i++){this.m_queryResults[i]=0;} -this.m_bounds=new Array(2);for(i=0;i<2;i++){this.m_bounds[i]=new Array(2*b2Settings.b2_maxProxies);for(var j=0;j<2*b2Settings.b2_maxProxies;j++){this.m_bounds[i][j]=new b2Bound();}} -var dX=worldAABB.maxVertex.x;var dY=worldAABB.maxVertex.y;dX-=worldAABB.minVertex.x;dY-=worldAABB.minVertex.y;this.m_quantizationFactor.x=b2Settings.USHRT_MAX/dX;this.m_quantizationFactor.y=b2Settings.USHRT_MAX/dY;var tProxy;for(i=0;i<b2Settings.b2_maxProxies-1;++i) -{tProxy=new b2Proxy();this.m_proxyPool[i]=tProxy;tProxy.SetNext(i+1);tProxy.timeStamp=0;tProxy.overlapCount=b2BroadPhase.b2_invalid;tProxy.userData=null;} -tProxy=new b2Proxy();this.m_proxyPool[b2Settings.b2_maxProxies-1]=tProxy;tProxy.SetNext(b2Pair.b2_nullProxy);tProxy.timeStamp=0;tProxy.overlapCount=b2BroadPhase.b2_invalid;tProxy.userData=null;this.m_freeProxy=0;this.m_timeStamp=1;this.m_queryResultCount=0;},InRange:function(aabb){var dX;var dY;var d2X;var d2Y;dX=aabb.minVertex.x;dY=aabb.minVertex.y;dX-=this.m_worldAABB.maxVertex.x;dY-=this.m_worldAABB.maxVertex.y;d2X=this.m_worldAABB.minVertex.x;d2Y=this.m_worldAABB.minVertex.y;d2X-=aabb.maxVertex.x;d2Y-=aabb.maxVertex.y;dX=b2Math.b2Max(dX,d2X);dY=b2Math.b2Max(dY,d2Y);return b2Math.b2Max(dX,dY)<0.0;},GetProxy:function(proxyId){if(proxyId==b2Pair.b2_nullProxy||this.m_proxyPool[proxyId].IsValid()==false) -{return null;} -return this.m_proxyPool[proxyId];},CreateProxy:function(aabb,userData){var index=0;var proxy;var proxyId=this.m_freeProxy;proxy=this.m_proxyPool[proxyId];this.m_freeProxy=proxy.GetNext();proxy.overlapCount=0;proxy.userData=userData;var boundCount=2*this.m_proxyCount;var lowerValues=new Array();var upperValues=new Array();this.ComputeBounds(lowerValues,upperValues,aabb);for(var axis=0;axis<2;++axis) -{var bounds=this.m_bounds[axis];var lowerIndex=0;var upperIndex=0;var lowerIndexOut=[lowerIndex];var upperIndexOut=[upperIndex];this.Query(lowerIndexOut,upperIndexOut,lowerValues[axis],upperValues[axis],bounds,boundCount,axis);lowerIndex=lowerIndexOut[0];upperIndex=upperIndexOut[0];var tArr=new Array();var j=0;var tEnd=boundCount-upperIndex -var tBound1;var tBound2;for(j=0;j<tEnd;j++){tArr[j]=new b2Bound();tBound1=tArr[j];tBound2=bounds[upperIndex+j];tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} -tEnd=tArr.length;var tIndex=upperIndex+2;for(j=0;j<tEnd;j++){tBound2=tArr[j];tBound1=bounds[tIndex+j] -tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} -tArr=new Array();tEnd=upperIndex-lowerIndex;for(j=0;j<tEnd;j++){tArr[j]=new b2Bound();tBound1=tArr[j];tBound2=bounds[lowerIndex+j];tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} -tEnd=tArr.length;tIndex=lowerIndex+1;for(j=0;j<tEnd;j++){tBound2=tArr[j];tBound1=bounds[tIndex+j] -tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} -++upperIndex;bounds[lowerIndex].value=lowerValues[axis];bounds[lowerIndex].proxyId=proxyId;bounds[upperIndex].value=upperValues[axis];bounds[upperIndex].proxyId=proxyId;bounds[lowerIndex].stabbingCount=lowerIndex==0?0:bounds[lowerIndex-1].stabbingCount;bounds[upperIndex].stabbingCount=bounds[upperIndex-1].stabbingCount;for(index=lowerIndex;index<upperIndex;++index) -{bounds[index].stabbingCount++;} -for(index=lowerIndex;index<boundCount+2;++index) -{var proxy2=this.m_proxyPool[bounds[index].proxyId];if(bounds[index].IsLower()) -{proxy2.lowerBounds[axis]=index;} -else -{proxy2.upperBounds[axis]=index;}}} -++this.m_proxyCount;for(var i=0;i<this.m_queryResultCount;++i) -{this.m_pairManager.AddBufferedPair(proxyId,this.m_queryResults[i]);} -this.m_pairManager.Commit();this.m_queryResultCount=0;this.IncrementTimeStamp();return proxyId;},DestroyProxy:function(proxyId){var proxy=this.m_proxyPool[proxyId];var boundCount=2*this.m_proxyCount;for(var axis=0;axis<2;++axis) -{var bounds=this.m_bounds[axis];var lowerIndex=proxy.lowerBounds[axis];var upperIndex=proxy.upperBounds[axis];var lowerValue=bounds[lowerIndex].value;var upperValue=bounds[upperIndex].value;var tArr=new Array();var j=0;var tEnd=upperIndex-lowerIndex-1;var tBound1;var tBound2;for(j=0;j<tEnd;j++){tArr[j]=new b2Bound();tBound1=tArr[j];tBound2=bounds[lowerIndex+1+j];tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} -tEnd=tArr.length;var tIndex=lowerIndex;for(j=0;j<tEnd;j++){tBound2=tArr[j];tBound1=bounds[tIndex+j] -tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} -tArr=new Array();tEnd=boundCount-upperIndex-1;for(j=0;j<tEnd;j++){tArr[j]=new b2Bound();tBound1=tArr[j];tBound2=bounds[upperIndex+1+j];tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} -tEnd=tArr.length;tIndex=upperIndex-1;for(j=0;j<tEnd;j++){tBound2=tArr[j];tBound1=bounds[tIndex+j] -tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} -tEnd=boundCount-2;for(var index=lowerIndex;index<tEnd;++index) -{var proxy2=this.m_proxyPool[bounds[index].proxyId];if(bounds[index].IsLower()) -{proxy2.lowerBounds[axis]=index;} -else -{proxy2.upperBounds[axis]=index;}} -tEnd=upperIndex-1;for(var index2=lowerIndex;index2<tEnd;++index2) -{bounds[index2].stabbingCount--;} -this.Query([0],[0],lowerValue,upperValue,bounds,boundCount-2,axis);} -for(var i=0;i<this.m_queryResultCount;++i) -{this.m_pairManager.RemoveBufferedPair(proxyId,this.m_queryResults[i]);} -this.m_pairManager.Commit();this.m_queryResultCount=0;this.IncrementTimeStamp();proxy.userData=null;proxy.overlapCount=b2BroadPhase.b2_invalid;proxy.lowerBounds[0]=b2BroadPhase.b2_invalid;proxy.lowerBounds[1]=b2BroadPhase.b2_invalid;proxy.upperBounds[0]=b2BroadPhase.b2_invalid;proxy.upperBounds[1]=b2BroadPhase.b2_invalid;proxy.SetNext(this.m_freeProxy);this.m_freeProxy=proxyId;--this.m_proxyCount;},MoveProxy:function(proxyId,aabb){var axis=0;var index=0;var bound;var prevBound -var nextBound -var nextProxyId=0;var nextProxy;if(proxyId==b2Pair.b2_nullProxy||b2Settings.b2_maxProxies<=proxyId) -{return;} -if(aabb.IsValid()==false) -{return;} -var boundCount=2*this.m_proxyCount;var proxy=this.m_proxyPool[proxyId];var newValues=new b2BoundValues();this.ComputeBounds(newValues.lowerValues,newValues.upperValues,aabb);var oldValues=new b2BoundValues();for(axis=0;axis<2;++axis) -{oldValues.lowerValues[axis]=this.m_bounds[axis][proxy.lowerBounds[axis]].value;oldValues.upperValues[axis]=this.m_bounds[axis][proxy.upperBounds[axis]].value;} -for(axis=0;axis<2;++axis) -{var bounds=this.m_bounds[axis];var lowerIndex=proxy.lowerBounds[axis];var upperIndex=proxy.upperBounds[axis];var lowerValue=newValues.lowerValues[axis];var upperValue=newValues.upperValues[axis];var deltaLower=lowerValue-bounds[lowerIndex].value;var deltaUpper=upperValue-bounds[upperIndex].value;bounds[lowerIndex].value=lowerValue;bounds[upperIndex].value=upperValue;if(deltaLower<0) -{index=lowerIndex;while(index>0&&lowerValue<bounds[index-1].value) -{bound=bounds[index];prevBound=bounds[index-1];var prevProxyId=prevBound.proxyId;var prevProxy=this.m_proxyPool[prevBound.proxyId];prevBound.stabbingCount++;if(prevBound.IsUpper()==true) -{if(this.TestOverlap(newValues,prevProxy)) -{this.m_pairManager.AddBufferedPair(proxyId,prevProxyId);} -prevProxy.upperBounds[axis]++;bound.stabbingCount++;} -else -{prevProxy.lowerBounds[axis]++;bound.stabbingCount--;} -proxy.lowerBounds[axis]--;bound.Swap(prevBound);--index;}} -if(deltaUpper>0) -{index=upperIndex;while(index<boundCount-1&&bounds[index+1].value<=upperValue) -{bound=bounds[index];nextBound=bounds[index+1];nextProxyId=nextBound.proxyId;nextProxy=this.m_proxyPool[nextProxyId];nextBound.stabbingCount++;if(nextBound.IsLower()==true) -{if(this.TestOverlap(newValues,nextProxy)) -{this.m_pairManager.AddBufferedPair(proxyId,nextProxyId);} -nextProxy.lowerBounds[axis]--;bound.stabbingCount++;} -else -{nextProxy.upperBounds[axis]--;bound.stabbingCount--;} -proxy.upperBounds[axis]++;bound.Swap(nextBound);index++;}} -if(deltaLower>0) -{index=lowerIndex;while(index<boundCount-1&&bounds[index+1].value<=lowerValue) -{bound=bounds[index];nextBound=bounds[index+1];nextProxyId=nextBound.proxyId;nextProxy=this.m_proxyPool[nextProxyId];nextBound.stabbingCount--;if(nextBound.IsUpper()) -{if(this.TestOverlap(oldValues,nextProxy)) -{this.m_pairManager.RemoveBufferedPair(proxyId,nextProxyId);} -nextProxy.upperBounds[axis]--;bound.stabbingCount--;} -else -{nextProxy.lowerBounds[axis]--;bound.stabbingCount++;} -proxy.lowerBounds[axis]++;bound.Swap(nextBound);index++;}} -if(deltaUpper<0) -{index=upperIndex;while(index>0&&upperValue<bounds[index-1].value) -{bound=bounds[index];prevBound=bounds[index-1];prevProxyId=prevBound.proxyId;prevProxy=this.m_proxyPool[prevProxyId];prevBound.stabbingCount--;if(prevBound.IsLower()==true) -{if(this.TestOverlap(oldValues,prevProxy)) -{this.m_pairManager.RemoveBufferedPair(proxyId,prevProxyId);} -prevProxy.lowerBounds[axis]++;bound.stabbingCount--;} -else -{prevProxy.upperBounds[axis]++;bound.stabbingCount++;} -proxy.upperBounds[axis]--;bound.Swap(prevBound);index--;}}}},Commit:function(){this.m_pairManager.Commit();},QueryAABB:function(aabb,userData,maxCount){var lowerValues=new Array();var upperValues=new Array();this.ComputeBounds(lowerValues,upperValues,aabb);var lowerIndex=0;var upperIndex=0;var lowerIndexOut=[lowerIndex];var upperIndexOut=[upperIndex];this.Query(lowerIndexOut,upperIndexOut,lowerValues[0],upperValues[0],this.m_bounds[0],2*this.m_proxyCount,0);this.Query(lowerIndexOut,upperIndexOut,lowerValues[1],upperValues[1],this.m_bounds[1],2*this.m_proxyCount,1);var count=0;for(var i=0;i<this.m_queryResultCount&&count<maxCount;++i,++count) -{var proxy=this.m_proxyPool[this.m_queryResults[i]];userData[i]=proxy.userData;} -this.m_queryResultCount=0;this.IncrementTimeStamp();return count;},Validate:function(){var pair;var proxy1;var proxy2;var overlap;for(var axis=0;axis<2;++axis) -{var bounds=this.m_bounds[axis];var boundCount=2*this.m_proxyCount;var stabbingCount=0;for(var i=0;i<boundCount;++i) -{var bound=bounds[i];if(bound.IsLower()==true) -{stabbingCount++;} -else -{stabbingCount--;}}}},ComputeBounds:function(lowerValues,upperValues,aabb) -{var minVertexX=aabb.minVertex.x;var minVertexY=aabb.minVertex.y;minVertexX=b2Math.b2Min(minVertexX,this.m_worldAABB.maxVertex.x);minVertexY=b2Math.b2Min(minVertexY,this.m_worldAABB.maxVertex.y);minVertexX=b2Math.b2Max(minVertexX,this.m_worldAABB.minVertex.x);minVertexY=b2Math.b2Max(minVertexY,this.m_worldAABB.minVertex.y);var maxVertexX=aabb.maxVertex.x;var maxVertexY=aabb.maxVertex.y;maxVertexX=b2Math.b2Min(maxVertexX,this.m_worldAABB.maxVertex.x);maxVertexY=b2Math.b2Min(maxVertexY,this.m_worldAABB.maxVertex.y);maxVertexX=b2Math.b2Max(maxVertexX,this.m_worldAABB.minVertex.x);maxVertexY=b2Math.b2Max(maxVertexY,this.m_worldAABB.minVertex.y);lowerValues[0]=(this.m_quantizationFactor.x*(minVertexX-this.m_worldAABB.minVertex.x))&(b2Settings.USHRT_MAX-1);upperValues[0]=((this.m_quantizationFactor.x*(maxVertexX-this.m_worldAABB.minVertex.x))&0x0000ffff)|1;lowerValues[1]=(this.m_quantizationFactor.y*(minVertexY-this.m_worldAABB.minVertex.y))&(b2Settings.USHRT_MAX-1);upperValues[1]=((this.m_quantizationFactor.y*(maxVertexY-this.m_worldAABB.minVertex.y))&0x0000ffff)|1;},TestOverlapValidate:function(p1,p2){for(var axis=0;axis<2;++axis) -{var bounds=this.m_bounds[axis];if(bounds[p1.lowerBounds[axis]].value>bounds[p2.upperBounds[axis]].value) -return false;if(bounds[p1.upperBounds[axis]].value<bounds[p2.lowerBounds[axis]].value) -return false;} -return true;},TestOverlap:function(b,p) -{for(var axis=0;axis<2;++axis) -{var bounds=this.m_bounds[axis];if(b.lowerValues[axis]>bounds[p.upperBounds[axis]].value) -return false;if(b.upperValues[axis]<bounds[p.lowerBounds[axis]].value) -return false;} -return true;},Query:function(lowerQueryOut,upperQueryOut,lowerValue,upperValue,bounds,boundCount,axis){var lowerQuery=b2BroadPhase.BinarySearch(bounds,boundCount,lowerValue);var upperQuery=b2BroadPhase.BinarySearch(bounds,boundCount,upperValue);for(var j=lowerQuery;j<upperQuery;++j) -{if(bounds[j].IsLower()) -{this.IncrementOverlapCount(bounds[j].proxyId);}} -if(lowerQuery>0) -{var i=lowerQuery-1;var s=bounds[i].stabbingCount;while(s) -{if(bounds[i].IsLower()) -{var proxy=this.m_proxyPool[bounds[i].proxyId];if(lowerQuery<=proxy.upperBounds[axis]) -{this.IncrementOverlapCount(bounds[i].proxyId);--s;}} ---i;}} -lowerQueryOut[0]=lowerQuery;upperQueryOut[0]=upperQuery;},IncrementOverlapCount:function(proxyId){var proxy=this.m_proxyPool[proxyId];if(proxy.timeStamp<this.m_timeStamp) -{proxy.timeStamp=this.m_timeStamp;proxy.overlapCount=1;} -else -{proxy.overlapCount=2;this.m_queryResults[this.m_queryResultCount]=proxyId;++this.m_queryResultCount;}},IncrementTimeStamp:function(){if(this.m_timeStamp==b2Settings.USHRT_MAX) -{for(var i=0;i<b2Settings.b2_maxProxies;++i) -{this.m_proxyPool[i].timeStamp=0;} -this.m_timeStamp=1;} -else -{++this.m_timeStamp;}},m_pairManager:new b2PairManager(),m_proxyPool:new Array(b2Settings.b2_maxPairs),m_freeProxy:0,m_bounds:new Array(2*b2Settings.b2_maxProxies),m_queryResults:new Array(b2Settings.b2_maxProxies),m_queryResultCount:0,m_worldAABB:null,m_quantizationFactor:new b2Vec2(),m_proxyCount:0,m_timeStamp:0};b2BroadPhase.s_validate=false;b2BroadPhase.b2_invalid=b2Settings.USHRT_MAX;b2BroadPhase.b2_nullEdge=b2Settings.USHRT_MAX;b2BroadPhase.BinarySearch=function(bounds,count,value) -{var low=0;var high=count-1;while(low<=high) -{var mid=Math.floor((low+high)/2);if(bounds[mid].value>value) -{high=mid-1;} -else if(bounds[mid].value<value) -{low=mid+1;} -else -{return(mid);}} -return(low);}; -var b2Collision=Class.create();b2Collision.prototype={initialize:function(){}} -b2Collision.b2_nullFeature=0x000000ff;b2Collision.ClipSegmentToLine=function(vOut,vIn,normal,offset) -{var numOut=0;var vIn0=vIn[0].v;var vIn1=vIn[1].v;var distance0=b2Math.b2Dot(normal,vIn[0].v)-offset;var distance1=b2Math.b2Dot(normal,vIn[1].v)-offset;if(distance0<=0.0)vOut[numOut++]=vIn[0];if(distance1<=0.0)vOut[numOut++]=vIn[1];if(distance0*distance1<0.0) -{var interp=distance0/(distance0-distance1);var tVec=vOut[numOut].v;tVec.x=vIn0.x+interp*(vIn1.x-vIn0.x);tVec.y=vIn0.y+interp*(vIn1.y-vIn0.y);if(distance0>0.0) -{vOut[numOut].id=vIn[0].id;} -else -{vOut[numOut].id=vIn[1].id;} -++numOut;} -return numOut;};b2Collision.EdgeSeparation=function(poly1,edge1,poly2) -{var vert1s=poly1.m_vertices;var count2=poly2.m_vertexCount;var vert2s=poly2.m_vertices;var normalX=poly1.m_normals[edge1].x;var normalY=poly1.m_normals[edge1].y;var tX=normalX;var tMat=poly1.m_R;normalX=tMat.col1.x*tX+tMat.col2.x*normalY;normalY=tMat.col1.y*tX+tMat.col2.y*normalY;var normalLocal2X=normalX;var normalLocal2Y=normalY;tMat=poly2.m_R;tX=normalLocal2X*tMat.col1.x+normalLocal2Y*tMat.col1.y;normalLocal2Y=normalLocal2X*tMat.col2.x+normalLocal2Y*tMat.col2.y;normalLocal2X=tX;var vertexIndex2=0;var minDot=Number.MAX_VALUE;for(var i=0;i<count2;++i) -{var tVec=vert2s[i];var dot=tVec.x*normalLocal2X+tVec.y*normalLocal2Y;if(dot<minDot) -{minDot=dot;vertexIndex2=i;}} -tMat=poly1.m_R;var v1X=poly1.m_position.x+(tMat.col1.x*vert1s[edge1].x+tMat.col2.x*vert1s[edge1].y) -var v1Y=poly1.m_position.y+(tMat.col1.y*vert1s[edge1].x+tMat.col2.y*vert1s[edge1].y) -tMat=poly2.m_R;var v2X=poly2.m_position.x+(tMat.col1.x*vert2s[vertexIndex2].x+tMat.col2.x*vert2s[vertexIndex2].y) -var v2Y=poly2.m_position.y+(tMat.col1.y*vert2s[vertexIndex2].x+tMat.col2.y*vert2s[vertexIndex2].y) -v2X-=v1X;v2Y-=v1Y;var separation=v2X*normalX+v2Y*normalY;return separation;};b2Collision.FindMaxSeparation=function(edgeIndex,poly1,poly2,conservative) -{var count1=poly1.m_vertexCount;var dX=poly2.m_position.x-poly1.m_position.x;var dY=poly2.m_position.y-poly1.m_position.y;var dLocal1X=(dX*poly1.m_R.col1.x+dY*poly1.m_R.col1.y);var dLocal1Y=(dX*poly1.m_R.col2.x+dY*poly1.m_R.col2.y);var edge=0;var maxDot=-Number.MAX_VALUE;for(var i=0;i<count1;++i) -{var dot=(poly1.m_normals[i].x*dLocal1X+poly1.m_normals[i].y*dLocal1Y);if(dot>maxDot) -{maxDot=dot;edge=i;}} -var s=b2Collision.EdgeSeparation(poly1,edge,poly2);if(s>0.0&&conservative==false) -{return s;} -var prevEdge=edge-1>=0?edge-1:count1-1;var sPrev=b2Collision.EdgeSeparation(poly1,prevEdge,poly2);if(sPrev>0.0&&conservative==false) -{return sPrev;} -var nextEdge=edge+1<count1?edge+1:0;var sNext=b2Collision.EdgeSeparation(poly1,nextEdge,poly2);if(sNext>0.0&&conservative==false) -{return sNext;} -var bestEdge=0;var bestSeparation;var increment=0;if(sPrev>s&&sPrev>sNext) -{increment=-1;bestEdge=prevEdge;bestSeparation=sPrev;} -else if(sNext>s) -{increment=1;bestEdge=nextEdge;bestSeparation=sNext;} -else -{edgeIndex[0]=edge;return s;} -while(true) -{if(increment==-1) -edge=bestEdge-1>=0?bestEdge-1:count1-1;else -edge=bestEdge+1<count1?bestEdge+1:0;s=b2Collision.EdgeSeparation(poly1,edge,poly2);if(s>0.0&&conservative==false) -{return s;} -if(s>bestSeparation) -{bestEdge=edge;bestSeparation=s;} -else -{break;}} -edgeIndex[0]=bestEdge;return bestSeparation;};b2Collision.FindIncidentEdge=function(c,poly1,edge1,poly2) -{var count1=poly1.m_vertexCount;var vert1s=poly1.m_vertices;var count2=poly2.m_vertexCount;var vert2s=poly2.m_vertices;var vertex11=edge1;var vertex12=edge1+1==count1?0:edge1+1;var tVec=vert1s[vertex12];var normal1Local1X=tVec.x;var normal1Local1Y=tVec.y;tVec=vert1s[vertex11];normal1Local1X-=tVec.x;normal1Local1Y-=tVec.y;var tX=normal1Local1X;normal1Local1X=normal1Local1Y;normal1Local1Y=-tX;var invLength=1.0/Math.sqrt(normal1Local1X*normal1Local1X+normal1Local1Y*normal1Local1Y);normal1Local1X*=invLength;normal1Local1Y*=invLength;var normal1X=normal1Local1X;var normal1Y=normal1Local1Y;tX=normal1X;var tMat=poly1.m_R;normal1X=tMat.col1.x*tX+tMat.col2.x*normal1Y;normal1Y=tMat.col1.y*tX+tMat.col2.y*normal1Y;var normal1Local2X=normal1X;var normal1Local2Y=normal1Y;tMat=poly2.m_R;tX=normal1Local2X*tMat.col1.x+normal1Local2Y*tMat.col1.y;normal1Local2Y=normal1Local2X*tMat.col2.x+normal1Local2Y*tMat.col2.y;normal1Local2X=tX;var vertex21=0;var vertex22=0;var minDot=Number.MAX_VALUE;for(var i=0;i<count2;++i) -{var i1=i;var i2=i+1<count2?i+1:0;tVec=vert2s[i2];var normal2Local2X=tVec.x;var normal2Local2Y=tVec.y;tVec=vert2s[i1];normal2Local2X-=tVec.x;normal2Local2Y-=tVec.y;tX=normal2Local2X;normal2Local2X=normal2Local2Y;normal2Local2Y=-tX;invLength=1.0/Math.sqrt(normal2Local2X*normal2Local2X+normal2Local2Y*normal2Local2Y);normal2Local2X*=invLength;normal2Local2Y*=invLength;var dot=normal2Local2X*normal1Local2X+normal2Local2Y*normal1Local2Y;if(dot<minDot) -{minDot=dot;vertex21=i1;vertex22=i2;}} -var tClip;tClip=c[0];tVec=tClip.v;tVec.SetV(vert2s[vertex21]);tVec.MulM(poly2.m_R);tVec.Add(poly2.m_position);tClip.id.features.referenceFace=edge1;tClip.id.features.incidentEdge=vertex21;tClip.id.features.incidentVertex=vertex21;tClip=c[1];tVec=tClip.v;tVec.SetV(vert2s[vertex22]);tVec.MulM(poly2.m_R);tVec.Add(poly2.m_position);tClip.id.features.referenceFace=edge1;tClip.id.features.incidentEdge=vertex21;tClip.id.features.incidentVertex=vertex22;};b2Collision.b2CollidePolyTempVec=new b2Vec2();b2Collision.b2CollidePoly=function(manifold,polyA,polyB,conservative) -{manifold.pointCount=0;var edgeA=0;var edgeAOut=[edgeA];var separationA=b2Collision.FindMaxSeparation(edgeAOut,polyA,polyB,conservative);edgeA=edgeAOut[0];if(separationA>0.0&&conservative==false) -return;var edgeB=0;var edgeBOut=[edgeB];var separationB=b2Collision.FindMaxSeparation(edgeBOut,polyB,polyA,conservative);edgeB=edgeBOut[0];if(separationB>0.0&&conservative==false) -return;var poly1;var poly2;var edge1=0;var flip=0;var k_relativeTol=0.98;var k_absoluteTol=0.001;if(separationB>k_relativeTol*separationA+k_absoluteTol) -{poly1=polyB;poly2=polyA;edge1=edgeB;flip=1;} -else -{poly1=polyA;poly2=polyB;edge1=edgeA;flip=0;} -var incidentEdge=[new ClipVertex(),new ClipVertex()];b2Collision.FindIncidentEdge(incidentEdge,poly1,edge1,poly2);var count1=poly1.m_vertexCount;var vert1s=poly1.m_vertices;var v11=vert1s[edge1];var v12=edge1+1<count1?vert1s[edge1+1]:vert1s[0];var dvX=v12.x-v11.x;var dvY=v12.y-v11.y;var sideNormalX=v12.x-v11.x;var sideNormalY=v12.y-v11.y;var tX=sideNormalX;var tMat=poly1.m_R;sideNormalX=tMat.col1.x*tX+tMat.col2.x*sideNormalY;sideNormalY=tMat.col1.y*tX+tMat.col2.y*sideNormalY;var invLength=1.0/Math.sqrt(sideNormalX*sideNormalX+sideNormalY*sideNormalY);sideNormalX*=invLength;sideNormalY*=invLength;var frontNormalX=sideNormalX;var frontNormalY=sideNormalY;tX=frontNormalX;frontNormalX=frontNormalY;frontNormalY=-tX;var v11X=v11.x;var v11Y=v11.y;tX=v11X;tMat=poly1.m_R;v11X=tMat.col1.x*tX+tMat.col2.x*v11Y;v11Y=tMat.col1.y*tX+tMat.col2.y*v11Y;v11X+=poly1.m_position.x;v11Y+=poly1.m_position.y;var v12X=v12.x;var v12Y=v12.y;tX=v12X;tMat=poly1.m_R;v12X=tMat.col1.x*tX+tMat.col2.x*v12Y;v12Y=tMat.col1.y*tX+tMat.col2.y*v12Y;v12X+=poly1.m_position.x;v12Y+=poly1.m_position.y;var frontOffset=frontNormalX*v11X+frontNormalY*v11Y;var sideOffset1=-(sideNormalX*v11X+sideNormalY*v11Y);var sideOffset2=sideNormalX*v12X+sideNormalY*v12Y;var clipPoints1=[new ClipVertex(),new ClipVertex()];var clipPoints2=[new ClipVertex(),new ClipVertex()];var np=0;b2Collision.b2CollidePolyTempVec.Set(-sideNormalX,-sideNormalY);np=b2Collision.ClipSegmentToLine(clipPoints1,incidentEdge,b2Collision.b2CollidePolyTempVec,sideOffset1);if(np<2) -return;b2Collision.b2CollidePolyTempVec.Set(sideNormalX,sideNormalY);np=b2Collision.ClipSegmentToLine(clipPoints2,clipPoints1,b2Collision.b2CollidePolyTempVec,sideOffset2);if(np<2) -return;if(flip){manifold.normal.Set(-frontNormalX,-frontNormalY);} -else{manifold.normal.Set(frontNormalX,frontNormalY);} -var pointCount=0;for(var i=0;i<b2Settings.b2_maxManifoldPoints;++i) -{var tVec=clipPoints2[i].v;var separation=(frontNormalX*tVec.x+frontNormalY*tVec.y)-frontOffset;if(separation<=0.0||conservative==true) -{var cp=manifold.points[pointCount];cp.separation=separation;cp.position.SetV(clipPoints2[i].v);cp.id.Set(clipPoints2[i].id);cp.id.features.flip=flip;++pointCount;}} -manifold.pointCount=pointCount;};b2Collision.b2CollideCircle=function(manifold,circle1,circle2,conservative) -{manifold.pointCount=0;var dX=circle2.m_position.x-circle1.m_position.x;var dY=circle2.m_position.y-circle1.m_position.y;var distSqr=dX*dX+dY*dY;var radiusSum=circle1.m_radius+circle2.m_radius;if(distSqr>radiusSum*radiusSum&&conservative==false) -{return;} -var separation;if(distSqr<Number.MIN_VALUE) -{separation=-radiusSum;manifold.normal.Set(0.0,1.0);} -else -{var dist=Math.sqrt(distSqr);separation=dist-radiusSum;var a=1.0/dist;manifold.normal.x=a*dX;manifold.normal.y=a*dY;} -manifold.pointCount=1;var tPoint=manifold.points[0];tPoint.id.set_key(0);tPoint.separation=separation;tPoint.position.x=circle2.m_position.x-(circle2.m_radius*manifold.normal.x);tPoint.position.y=circle2.m_position.y-(circle2.m_radius*manifold.normal.y);};b2Collision.b2CollidePolyAndCircle=function(manifold,poly,circle,conservative) -{manifold.pointCount=0;var tPoint;var dX;var dY;var xLocalX=circle.m_position.x-poly.m_position.x;var xLocalY=circle.m_position.y-poly.m_position.y;var tMat=poly.m_R;var tX=xLocalX*tMat.col1.x+xLocalY*tMat.col1.y;xLocalY=xLocalX*tMat.col2.x+xLocalY*tMat.col2.y;xLocalX=tX;var dist;var normalIndex=0;var separation=-Number.MAX_VALUE;var radius=circle.m_radius;for(var i=0;i<poly.m_vertexCount;++i) -{var s=poly.m_normals[i].x*(xLocalX-poly.m_vertices[i].x)+poly.m_normals[i].y*(xLocalY-poly.m_vertices[i].y);if(s>radius) -{return;} -if(s>separation) -{separation=s;normalIndex=i;}} -if(separation<Number.MIN_VALUE) -{manifold.pointCount=1;var tVec=poly.m_normals[normalIndex];manifold.normal.x=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y;manifold.normal.y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y;tPoint=manifold.points[0];tPoint.id.features.incidentEdge=normalIndex;tPoint.id.features.incidentVertex=b2Collision.b2_nullFeature;tPoint.id.features.referenceFace=b2Collision.b2_nullFeature;tPoint.id.features.flip=0;tPoint.position.x=circle.m_position.x-radius*manifold.normal.x;tPoint.position.y=circle.m_position.y-radius*manifold.normal.y;tPoint.separation=separation-radius;return;} -var vertIndex1=normalIndex;var vertIndex2=vertIndex1+1<poly.m_vertexCount?vertIndex1+1:0;var eX=poly.m_vertices[vertIndex2].x-poly.m_vertices[vertIndex1].x;var eY=poly.m_vertices[vertIndex2].y-poly.m_vertices[vertIndex1].y;var length=Math.sqrt(eX*eX+eY*eY);eX/=length;eY/=length;if(length<Number.MIN_VALUE) -{dX=xLocalX-poly.m_vertices[vertIndex1].x;dY=xLocalY-poly.m_vertices[vertIndex1].y;dist=Math.sqrt(dX*dX+dY*dY);dX/=dist;dY/=dist;if(dist>radius) -{return;} -manifold.pointCount=1;manifold.normal.Set(tMat.col1.x*dX+tMat.col2.x*dY,tMat.col1.y*dX+tMat.col2.y*dY);tPoint=manifold.points[0];tPoint.id.features.incidentEdge=b2Collision.b2_nullFeature;tPoint.id.features.incidentVertex=vertIndex1;tPoint.id.features.referenceFace=b2Collision.b2_nullFeature;tPoint.id.features.flip=0;tPoint.position.x=circle.m_position.x-radius*manifold.normal.x;tPoint.position.y=circle.m_position.y-radius*manifold.normal.y;tPoint.separation=dist-radius;return;} -var u=(xLocalX-poly.m_vertices[vertIndex1].x)*eX+(xLocalY-poly.m_vertices[vertIndex1].y)*eY;tPoint=manifold.points[0];tPoint.id.features.incidentEdge=b2Collision.b2_nullFeature;tPoint.id.features.incidentVertex=b2Collision.b2_nullFeature;tPoint.id.features.referenceFace=b2Collision.b2_nullFeature;tPoint.id.features.flip=0;var pX,pY;if(u<=0.0) -{pX=poly.m_vertices[vertIndex1].x;pY=poly.m_vertices[vertIndex1].y;tPoint.id.features.incidentVertex=vertIndex1;} -else if(u>=length) -{pX=poly.m_vertices[vertIndex2].x;pY=poly.m_vertices[vertIndex2].y;tPoint.id.features.incidentVertex=vertIndex2;} -else -{pX=eX*u+poly.m_vertices[vertIndex1].x;pY=eY*u+poly.m_vertices[vertIndex1].y;tPoint.id.features.incidentEdge=vertIndex1;} -dX=xLocalX-pX;dY=xLocalY-pY;dist=Math.sqrt(dX*dX+dY*dY);dX/=dist;dY/=dist;if(dist>radius) -{return;} -manifold.pointCount=1;manifold.normal.Set(tMat.col1.x*dX+tMat.col2.x*dY,tMat.col1.y*dX+tMat.col2.y*dY);tPoint.position.x=circle.m_position.x-radius*manifold.normal.x;tPoint.position.y=circle.m_position.y-radius*manifold.normal.y;tPoint.separation=dist-radius;};b2Collision.b2TestOverlap=function(a,b) -{var t1=b.minVertex;var t2=a.maxVertex;var d1X=t1.x-t2.x;var d1Y=t1.y-t2.y;t1=a.minVertex;t2=b.maxVertex;var d2X=t1.x-t2.x;var d2Y=t1.y-t2.y;if(d1X>0.0||d1Y>0.0) -return false;if(d2X>0.0||d2Y>0.0) -return false;return true;}; -var Features=Class.create();Features.prototype={set_referenceFace:function(value){this._referenceFace=value;this._m_id._key=(this._m_id._key&0xffffff00)|(this._referenceFace&0x000000ff)},get_referenceFace:function(){return this._referenceFace;},_referenceFace:0,set_incidentEdge:function(value){this._incidentEdge=value;this._m_id._key=(this._m_id._key&0xffff00ff)|((this._incidentEdge<<8)&0x0000ff00)},get_incidentEdge:function(){return this._incidentEdge;},_incidentEdge:0,set_incidentVertex:function(value){this._incidentVertex=value;this._m_id._key=(this._m_id._key&0xff00ffff)|((this._incidentVertex<<16)&0x00ff0000)},get_incidentVertex:function(){return this._incidentVertex;},_incidentVertex:0,set_flip:function(value){this._flip=value;this._m_id._key=(this._m_id._key&0x00ffffff)|((this._flip<<24)&0xff000000)},get_flip:function(){return this._flip;},_flip:0,_m_id:null,initialize:function(){}}; -var b2ContactID=Class.create();b2ContactID.prototype={initialize:function(){this.features=new Features();this.features._m_id=this;},Set:function(id){this.set_key(id._key);},Copy:function(){var id=new b2ContactID();id.set_key(this._key);return id;},get_key:function(){return this._key;},set_key:function(value){this._key=value;this.features._referenceFace=this._key&0x000000ff;this.features._incidentEdge=((this._key&0x0000ff00)>>8)&0x000000ff;this.features._incidentVertex=((this._key&0x00ff0000)>>16)&0x000000ff;this.features._flip=((this._key&0xff000000)>>24)&0x000000ff;},features:new Features(),_key:0}; -var b2ContactPoint=Class.create();b2ContactPoint.prototype={position:new b2Vec2(),separation:null,normalImpulse:null,tangentImpulse:null,id:new b2ContactID(),initialize:function(){this.position=new b2Vec2();this.id=new b2ContactID();}};var b2Distance=Class.create();b2Distance.prototype={initialize:function(){}};b2Distance.ProcessTwo=function(p1Out,p2Out,p1s,p2s,points) -{var rX=-points[1].x;var rY=-points[1].y;var dX=points[0].x-points[1].x;var dY=points[0].y-points[1].y;var length=Math.sqrt(dX*dX+dY*dY);dX/=length;dY/=length;var lambda=rX*dX+rY*dY;if(lambda<=0.0||length<Number.MIN_VALUE) -{p1Out.SetV(p1s[1]);p2Out.SetV(p2s[1]);p1s[0].SetV(p1s[1]);p2s[0].SetV(p2s[1]);points[0].SetV(points[1]);return 1;} -lambda/=length;p1Out.x=p1s[1].x+lambda*(p1s[0].x-p1s[1].x);p1Out.y=p1s[1].y+lambda*(p1s[0].y-p1s[1].y);p2Out.x=p2s[1].x+lambda*(p2s[0].x-p2s[1].x);p2Out.y=p2s[1].y+lambda*(p2s[0].y-p2s[1].y);return 2;};b2Distance.ProcessThree=function(p1Out,p2Out,p1s,p2s,points) -{var aX=points[0].x;var aY=points[0].y;var bX=points[1].x;var bY=points[1].y;var cX=points[2].x;var cY=points[2].y;var abX=bX-aX;var abY=bY-aY;var acX=cX-aX;var acY=cY-aY;var bcX=cX-bX;var bcY=cY-bY;var sn=-(aX*abX+aY*abY);var sd=(bX*abX+bY*abY);var tn=-(aX*acX+aY*acY);var td=(cX*acX+cY*acY);var un=-(bX*bcX+bY*bcY);var ud=(cX*bcX+cY*bcY);if(td<=0.0&&ud<=0.0) -{p1Out.SetV(p1s[2]);p2Out.SetV(p2s[2]);p1s[0].SetV(p1s[2]);p2s[0].SetV(p2s[2]);points[0].SetV(points[2]);return 1;} -var n=abX*acY-abY*acX;var vc=n*(aX*bY-aY*bX);var va=n*(bX*cY-bY*cX);if(va<=0.0&&un>=0.0&&ud>=0.0) -{var lambda=un/(un+ud);p1Out.x=p1s[1].x+lambda*(p1s[2].x-p1s[1].x);p1Out.y=p1s[1].y+lambda*(p1s[2].y-p1s[1].y);p2Out.x=p2s[1].x+lambda*(p2s[2].x-p2s[1].x);p2Out.y=p2s[1].y+lambda*(p2s[2].y-p2s[1].y);p1s[0].SetV(p1s[2]);p2s[0].SetV(p2s[2]);points[0].SetV(points[2]);return 2;} -var vb=n*(cX*aY-cY*aX);if(vb<=0.0&&tn>=0.0&&td>=0.0) -{var lambda=tn/(tn+td);p1Out.x=p1s[0].x+lambda*(p1s[2].x-p1s[0].x);p1Out.y=p1s[0].y+lambda*(p1s[2].y-p1s[0].y);p2Out.x=p2s[0].x+lambda*(p2s[2].x-p2s[0].x);p2Out.y=p2s[0].y+lambda*(p2s[2].y-p2s[0].y);p1s[1].SetV(p1s[2]);p2s[1].SetV(p2s[2]);points[1].SetV(points[2]);return 2;} -var denom=va+vb+vc;denom=1.0/denom;var u=va*denom;var v=vb*denom;var w=1.0-u-v;p1Out.x=u*p1s[0].x+v*p1s[1].x+w*p1s[2].x;p1Out.y=u*p1s[0].y+v*p1s[1].y+w*p1s[2].y;p2Out.x=u*p2s[0].x+v*p2s[1].x+w*p2s[2].x;p2Out.y=u*p2s[0].y+v*p2s[1].y+w*p2s[2].y;return 3;};b2Distance.InPoinsts=function(w,points,pointCount) -{for(var i=0;i<pointCount;++i) -{if(w.x==points[i].x&&w.y==points[i].y) -{return true;}} -return false;};b2Distance.Distance=function(p1Out,p2Out,shape1,shape2) -{var p1s=new Array(3);var p2s=new Array(3);var points=new Array(3);var pointCount=0;p1Out.SetV(shape1.m_position);p2Out.SetV(shape2.m_position);var vSqr=0.0;var maxIterations=20;for(var iter=0;iter<maxIterations;++iter) -{var vX=p2Out.x-p1Out.x;var vY=p2Out.y-p1Out.y;var w1=shape1.Support(vX,vY);var w2=shape2.Support(-vX,-vY);vSqr=(vX*vX+vY*vY);var wX=w2.x-w1.x;var wY=w2.y-w1.y;var vw=(vX*wX+vY*wY);if(vSqr-b2Dot(vX*wX+vY*wY)<=0.01*vSqr) -{if(pointCount==0) -{p1Out.SetV(w1);p2Out.SetV(w2);} -b2Distance.g_GJK_Iterations=iter;return Math.sqrt(vSqr);} -switch(pointCount) -{case 0:p1s[0].SetV(w1);p2s[0].SetV(w2);points[0]=w;p1Out.SetV(p1s[0]);p2Out.SetV(p2s[0]);++pointCount;break;case 1:p1s[1].SetV(w1);p2s[1].SetV(w2);points[1].x=wX;points[1].y=wY;pointCount=b2Distance.ProcessTwo(p1Out,p2Out,p1s,p2s,points);break;case 2:p1s[2].SetV(w1);p2s[2].SetV(w2);points[2].x=wX;points[2].y=wY;pointCount=b2Distance.ProcessThree(p1Out,p2Out,p1s,p2s,points);break;} -if(pointCount==3) -{b2Distance.g_GJK_Iterations=iter;return 0.0;} -var maxSqr=-Number.MAX_VALUE;for(var i=0;i<pointCount;++i) -{maxSqr=b2Math.b2Max(maxSqr,(points[i].x*points[i].x+points[i].y*points[i].y));} -if(pointCount==3||vSqr<=100.0*Number.MIN_VALUE*maxSqr) -{b2Distance.g_GJK_Iterations=iter;return Math.sqrt(vSqr);}} -b2Distance.g_GJK_Iterations=maxIterations;return Math.sqrt(vSqr);};b2Distance.g_GJK_Iterations=0; -var b2Manifold=Class.create();b2Manifold.prototype={initialize:function(){this.points=new Array(b2Settings.b2_maxManifoldPoints);for(var i=0;i<b2Settings.b2_maxManifoldPoints;i++){this.points[i]=new b2ContactPoint();} -this.normal=new b2Vec2();},points:null,normal:null,pointCount:0}; -var b2OBB=Class.create();b2OBB.prototype={R:new b2Mat22(),center:new b2Vec2(),extents:new b2Vec2(),initialize:function(){this.R=new b2Mat22();this.center=new b2Vec2();this.extents=new b2Vec2();}}; -var b2Proxy=Class.create();b2Proxy.prototype={GetNext:function(){return this.lowerBounds[0];},SetNext:function(next){this.lowerBounds[0]=next;},IsValid:function(){return this.overlapCount!=b2BroadPhase.b2_invalid;},lowerBounds:[(0),(0)],upperBounds:[(0),(0)],overlapCount:0,timeStamp:0,userData:null,initialize:function(){this.lowerBounds=[(0),(0)];this.upperBounds=[(0),(0)];}} - -var ClipVertex=Class.create();ClipVertex.prototype={v:new b2Vec2(),id:new b2ContactID(),initialize:function(){this.v=new b2Vec2();this.id=new b2ContactID();}};var b2Shape=Class.create();b2Shape.prototype={TestPoint:function(p){return false},GetUserData:function(){return this.m_userData;},GetType:function(){return this.m_type;},GetBody:function(){return this.m_body;},GetPosition:function(){return this.m_position;},GetRotationMatrix:function(){return this.m_R;},ResetProxy:function(broadPhase){},GetNext:function(){return this.m_next;},initialize:function(def,body){this.m_R=new b2Mat22();this.m_position=new b2Vec2();this.m_userData=def.userData;this.m_friction=def.friction;this.m_restitution=def.restitution;this.m_body=body;this.m_proxyId=b2Pair.b2_nullProxy;this.m_maxRadius=0.0;this.m_categoryBits=def.categoryBits;this.m_maskBits=def.maskBits;this.m_groupIndex=def.groupIndex;},DestroyProxy:function() -{if(this.m_proxyId!=b2Pair.b2_nullProxy) -{this.m_body.m_world.m_broadPhase.DestroyProxy(this.m_proxyId);this.m_proxyId=b2Pair.b2_nullProxy;}},Synchronize:function(position1,R1,position2,R2){},QuickSync:function(position,R){},Support:function(dX,dY,out){},GetMaxRadius:function(){return this.m_maxRadius;},m_next:null,m_R:new b2Mat22(),m_position:new b2Vec2(),m_type:0,m_userData:null,m_body:null,m_friction:null,m_restitution:null,m_maxRadius:null,m_proxyId:0,m_categoryBits:0,m_maskBits:0,m_groupIndex:0};b2Shape.Create=function(def,body,center){switch(def.type) -{case b2Shape.e_circleShape:{return new b2CircleShape(def,body,center);} -case b2Shape.e_boxShape:case b2Shape.e_polyShape:{return new b2PolyShape(def,body,center);}} -return null;};b2Shape.Destroy=function(shape) -{if(shape.m_proxyId!=b2Pair.b2_nullProxy) -shape.m_body.m_world.m_broadPhase.DestroyProxy(shape.m_proxyId);};b2Shape.e_unknownShape=-1;b2Shape.e_circleShape=0;b2Shape.e_boxShape=1;b2Shape.e_polyShape=2;b2Shape.e_meshShape=3;b2Shape.e_shapeTypeCount=4;b2Shape.PolyMass=function(massData,vs,count,rho) -{var center=new b2Vec2();center.SetZero();var area=0.0;var I=0.0;var pRef=new b2Vec2(0.0,0.0);var inv3=1.0/3.0;for(var i=0;i<count;++i) -{var p1=pRef;var p2=vs[i];var p3=i+1<count?vs[i+1]:vs[0];var e1=b2Math.SubtractVV(p2,p1);var e2=b2Math.SubtractVV(p3,p1);var D=b2Math.b2CrossVV(e1,e2);var triangleArea=0.5*D;area+=triangleArea;var tVec=new b2Vec2();tVec.SetV(p1);tVec.Add(p2);tVec.Add(p3);tVec.Multiply(inv3*triangleArea);center.Add(tVec);var px=p1.x;var py=p1.y;var ex1=e1.x;var ey1=e1.y;var ex2=e2.x;var ey2=e2.y;var intx2=inv3*(0.25*(ex1*ex1+ex2*ex1+ex2*ex2)+(px*ex1+px*ex2))+0.5*px*px;var inty2=inv3*(0.25*(ey1*ey1+ey2*ey1+ey2*ey2)+(py*ey1+py*ey2))+0.5*py*py;I+=D*(intx2+inty2);} -massData.mass=rho*area;center.Multiply(1.0/area);massData.center=center;I=rho*(I-area*b2Math.b2Dot(center,center));massData.I=I;};b2Shape.PolyCentroid=function(vs,count,out) -{var cX=0.0;var cY=0.0;var area=0.0;var pRefX=0.0;var pRefY=0.0;var inv3=1.0/3.0;for(var i=0;i<count;++i) -{var p1X=pRefX;var p1Y=pRefY;var p2X=vs[i].x;var p2Y=vs[i].y;var p3X=i+1<count?vs[i+1].x:vs[0].x;var p3Y=i+1<count?vs[i+1].y:vs[0].y;var e1X=p2X-p1X;var e1Y=p2Y-p1Y;var e2X=p3X-p1X;var e2Y=p3Y-p1Y;var D=(e1X*e2Y-e1Y*e2X);var triangleArea=0.5*D;area+=triangleArea;cX+=triangleArea*inv3*(p1X+p2X+p3X);cY+=triangleArea*inv3*(p1Y+p2Y+p3Y);} -cX*=1.0/area;cY*=1.0/area;out.Set(cX,cY);}; -var b2ShapeDef=Class.create();b2ShapeDef.prototype={initialize:function() -{this.type=b2Shape.e_unknownShape;this.userData=null;this.localPosition=new b2Vec2(0.0,0.0);this.localRotation=0.0;this.friction=0.2;this.restitution=0.0;this.density=0.0;this.categoryBits=0x0001;this.maskBits=0xFFFF;this.groupIndex=0;},ComputeMass:function(massData) -{massData.center=new b2Vec2(0.0,0.0) -if(this.density==0.0) -{massData.mass=0.0;massData.center.Set(0.0,0.0);massData.I=0.0;};switch(this.type) -{case b2Shape.e_circleShape:{var circle=this;massData.mass=this.density*b2Settings.b2_pi*circle.radius*circle.radius;massData.center.Set(0.0,0.0);massData.I=0.5*(massData.mass)*circle.radius*circle.radius;} -break;case b2Shape.e_boxShape:{var box=this;massData.mass=4.0*this.density*box.extents.x*box.extents.y;massData.center.Set(0.0,0.0);massData.I=massData.mass/3.0*b2Math.b2Dot(box.extents,box.extents);} -break;case b2Shape.e_polyShape:{var poly=this;b2Shape.PolyMass(massData,poly.vertices,poly.vertexCount,this.density);} -break;default:massData.mass=0.0;massData.center.Set(0.0,0.0);massData.I=0.0;break;}},type:0,userData:null,localPosition:null,localRotation:null,friction:null,restitution:null,density:null,categoryBits:0,maskBits:0,groupIndex:0}; -var b2BoxDef=Class.create();Object.extend(b2BoxDef.prototype,b2ShapeDef.prototype);Object.extend(b2BoxDef.prototype,{initialize:function() -{this.type=b2Shape.e_unknownShape;this.userData=null;this.localPosition=new b2Vec2(0.0,0.0);this.localRotation=0.0;this.friction=0.2;this.restitution=0.0;this.density=0.0;this.categoryBits=0x0001;this.maskBits=0xFFFF;this.groupIndex=0;this.type=b2Shape.e_boxShape;this.extents=new b2Vec2(1.0,1.0);},extents:null}); -var b2CircleDef=Class.create();Object.extend(b2CircleDef.prototype,b2ShapeDef.prototype);Object.extend(b2CircleDef.prototype,{initialize:function() -{this.type=b2Shape.e_unknownShape;this.userData=null;this.localPosition=new b2Vec2(0.0,0.0);this.localRotation=0.0;this.friction=0.2;this.restitution=0.0;this.density=0.0;this.categoryBits=0x0001;this.maskBits=0xFFFF;this.groupIndex=0;this.type=b2Shape.e_circleShape;this.radius=1.0;},radius:null});var b2CircleShape=Class.create();Object.extend(b2CircleShape.prototype,b2Shape.prototype);Object.extend(b2CircleShape.prototype,{TestPoint:function(p){var d=new b2Vec2();d.SetV(p);d.Subtract(this.m_position);return b2Math.b2Dot(d,d)<=this.m_radius*this.m_radius;},initialize:function(def,body,localCenter){this.m_R=new b2Mat22();this.m_position=new b2Vec2();this.m_userData=def.userData;this.m_friction=def.friction;this.m_restitution=def.restitution;this.m_body=body;this.m_proxyId=b2Pair.b2_nullProxy;this.m_maxRadius=0.0;this.m_categoryBits=def.categoryBits;this.m_maskBits=def.maskBits;this.m_groupIndex=def.groupIndex;this.m_localPosition=new b2Vec2();var circle=def;this.m_localPosition.Set(def.localPosition.x-localCenter.x,def.localPosition.y-localCenter.y);this.m_type=b2Shape.e_circleShape;this.m_radius=circle.radius;this.m_R.SetM(this.m_body.m_R);var rX=this.m_R.col1.x*this.m_localPosition.x+this.m_R.col2.x*this.m_localPosition.y;var rY=this.m_R.col1.y*this.m_localPosition.x+this.m_R.col2.y*this.m_localPosition.y;this.m_position.x=this.m_body.m_position.x+rX;this.m_position.y=this.m_body.m_position.y+rY;this.m_maxRadius=Math.sqrt(rX*rX+rY*rY)+this.m_radius;var aabb=new b2AABB();aabb.minVertex.Set(this.m_position.x-this.m_radius,this.m_position.y-this.m_radius);aabb.maxVertex.Set(this.m_position.x+this.m_radius,this.m_position.y+this.m_radius);var broadPhase=this.m_body.m_world.m_broadPhase;if(broadPhase.InRange(aabb)) -{this.m_proxyId=broadPhase.CreateProxy(aabb,this);} -else -{this.m_proxyId=b2Pair.b2_nullProxy;} -if(this.m_proxyId==b2Pair.b2_nullProxy) -{this.m_body.Freeze();}},Synchronize:function(position1,R1,position2,R2){this.m_R.SetM(R2);this.m_position.x=(R2.col1.x*this.m_localPosition.x+R2.col2.x*this.m_localPosition.y)+position2.x;this.m_position.y=(R2.col1.y*this.m_localPosition.x+R2.col2.y*this.m_localPosition.y)+position2.y;if(this.m_proxyId==b2Pair.b2_nullProxy) -{return;} -var p1X=position1.x+(R1.col1.x*this.m_localPosition.x+R1.col2.x*this.m_localPosition.y);var p1Y=position1.y+(R1.col1.y*this.m_localPosition.x+R1.col2.y*this.m_localPosition.y);var lowerX=Math.min(p1X,this.m_position.x);var lowerY=Math.min(p1Y,this.m_position.y);var upperX=Math.max(p1X,this.m_position.x);var upperY=Math.max(p1Y,this.m_position.y);var aabb=new b2AABB();aabb.minVertex.Set(lowerX-this.m_radius,lowerY-this.m_radius);aabb.maxVertex.Set(upperX+this.m_radius,upperY+this.m_radius);var broadPhase=this.m_body.m_world.m_broadPhase;if(broadPhase.InRange(aabb)) -{broadPhase.MoveProxy(this.m_proxyId,aabb);} -else -{this.m_body.Freeze();}},QuickSync:function(position,R){this.m_R.SetM(R);this.m_position.x=(R.col1.x*this.m_localPosition.x+R.col2.x*this.m_localPosition.y)+position.x;this.m_position.y=(R.col1.y*this.m_localPosition.x+R.col2.y*this.m_localPosition.y)+position.y;},ResetProxy:function(broadPhase) -{if(this.m_proxyId==b2Pair.b2_nullProxy) -{return;} -var proxy=broadPhase.GetProxy(this.m_proxyId);broadPhase.DestroyProxy(this.m_proxyId);proxy=null;var aabb=new b2AABB();aabb.minVertex.Set(this.m_position.x-this.m_radius,this.m_position.y-this.m_radius);aabb.maxVertex.Set(this.m_position.x+this.m_radius,this.m_position.y+this.m_radius);if(broadPhase.InRange(aabb)) -{this.m_proxyId=broadPhase.CreateProxy(aabb,this);} -else -{this.m_proxyId=b2Pair.b2_nullProxy;} -if(this.m_proxyId==b2Pair.b2_nullProxy) -{this.m_body.Freeze();}},Support:function(dX,dY,out) -{var len=Math.sqrt(dX*dX+dY*dY);dX/=len;dY/=len;out.Set(this.m_position.x+this.m_radius*dX,this.m_position.y+this.m_radius*dY);},m_localPosition:new b2Vec2(),m_radius:null}); -var b2MassData=Class.create();b2MassData.prototype={mass:0.0,center:new b2Vec2(0,0),I:0.0,initialize:function(){this.center=new b2Vec2(0,0);}} - -var b2PolyDef=Class.create();Object.extend(b2PolyDef.prototype,b2ShapeDef.prototype);Object.extend(b2PolyDef.prototype,{initialize:function() -{this.type=b2Shape.e_unknownShape;this.userData=null;this.localPosition=new b2Vec2(0.0,0.0);this.localRotation=0.0;this.friction=0.2;this.restitution=0.0;this.density=0.0;this.categoryBits=0x0001;this.maskBits=0xFFFF;this.groupIndex=0;this.vertices=new Array(b2Settings.b2_maxPolyVertices);this.type=b2Shape.e_polyShape;this.vertexCount=0;for(var i=0;i<b2Settings.b2_maxPolyVertices;i++){this.vertices[i]=new b2Vec2();}},vertices:new Array(b2Settings.b2_maxPolyVertices),vertexCount:0});var b2PolyShape=Class.create();Object.extend(b2PolyShape.prototype,b2Shape.prototype);Object.extend(b2PolyShape.prototype,{TestPoint:function(p){var pLocal=new b2Vec2();pLocal.SetV(p);pLocal.Subtract(this.m_position);pLocal.MulTM(this.m_R);for(var i=0;i<this.m_vertexCount;++i) -{var tVec=new b2Vec2();tVec.SetV(pLocal);tVec.Subtract(this.m_vertices[i]);var dot=b2Math.b2Dot(this.m_normals[i],tVec);if(dot>0.0) -{return false;}} -return true;},initialize:function(def,body,newOrigin){this.m_R=new b2Mat22();this.m_position=new b2Vec2();this.m_userData=def.userData;this.m_friction=def.friction;this.m_restitution=def.restitution;this.m_body=body;this.m_proxyId=b2Pair.b2_nullProxy;this.m_maxRadius=0.0;this.m_categoryBits=def.categoryBits;this.m_maskBits=def.maskBits;this.m_groupIndex=def.groupIndex;this.syncAABB=new b2AABB();this.syncMat=new b2Mat22();this.m_localCentroid=new b2Vec2();this.m_localOBB=new b2OBB();var i=0;var hX;var hY;var tVec;var aabb=new b2AABB();this.m_vertices=new Array(b2Settings.b2_maxPolyVertices);this.m_coreVertices=new Array(b2Settings.b2_maxPolyVertices);this.m_normals=new Array(b2Settings.b2_maxPolyVertices);this.m_type=b2Shape.e_polyShape;var localR=new b2Mat22(def.localRotation);if(def.type==b2Shape.e_boxShape) -{this.m_localCentroid.x=def.localPosition.x-newOrigin.x;this.m_localCentroid.y=def.localPosition.y-newOrigin.y;var box=def;this.m_vertexCount=4;hX=box.extents.x;hY=box.extents.y;var hcX=Math.max(0.0,hX-2.0*b2Settings.b2_linearSlop);var hcY=Math.max(0.0,hY-2.0*b2Settings.b2_linearSlop);tVec=this.m_vertices[0]=new b2Vec2();tVec.x=localR.col1.x*hX+localR.col2.x*hY;tVec.y=localR.col1.y*hX+localR.col2.y*hY;tVec=this.m_vertices[1]=new b2Vec2();tVec.x=localR.col1.x*-hX+localR.col2.x*hY;tVec.y=localR.col1.y*-hX+localR.col2.y*hY;tVec=this.m_vertices[2]=new b2Vec2();tVec.x=localR.col1.x*-hX+localR.col2.x*-hY;tVec.y=localR.col1.y*-hX+localR.col2.y*-hY;tVec=this.m_vertices[3]=new b2Vec2();tVec.x=localR.col1.x*hX+localR.col2.x*-hY;tVec.y=localR.col1.y*hX+localR.col2.y*-hY;tVec=this.m_coreVertices[0]=new b2Vec2();tVec.x=localR.col1.x*hcX+localR.col2.x*hcY;tVec.y=localR.col1.y*hcX+localR.col2.y*hcY;tVec=this.m_coreVertices[1]=new b2Vec2();tVec.x=localR.col1.x*-hcX+localR.col2.x*hcY;tVec.y=localR.col1.y*-hcX+localR.col2.y*hcY;tVec=this.m_coreVertices[2]=new b2Vec2();tVec.x=localR.col1.x*-hcX+localR.col2.x*-hcY;tVec.y=localR.col1.y*-hcX+localR.col2.y*-hcY;tVec=this.m_coreVertices[3]=new b2Vec2();tVec.x=localR.col1.x*hcX+localR.col2.x*-hcY;tVec.y=localR.col1.y*hcX+localR.col2.y*-hcY;} -else -{var poly=def;this.m_vertexCount=poly.vertexCount;b2Shape.PolyCentroid(poly.vertices,poly.vertexCount,b2PolyShape.tempVec);var centroidX=b2PolyShape.tempVec.x;var centroidY=b2PolyShape.tempVec.y;this.m_localCentroid.x=def.localPosition.x+(localR.col1.x*centroidX+localR.col2.x*centroidY)-newOrigin.x;this.m_localCentroid.y=def.localPosition.y+(localR.col1.y*centroidX+localR.col2.y*centroidY)-newOrigin.y;for(i=0;i<this.m_vertexCount;++i) -{this.m_vertices[i]=new b2Vec2();this.m_coreVertices[i]=new b2Vec2();hX=poly.vertices[i].x-centroidX;hY=poly.vertices[i].y-centroidY;this.m_vertices[i].x=localR.col1.x*hX+localR.col2.x*hY;this.m_vertices[i].y=localR.col1.y*hX+localR.col2.y*hY;var uX=this.m_vertices[i].x;var uY=this.m_vertices[i].y;var length=Math.sqrt(uX*uX+uY*uY);if(length>Number.MIN_VALUE) -{uX*=1.0/length;uY*=1.0/length;} -this.m_coreVertices[i].x=this.m_vertices[i].x-2.0*b2Settings.b2_linearSlop*uX;this.m_coreVertices[i].y=this.m_vertices[i].y-2.0*b2Settings.b2_linearSlop*uY;}} -var minVertexX=Number.MAX_VALUE;var minVertexY=Number.MAX_VALUE;var maxVertexX=-Number.MAX_VALUE;var maxVertexY=-Number.MAX_VALUE;this.m_maxRadius=0.0;for(i=0;i<this.m_vertexCount;++i) -{var v=this.m_vertices[i];minVertexX=Math.min(minVertexX,v.x);minVertexY=Math.min(minVertexY,v.y);maxVertexX=Math.max(maxVertexX,v.x);maxVertexY=Math.max(maxVertexY,v.y);this.m_maxRadius=Math.max(this.m_maxRadius,v.Length());} -this.m_localOBB.R.SetIdentity();this.m_localOBB.center.Set((minVertexX+maxVertexX)*0.5,(minVertexY+maxVertexY)*0.5);this.m_localOBB.extents.Set((maxVertexX-minVertexX)*0.5,(maxVertexY-minVertexY)*0.5);var i1=0;var i2=0;for(i=0;i<this.m_vertexCount;++i) -{this.m_normals[i]=new b2Vec2();i1=i;i2=i+1<this.m_vertexCount?i+1:0;this.m_normals[i].x=this.m_vertices[i2].y-this.m_vertices[i1].y;this.m_normals[i].y=-(this.m_vertices[i2].x-this.m_vertices[i1].x);this.m_normals[i].Normalize();} -for(i=0;i<this.m_vertexCount;++i) -{i1=i;i2=i+1<this.m_vertexCount?i+1:0;} -this.m_R.SetM(this.m_body.m_R);this.m_position.x=this.m_body.m_position.x+(this.m_R.col1.x*this.m_localCentroid.x+this.m_R.col2.x*this.m_localCentroid.y);this.m_position.y=this.m_body.m_position.y+(this.m_R.col1.y*this.m_localCentroid.x+this.m_R.col2.y*this.m_localCentroid.y);b2PolyShape.tAbsR.col1.x=this.m_R.col1.x*this.m_localOBB.R.col1.x+this.m_R.col2.x*this.m_localOBB.R.col1.y;b2PolyShape.tAbsR.col1.y=this.m_R.col1.y*this.m_localOBB.R.col1.x+this.m_R.col2.y*this.m_localOBB.R.col1.y;b2PolyShape.tAbsR.col2.x=this.m_R.col1.x*this.m_localOBB.R.col2.x+this.m_R.col2.x*this.m_localOBB.R.col2.y;b2PolyShape.tAbsR.col2.y=this.m_R.col1.y*this.m_localOBB.R.col2.x+this.m_R.col2.y*this.m_localOBB.R.col2.y;b2PolyShape.tAbsR.Abs() -hX=b2PolyShape.tAbsR.col1.x*this.m_localOBB.extents.x+b2PolyShape.tAbsR.col2.x*this.m_localOBB.extents.y;hY=b2PolyShape.tAbsR.col1.y*this.m_localOBB.extents.x+b2PolyShape.tAbsR.col2.y*this.m_localOBB.extents.y;var positionX=this.m_position.x+(this.m_R.col1.x*this.m_localOBB.center.x+this.m_R.col2.x*this.m_localOBB.center.y);var positionY=this.m_position.y+(this.m_R.col1.y*this.m_localOBB.center.x+this.m_R.col2.y*this.m_localOBB.center.y);aabb.minVertex.x=positionX-hX;aabb.minVertex.y=positionY-hY;aabb.maxVertex.x=positionX+hX;aabb.maxVertex.y=positionY+hY;var broadPhase=this.m_body.m_world.m_broadPhase;if(broadPhase.InRange(aabb)) -{this.m_proxyId=broadPhase.CreateProxy(aabb,this);} -else -{this.m_proxyId=b2Pair.b2_nullProxy;} -if(this.m_proxyId==b2Pair.b2_nullProxy) -{this.m_body.Freeze();}},syncAABB:new b2AABB(),syncMat:new b2Mat22(),Synchronize:function(position1,R1,position2,R2){this.m_R.SetM(R2);this.m_position.x=this.m_body.m_position.x+(R2.col1.x*this.m_localCentroid.x+R2.col2.x*this.m_localCentroid.y);this.m_position.y=this.m_body.m_position.y+(R2.col1.y*this.m_localCentroid.x+R2.col2.y*this.m_localCentroid.y);if(this.m_proxyId==b2Pair.b2_nullProxy) -{return;} -var hX;var hY;var v1=R1.col1;var v2=R1.col2;var v3=this.m_localOBB.R.col1;var v4=this.m_localOBB.R.col2;this.syncMat.col1.x=v1.x*v3.x+v2.x*v3.y;this.syncMat.col1.y=v1.y*v3.x+v2.y*v3.y;this.syncMat.col2.x=v1.x*v4.x+v2.x*v4.y;this.syncMat.col2.y=v1.y*v4.x+v2.y*v4.y;this.syncMat.Abs();hX=this.m_localCentroid.x+this.m_localOBB.center.x;hY=this.m_localCentroid.y+this.m_localOBB.center.y;var centerX=position1.x+(R1.col1.x*hX+R1.col2.x*hY);var centerY=position1.y+(R1.col1.y*hX+R1.col2.y*hY);hX=this.syncMat.col1.x*this.m_localOBB.extents.x+this.syncMat.col2.x*this.m_localOBB.extents.y;hY=this.syncMat.col1.y*this.m_localOBB.extents.x+this.syncMat.col2.y*this.m_localOBB.extents.y;this.syncAABB.minVertex.x=centerX-hX;this.syncAABB.minVertex.y=centerY-hY;this.syncAABB.maxVertex.x=centerX+hX;this.syncAABB.maxVertex.y=centerY+hY;v1=R2.col1;v2=R2.col2;v3=this.m_localOBB.R.col1;v4=this.m_localOBB.R.col2;this.syncMat.col1.x=v1.x*v3.x+v2.x*v3.y;this.syncMat.col1.y=v1.y*v3.x+v2.y*v3.y;this.syncMat.col2.x=v1.x*v4.x+v2.x*v4.y;this.syncMat.col2.y=v1.y*v4.x+v2.y*v4.y;this.syncMat.Abs();hX=this.m_localCentroid.x+this.m_localOBB.center.x;hY=this.m_localCentroid.y+this.m_localOBB.center.y;centerX=position2.x+(R2.col1.x*hX+R2.col2.x*hY);centerY=position2.y+(R2.col1.y*hX+R2.col2.y*hY);hX=this.syncMat.col1.x*this.m_localOBB.extents.x+this.syncMat.col2.x*this.m_localOBB.extents.y;hY=this.syncMat.col1.y*this.m_localOBB.extents.x+this.syncMat.col2.y*this.m_localOBB.extents.y;this.syncAABB.minVertex.x=Math.min(this.syncAABB.minVertex.x,centerX-hX);this.syncAABB.minVertex.y=Math.min(this.syncAABB.minVertex.y,centerY-hY);this.syncAABB.maxVertex.x=Math.max(this.syncAABB.maxVertex.x,centerX+hX);this.syncAABB.maxVertex.y=Math.max(this.syncAABB.maxVertex.y,centerY+hY);var broadPhase=this.m_body.m_world.m_broadPhase;if(broadPhase.InRange(this.syncAABB)) -{broadPhase.MoveProxy(this.m_proxyId,this.syncAABB);} -else -{this.m_body.Freeze();}},QuickSync:function(position,R){this.m_R.SetM(R);this.m_position.x=position.x+(R.col1.x*this.m_localCentroid.x+R.col2.x*this.m_localCentroid.y);this.m_position.y=position.y+(R.col1.y*this.m_localCentroid.x+R.col2.y*this.m_localCentroid.y);},ResetProxy:function(broadPhase){if(this.m_proxyId==b2Pair.b2_nullProxy) -{return;} -var proxy=broadPhase.GetProxy(this.m_proxyId);broadPhase.DestroyProxy(this.m_proxyId);proxy=null;var R=b2Math.b2MulMM(this.m_R,this.m_localOBB.R);var absR=b2Math.b2AbsM(R);var h=b2Math.b2MulMV(absR,this.m_localOBB.extents);var position=b2Math.b2MulMV(this.m_R,this.m_localOBB.center);position.Add(this.m_position);var aabb=new b2AABB();aabb.minVertex.SetV(position);aabb.minVertex.Subtract(h);aabb.maxVertex.SetV(position);aabb.maxVertex.Add(h);if(broadPhase.InRange(aabb)) -{this.m_proxyId=broadPhase.CreateProxy(aabb,this);} -else -{this.m_proxyId=b2Pair.b2_nullProxy;} -if(this.m_proxyId==b2Pair.b2_nullProxy) -{this.m_body.Freeze();}},Support:function(dX,dY,out) -{var dLocalX=(dX*this.m_R.col1.x+dY*this.m_R.col1.y);var dLocalY=(dX*this.m_R.col2.x+dY*this.m_R.col2.y);var bestIndex=0;var bestValue=(this.m_coreVertices[0].x*dLocalX+this.m_coreVertices[0].y*dLocalY);for(var i=1;i<this.m_vertexCount;++i) -{var value=(this.m_coreVertices[i].x*dLocalX+this.m_coreVertices[i].y*dLocalY);if(value>bestValue) -{bestIndex=i;bestValue=value;}} -out.Set(this.m_position.x+(this.m_R.col1.x*this.m_coreVertices[bestIndex].x+this.m_R.col2.x*this.m_coreVertices[bestIndex].y),this.m_position.y+(this.m_R.col1.y*this.m_coreVertices[bestIndex].x+this.m_R.col2.y*this.m_coreVertices[bestIndex].y));},m_localCentroid:new b2Vec2(),m_localOBB:new b2OBB(),m_vertices:null,m_coreVertices:null,m_vertexCount:0,m_normals:null});b2PolyShape.tempVec=new b2Vec2();b2PolyShape.tAbsR=new b2Mat22(); -var b2Body=Class.create();b2Body.prototype={SetOriginPosition:function(position,rotation){if(this.IsFrozen()) -{return;} -this.m_rotation=rotation;this.m_R.Set(this.m_rotation);this.m_position=b2Math.AddVV(position,b2Math.b2MulMV(this.m_R,this.m_center));this.m_position0.SetV(this.m_position);this.m_rotation0=this.m_rotation;for(var s=this.m_shapeList;s!=null;s=s.m_next) -{s.Synchronize(this.m_position,this.m_R,this.m_position,this.m_R);} -this.m_world.m_broadPhase.Commit();},GetOriginPosition:function(){return b2Math.SubtractVV(this.m_position,b2Math.b2MulMV(this.m_R,this.m_center));},SetCenterPosition:function(position,rotation){if(this.IsFrozen()) -{return;} -this.m_rotation=rotation;this.m_R.Set(this.m_rotation);this.m_position.SetV(position);this.m_position0.SetV(this.m_position);this.m_rotation0=this.m_rotation;for(var s=this.m_shapeList;s!=null;s=s.m_next) -{s.Synchronize(this.m_position,this.m_R,this.m_position,this.m_R);} -this.m_world.m_broadPhase.Commit();},GetCenterPosition:function(){return this.m_position;},GetRotation:function(){return this.m_rotation;},GetRotationMatrix:function(){return this.m_R;},SetLinearVelocity:function(v){this.m_linearVelocity.SetV(v);},GetLinearVelocity:function(){return this.m_linearVelocity;},SetAngularVelocity:function(w){this.m_angularVelocity=w;},GetAngularVelocity:function(){return this.m_angularVelocity;},ApplyForce:function(force,point) -{if(this.IsSleeping()==false) -{this.m_force.Add(force);this.m_torque+=b2Math.b2CrossVV(b2Math.SubtractVV(point,this.m_position),force);}},ApplyTorque:function(torque) -{if(this.IsSleeping()==false) -{this.m_torque+=torque;}},ApplyImpulse:function(impulse,point) -{if(this.IsSleeping()==false) -{this.m_linearVelocity.Add(b2Math.MulFV(this.m_invMass,impulse));this.m_angularVelocity+=(this.m_invI*b2Math.b2CrossVV(b2Math.SubtractVV(point,this.m_position),impulse));}},GetMass:function(){return this.m_mass;},GetInertia:function(){return this.m_I;},GetWorldPoint:function(localPoint){return b2Math.AddVV(this.m_position,b2Math.b2MulMV(this.m_R,localPoint));},GetWorldVector:function(localVector){return b2Math.b2MulMV(this.m_R,localVector);},GetLocalPoint:function(worldPoint){return b2Math.b2MulTMV(this.m_R,b2Math.SubtractVV(worldPoint,this.m_position));},GetLocalVector:function(worldVector){return b2Math.b2MulTMV(this.m_R,worldVector);},IsStatic:function(){return(this.m_flags&b2Body.e_staticFlag)==b2Body.e_staticFlag;},IsFrozen:function() -{return(this.m_flags&b2Body.e_frozenFlag)==b2Body.e_frozenFlag;},IsSleeping:function(){return(this.m_flags&b2Body.e_sleepFlag)==b2Body.e_sleepFlag;},AllowSleeping:function(flag) -{if(flag) -{this.m_flags|=b2Body.e_allowSleepFlag;} -else -{this.m_flags&=~b2Body.e_allowSleepFlag;this.WakeUp();}},WakeUp:function(){this.m_flags&=~b2Body.e_sleepFlag;this.m_sleepTime=0.0;},GetShapeList:function(){return this.m_shapeList;},GetContactList:function() -{return this.m_contactList;},GetJointList:function() -{return this.m_jointList;},GetNext:function(){return this.m_next;},GetUserData:function(){return this.m_userData;},initialize:function(bd,world){this.sMat0=new b2Mat22();this.m_position=new b2Vec2();this.m_R=new b2Mat22(0);this.m_position0=new b2Vec2();var i=0;var sd;var massData;this.m_flags=0;this.m_position.SetV(bd.position);this.m_rotation=bd.rotation;this.m_R.Set(this.m_rotation);this.m_position0.SetV(this.m_position);this.m_rotation0=this.m_rotation;this.m_world=world;this.m_linearDamping=b2Math.b2Clamp(1.0-bd.linearDamping,0.0,1.0);this.m_angularDamping=b2Math.b2Clamp(1.0-bd.angularDamping,0.0,1.0);this.m_force=new b2Vec2(0.0,0.0);this.m_torque=0.0;this.m_mass=0.0;var massDatas=new Array(b2Settings.b2_maxShapesPerBody);for(i=0;i<b2Settings.b2_maxShapesPerBody;i++){massDatas[i]=new b2MassData();} -this.m_shapeCount=0;this.m_center=new b2Vec2(0.0,0.0);for(i=0;i<b2Settings.b2_maxShapesPerBody;++i) -{sd=bd.shapes[i];if(sd==null)break;massData=massDatas[i];sd.ComputeMass(massData);this.m_mass+=massData.mass;this.m_center.x+=massData.mass*(sd.localPosition.x+massData.center.x);this.m_center.y+=massData.mass*(sd.localPosition.y+massData.center.y);++this.m_shapeCount;} -if(this.m_mass>0.0) -{this.m_center.Multiply(1.0/this.m_mass);this.m_position.Add(b2Math.b2MulMV(this.m_R,this.m_center));} -else -{this.m_flags|=b2Body.e_staticFlag;} -this.m_I=0.0;for(i=0;i<this.m_shapeCount;++i) -{sd=bd.shapes[i];massData=massDatas[i];this.m_I+=massData.I;var r=b2Math.SubtractVV(b2Math.AddVV(sd.localPosition,massData.center),this.m_center);this.m_I+=massData.mass*b2Math.b2Dot(r,r);} -if(this.m_mass>0.0) -{this.m_invMass=1.0/this.m_mass;} -else -{this.m_invMass=0.0;} -if(this.m_I>0.0&&bd.preventRotation==false) -{this.m_invI=1.0/this.m_I;} -else -{this.m_I=0.0;this.m_invI=0.0;} -this.m_linearVelocity=b2Math.AddVV(bd.linearVelocity,b2Math.b2CrossFV(bd.angularVelocity,this.m_center));this.m_angularVelocity=bd.angularVelocity;this.m_jointList=null;this.m_contactList=null;this.m_prev=null;this.m_next=null;this.m_shapeList=null;for(i=0;i<this.m_shapeCount;++i) -{sd=bd.shapes[i];var shape=b2Shape.Create(sd,this,this.m_center);shape.m_next=this.m_shapeList;this.m_shapeList=shape;} -this.m_sleepTime=0.0;if(bd.allowSleep) -{this.m_flags|=b2Body.e_allowSleepFlag;} -if(bd.isSleeping) -{this.m_flags|=b2Body.e_sleepFlag;} -if((this.m_flags&b2Body.e_sleepFlag)||this.m_invMass==0.0) -{this.m_linearVelocity.Set(0.0,0.0);this.m_angularVelocity=0.0;} -this.m_userData=bd.userData;},Destroy:function(){var s=this.m_shapeList;while(s) -{var s0=s;s=s.m_next;b2Shape.Destroy(s0);}},sMat0:new b2Mat22(),SynchronizeShapes:function(){this.sMat0.Set(this.m_rotation0);for(var s=this.m_shapeList;s!=null;s=s.m_next) -{s.Synchronize(this.m_position0,this.sMat0,this.m_position,this.m_R);}},QuickSyncShapes:function(){for(var s=this.m_shapeList;s!=null;s=s.m_next) -{s.QuickSync(this.m_position,this.m_R);}},IsConnected:function(other){for(var jn=this.m_jointList;jn!=null;jn=jn.next) -{if(jn.other==other) -return jn.joint.m_collideConnected==false;} -return false;},Freeze:function(){this.m_flags|=b2Body.e_frozenFlag;this.m_linearVelocity.SetZero();this.m_angularVelocity=0.0;for(var s=this.m_shapeList;s!=null;s=s.m_next) -{s.DestroyProxy();}},m_flags:0,m_position:new b2Vec2(),m_rotation:null,m_R:new b2Mat22(0),m_position0:new b2Vec2(),m_rotation0:null,m_linearVelocity:null,m_angularVelocity:null,m_force:null,m_torque:null,m_center:null,m_world:null,m_prev:null,m_next:null,m_shapeList:null,m_shapeCount:0,m_jointList:null,m_contactList:null,m_mass:null,m_invMass:null,m_I:null,m_invI:null,m_linearDamping:null,m_angularDamping:null,m_sleepTime:null,m_userData:null};b2Body.e_staticFlag=0x0001;b2Body.e_frozenFlag=0x0002;b2Body.e_islandFlag=0x0004;b2Body.e_sleepFlag=0x0008;b2Body.e_allowSleepFlag=0x0010;b2Body.e_destroyFlag=0x0020; -var b2BodyDef=Class.create();b2BodyDef.prototype={initialize:function() -{this.shapes=new Array();this.userData=null;for(var i=0;i<b2Settings.b2_maxShapesPerBody;i++){this.shapes[i]=null;} -this.position=new b2Vec2(0.0,0.0);this.rotation=0.0;this.linearVelocity=new b2Vec2(0.0,0.0);this.angularVelocity=0.0;this.linearDamping=0.0;this.angularDamping=0.0;this.allowSleep=true;this.isSleeping=false;this.preventRotation=false;},userData:null,shapes:new Array(),position:null,rotation:null,linearVelocity:null,angularVelocity:null,linearDamping:null,angularDamping:null,allowSleep:null,isSleeping:null,preventRotation:null,AddShape:function(shape) -{for(var i=0;i<b2Settings.b2_maxShapesPerBody;++i) -{if(this.shapes[i]==null) -{this.shapes[i]=shape;break;}}}}; -var b2CollisionFilter=Class.create();b2CollisionFilter.prototype={ShouldCollide:function(shape1,shape2){if(shape1.m_groupIndex==shape2.m_groupIndex&&shape1.m_groupIndex!=0) -{return shape1.m_groupIndex>0;} -var collide=(shape1.m_maskBits&shape2.m_categoryBits)!=0&&(shape1.m_categoryBits&shape2.m_maskBits)!=0;return collide;},initialize:function(){}};b2CollisionFilter.b2_defaultFilter=new b2CollisionFilter; -var b2Island=Class.create();b2Island.prototype={initialize:function(bodyCapacity,contactCapacity,jointCapacity,allocator) -{var i=0;this.m_bodyCapacity=bodyCapacity;this.m_contactCapacity=contactCapacity;this.m_jointCapacity=jointCapacity;this.m_bodyCount=0;this.m_contactCount=0;this.m_jointCount=0;this.m_bodies=new Array(bodyCapacity);for(i=0;i<bodyCapacity;i++) -this.m_bodies[i]=null;this.m_contacts=new Array(contactCapacity);for(i=0;i<contactCapacity;i++) -this.m_contacts[i]=null;this.m_joints=new Array(jointCapacity);for(i=0;i<jointCapacity;i++) -this.m_joints[i]=null;this.m_allocator=allocator;},Clear:function() -{this.m_bodyCount=0;this.m_contactCount=0;this.m_jointCount=0;},Solve:function(step,gravity) -{var i=0;var b;for(i=0;i<this.m_bodyCount;++i) -{b=this.m_bodies[i];if(b.m_invMass==0.0) -continue;b.m_linearVelocity.Add(b2Math.MulFV(step.dt,b2Math.AddVV(gravity,b2Math.MulFV(b.m_invMass,b.m_force))));b.m_angularVelocity+=step.dt*b.m_invI*b.m_torque;b.m_linearVelocity.Multiply(b.m_linearDamping);b.m_angularVelocity*=b.m_angularDamping;b.m_position0.SetV(b.m_position);b.m_rotation0=b.m_rotation;} -var contactSolver=new b2ContactSolver(this.m_contacts,this.m_contactCount,this.m_allocator);contactSolver.PreSolve();for(i=0;i<this.m_jointCount;++i) -{this.m_joints[i].PrepareVelocitySolver();} -for(i=0;i<step.iterations;++i) -{contactSolver.SolveVelocityConstraints();for(var j=0;j<this.m_jointCount;++j) -{this.m_joints[j].SolveVelocityConstraints(step);}} -for(i=0;i<this.m_bodyCount;++i) -{b=this.m_bodies[i];if(b.m_invMass==0.0) -continue;b.m_position.x+=step.dt*b.m_linearVelocity.x;b.m_position.y+=step.dt*b.m_linearVelocity.y;b.m_rotation+=step.dt*b.m_angularVelocity;b.m_R.Set(b.m_rotation);} -for(i=0;i<this.m_jointCount;++i) -{this.m_joints[i].PreparePositionSolver();} -if(b2World.s_enablePositionCorrection) -{for(b2Island.m_positionIterationCount=0;b2Island.m_positionIterationCount<step.iterations;++b2Island.m_positionIterationCount) -{var contactsOkay=contactSolver.SolvePositionConstraints(b2Settings.b2_contactBaumgarte);var jointsOkay=true;for(i=0;i<this.m_jointCount;++i) -{var jointOkay=this.m_joints[i].SolvePositionConstraints();jointsOkay=jointsOkay&&jointOkay;} -if(contactsOkay&&jointsOkay) -{break;}}} -contactSolver.PostSolve();for(i=0;i<this.m_bodyCount;++i) -{b=this.m_bodies[i];if(b.m_invMass==0.0) -continue;b.m_R.Set(b.m_rotation);b.SynchronizeShapes();b.m_force.Set(0.0,0.0);b.m_torque=0.0;}},UpdateSleep:function(dt) -{var i=0;var b;var minSleepTime=Number.MAX_VALUE;var linTolSqr=b2Settings.b2_linearSleepTolerance*b2Settings.b2_linearSleepTolerance;var angTolSqr=b2Settings.b2_angularSleepTolerance*b2Settings.b2_angularSleepTolerance;for(i=0;i<this.m_bodyCount;++i) -{b=this.m_bodies[i];if(b.m_invMass==0.0) -{continue;} -if((b.m_flags&b2Body.e_allowSleepFlag)==0) -{b.m_sleepTime=0.0;minSleepTime=0.0;} -if((b.m_flags&b2Body.e_allowSleepFlag)==0||b.m_angularVelocity*b.m_angularVelocity>angTolSqr||b2Math.b2Dot(b.m_linearVelocity,b.m_linearVelocity)>linTolSqr) -{b.m_sleepTime=0.0;minSleepTime=0.0;} -else -{b.m_sleepTime+=dt;minSleepTime=b2Math.b2Min(minSleepTime,b.m_sleepTime);}} -if(minSleepTime>=b2Settings.b2_timeToSleep) -{for(i=0;i<this.m_bodyCount;++i) -{b=this.m_bodies[i];b.m_flags|=b2Body.e_sleepFlag;}}},AddBody:function(body) -{this.m_bodies[this.m_bodyCount++]=body;},AddContact:function(contact) -{this.m_contacts[this.m_contactCount++]=contact;},AddJoint:function(joint) -{this.m_joints[this.m_jointCount++]=joint;},m_allocator:null,m_bodies:null,m_contacts:null,m_joints:null,m_bodyCount:0,m_jointCount:0,m_contactCount:0,m_bodyCapacity:0,m_contactCapacity:0,m_jointCapacity:0,m_positionError:null};b2Island.m_positionIterationCount=0; -var b2TimeStep=Class.create();b2TimeStep.prototype={dt:null,inv_dt:null,iterations:0,initialize:function(){}}; -var b2ContactNode=Class.create();b2ContactNode.prototype={other:null,contact:null,prev:null,next:null,initialize:function(){}}; -var b2Contact=Class.create();b2Contact.prototype={GetManifolds:function(){return null},GetManifoldCount:function() -{return this.m_manifoldCount;},GetNext:function(){return this.m_next;},GetShape1:function(){return this.m_shape1;},GetShape2:function(){return this.m_shape2;},initialize:function(s1,s2) -{this.m_node1=new b2ContactNode();this.m_node2=new b2ContactNode();this.m_flags=0;if(!s1||!s2){this.m_shape1=null;this.m_shape2=null;return;} -this.m_shape1=s1;this.m_shape2=s2;this.m_manifoldCount=0;this.m_friction=Math.sqrt(this.m_shape1.m_friction*this.m_shape2.m_friction);this.m_restitution=b2Math.b2Max(this.m_shape1.m_restitution,this.m_shape2.m_restitution);this.m_prev=null;this.m_next=null;this.m_node1.contact=null;this.m_node1.prev=null;this.m_node1.next=null;this.m_node1.other=null;this.m_node2.contact=null;this.m_node2.prev=null;this.m_node2.next=null;this.m_node2.other=null;},Evaluate:function(){},m_flags:0,m_prev:null,m_next:null,m_node1:new b2ContactNode(),m_node2:new b2ContactNode(),m_shape1:null,m_shape2:null,m_manifoldCount:0,m_friction:null,m_restitution:null};b2Contact.e_islandFlag=0x0001;b2Contact.e_destroyFlag=0x0002;b2Contact.AddType=function(createFcn,destroyFcn,type1,type2) -{b2Contact.s_registers[type1][type2].createFcn=createFcn;b2Contact.s_registers[type1][type2].destroyFcn=destroyFcn;b2Contact.s_registers[type1][type2].primary=true;if(type1!=type2) -{b2Contact.s_registers[type2][type1].createFcn=createFcn;b2Contact.s_registers[type2][type1].destroyFcn=destroyFcn;b2Contact.s_registers[type2][type1].primary=false;}};b2Contact.InitializeRegisters=function(){b2Contact.s_registers=new Array(b2Shape.e_shapeTypeCount);for(var i=0;i<b2Shape.e_shapeTypeCount;i++){b2Contact.s_registers[i]=new Array(b2Shape.e_shapeTypeCount);for(var j=0;j<b2Shape.e_shapeTypeCount;j++){b2Contact.s_registers[i][j]=new b2ContactRegister();}} -b2Contact.AddType(b2CircleContact.Create,b2CircleContact.Destroy,b2Shape.e_circleShape,b2Shape.e_circleShape);b2Contact.AddType(b2PolyAndCircleContact.Create,b2PolyAndCircleContact.Destroy,b2Shape.e_polyShape,b2Shape.e_circleShape);b2Contact.AddType(b2PolyContact.Create,b2PolyContact.Destroy,b2Shape.e_polyShape,b2Shape.e_polyShape);};b2Contact.Create=function(shape1,shape2,allocator){if(b2Contact.s_initialized==false) -{b2Contact.InitializeRegisters();b2Contact.s_initialized=true;} -var type1=shape1.m_type;var type2=shape2.m_type;var createFcn=b2Contact.s_registers[type1][type2].createFcn;if(createFcn) -{if(b2Contact.s_registers[type1][type2].primary) -{return createFcn(shape1,shape2,allocator);} -else -{var c=createFcn(shape2,shape1,allocator);for(var i=0;i<c.GetManifoldCount();++i) -{var m=c.GetManifolds()[i];m.normal=m.normal.Negative();} -return c;}} -else -{return null;}};b2Contact.Destroy=function(contact,allocator){if(contact.GetManifoldCount()>0) -{contact.m_shape1.m_body.WakeUp();contact.m_shape2.m_body.WakeUp();} -var type1=contact.m_shape1.m_type;var type2=contact.m_shape2.m_type;var destroyFcn=b2Contact.s_registers[type1][type2].destroyFcn;destroyFcn(contact,allocator);};b2Contact.s_registers=null;b2Contact.s_initialized=false; -var b2ContactConstraint=Class.create();b2ContactConstraint.prototype={initialize:function(){this.normal=new b2Vec2();this.points=new Array(b2Settings.b2_maxManifoldPoints);for(var i=0;i<b2Settings.b2_maxManifoldPoints;i++){this.points[i]=new b2ContactConstraintPoint();}},points:null,normal:new b2Vec2(),manifold:null,body1:null,body2:null,friction:null,restitution:null,pointCount:0}; -var b2ContactConstraintPoint=Class.create();b2ContactConstraintPoint.prototype={localAnchor1:new b2Vec2(),localAnchor2:new b2Vec2(),normalImpulse:null,tangentImpulse:null,positionImpulse:null,normalMass:null,tangentMass:null,separation:null,velocityBias:null,initialize:function(){this.localAnchor1=new b2Vec2();this.localAnchor2=new b2Vec2();}}; -var b2ContactRegister=Class.create();b2ContactRegister.prototype={createFcn:null,destroyFcn:null,primary:null,initialize:function(){}}; -var b2ContactSolver=Class.create();b2ContactSolver.prototype={initialize:function(contacts,contactCount,allocator){this.m_constraints=new Array();this.m_allocator=allocator;var i=0;var tVec;var tMat;this.m_constraintCount=0;for(i=0;i<contactCount;++i) -{this.m_constraintCount+=contacts[i].GetManifoldCount();} -for(i=0;i<this.m_constraintCount;i++){this.m_constraints[i]=new b2ContactConstraint();} -var count=0;for(i=0;i<contactCount;++i) -{var contact=contacts[i];var b1=contact.m_shape1.m_body;var b2=contact.m_shape2.m_body;var manifoldCount=contact.GetManifoldCount();var manifolds=contact.GetManifolds();var friction=contact.m_friction;var restitution=contact.m_restitution;var v1X=b1.m_linearVelocity.x;var v1Y=b1.m_linearVelocity.y;var v2X=b2.m_linearVelocity.x;var v2Y=b2.m_linearVelocity.y;var w1=b1.m_angularVelocity;var w2=b2.m_angularVelocity;for(var j=0;j<manifoldCount;++j) -{var manifold=manifolds[j];var normalX=manifold.normal.x;var normalY=manifold.normal.y;var c=this.m_constraints[count];c.body1=b1;c.body2=b2;c.manifold=manifold;c.normal.x=normalX;c.normal.y=normalY;c.pointCount=manifold.pointCount;c.friction=friction;c.restitution=restitution;for(var k=0;k<c.pointCount;++k) -{var cp=manifold.points[k];var ccp=c.points[k];ccp.normalImpulse=cp.normalImpulse;ccp.tangentImpulse=cp.tangentImpulse;ccp.separation=cp.separation;var r1X=cp.position.x-b1.m_position.x;var r1Y=cp.position.y-b1.m_position.y;var r2X=cp.position.x-b2.m_position.x;var r2Y=cp.position.y-b2.m_position.y;tVec=ccp.localAnchor1;tMat=b1.m_R;tVec.x=r1X*tMat.col1.x+r1Y*tMat.col1.y;tVec.y=r1X*tMat.col2.x+r1Y*tMat.col2.y;tVec=ccp.localAnchor2;tMat=b2.m_R;tVec.x=r2X*tMat.col1.x+r2Y*tMat.col1.y;tVec.y=r2X*tMat.col2.x+r2Y*tMat.col2.y;var r1Sqr=r1X*r1X+r1Y*r1Y;var r2Sqr=r2X*r2X+r2Y*r2Y;var rn1=r1X*normalX+r1Y*normalY;var rn2=r2X*normalX+r2Y*normalY;var kNormal=b1.m_invMass+b2.m_invMass;kNormal+=b1.m_invI*(r1Sqr-rn1*rn1)+b2.m_invI*(r2Sqr-rn2*rn2);ccp.normalMass=1.0/kNormal;var tangentX=normalY -var tangentY=-normalX;var rt1=r1X*tangentX+r1Y*tangentY;var rt2=r2X*tangentX+r2Y*tangentY;var kTangent=b1.m_invMass+b2.m_invMass;kTangent+=b1.m_invI*(r1Sqr-rt1*rt1)+b2.m_invI*(r2Sqr-rt2*rt2);ccp.tangentMass=1.0/kTangent;ccp.velocityBias=0.0;if(ccp.separation>0.0) -{ccp.velocityBias=-60.0*ccp.separation;} -var tX=v2X+(-w2*r2Y)-v1X-(-w1*r1Y);var tY=v2Y+(w2*r2X)-v1Y-(w1*r1X);var vRel=c.normal.x*tX+c.normal.y*tY;if(vRel<-b2Settings.b2_velocityThreshold) -{ccp.velocityBias+=-c.restitution*vRel;}} -++count;}}},PreSolve:function(){var tVec;var tVec2;var tMat;for(var i=0;i<this.m_constraintCount;++i) -{var c=this.m_constraints[i];var b1=c.body1;var b2=c.body2;var invMass1=b1.m_invMass;var invI1=b1.m_invI;var invMass2=b2.m_invMass;var invI2=b2.m_invI;var normalX=c.normal.x;var normalY=c.normal.y;var tangentX=normalY;var tangentY=-normalX;var j=0;var tCount=0;if(b2World.s_enableWarmStarting) -{tCount=c.pointCount;for(j=0;j<tCount;++j) -{var ccp=c.points[j];var PX=ccp.normalImpulse*normalX+ccp.tangentImpulse*tangentX;var PY=ccp.normalImpulse*normalY+ccp.tangentImpulse*tangentY;tMat=b1.m_R;tVec=ccp.localAnchor1;var r1X=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y;var r1Y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y;tMat=b2.m_R;tVec=ccp.localAnchor2;var r2X=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y;var r2Y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y;b1.m_angularVelocity-=invI1*(r1X*PY-r1Y*PX);b1.m_linearVelocity.x-=invMass1*PX;b1.m_linearVelocity.y-=invMass1*PY;b2.m_angularVelocity+=invI2*(r2X*PY-r2Y*PX);b2.m_linearVelocity.x+=invMass2*PX;b2.m_linearVelocity.y+=invMass2*PY;ccp.positionImpulse=0.0;}} -else{tCount=c.pointCount;for(j=0;j<tCount;++j) -{var ccp2=c.points[j];ccp2.normalImpulse=0.0;ccp2.tangentImpulse=0.0;ccp2.positionImpulse=0.0;}}}},SolveVelocityConstraints:function(){var j=0;var ccp;var r1X;var r1Y;var r2X;var r2Y;var dvX;var dvY;var lambda;var newImpulse;var PX;var PY;var tMat;var tVec;for(var i=0;i<this.m_constraintCount;++i) -{var c=this.m_constraints[i];var b1=c.body1;var b2=c.body2;var b1_angularVelocity=b1.m_angularVelocity;var b1_linearVelocity=b1.m_linearVelocity;var b2_angularVelocity=b2.m_angularVelocity;var b2_linearVelocity=b2.m_linearVelocity;var invMass1=b1.m_invMass;var invI1=b1.m_invI;var invMass2=b2.m_invMass;var invI2=b2.m_invI;var normalX=c.normal.x;var normalY=c.normal.y;var tangentX=normalY;var tangentY=-normalX;var tCount=c.pointCount;for(j=0;j<tCount;++j) -{ccp=c.points[j];tMat=b1.m_R;tVec=ccp.localAnchor1;r1X=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y -r1Y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y -tMat=b2.m_R;tVec=ccp.localAnchor2;r2X=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y -r2Y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y -dvX=b2_linearVelocity.x+(-b2_angularVelocity*r2Y)-b1_linearVelocity.x-(-b1_angularVelocity*r1Y);dvY=b2_linearVelocity.y+(b2_angularVelocity*r2X)-b1_linearVelocity.y-(b1_angularVelocity*r1X);var vn=dvX*normalX+dvY*normalY;lambda=-ccp.normalMass*(vn-ccp.velocityBias);newImpulse=b2Math.b2Max(ccp.normalImpulse+lambda,0.0);lambda=newImpulse-ccp.normalImpulse;PX=lambda*normalX;PY=lambda*normalY;b1_linearVelocity.x-=invMass1*PX;b1_linearVelocity.y-=invMass1*PY;b1_angularVelocity-=invI1*(r1X*PY-r1Y*PX);b2_linearVelocity.x+=invMass2*PX;b2_linearVelocity.y+=invMass2*PY;b2_angularVelocity+=invI2*(r2X*PY-r2Y*PX);ccp.normalImpulse=newImpulse;dvX=b2_linearVelocity.x+(-b2_angularVelocity*r2Y)-b1_linearVelocity.x-(-b1_angularVelocity*r1Y);dvY=b2_linearVelocity.y+(b2_angularVelocity*r2X)-b1_linearVelocity.y-(b1_angularVelocity*r1X);var vt=dvX*tangentX+dvY*tangentY;lambda=ccp.tangentMass*(-vt);var maxFriction=c.friction*ccp.normalImpulse;newImpulse=b2Math.b2Clamp(ccp.tangentImpulse+lambda,-maxFriction,maxFriction);lambda=newImpulse-ccp.tangentImpulse;PX=lambda*tangentX;PY=lambda*tangentY;b1_linearVelocity.x-=invMass1*PX;b1_linearVelocity.y-=invMass1*PY;b1_angularVelocity-=invI1*(r1X*PY-r1Y*PX);b2_linearVelocity.x+=invMass2*PX;b2_linearVelocity.y+=invMass2*PY;b2_angularVelocity+=invI2*(r2X*PY-r2Y*PX);ccp.tangentImpulse=newImpulse;} -b1.m_angularVelocity=b1_angularVelocity;b2.m_angularVelocity=b2_angularVelocity;}},SolvePositionConstraints:function(beta){var minSeparation=0.0;var tMat;var tVec;for(var i=0;i<this.m_constraintCount;++i) -{var c=this.m_constraints[i];var b1=c.body1;var b2=c.body2;var b1_position=b1.m_position;var b1_rotation=b1.m_rotation;var b2_position=b2.m_position;var b2_rotation=b2.m_rotation;var invMass1=b1.m_invMass;var invI1=b1.m_invI;var invMass2=b2.m_invMass;var invI2=b2.m_invI;var normalX=c.normal.x;var normalY=c.normal.y;var tangentX=normalY;var tangentY=-normalX;var tCount=c.pointCount;for(var j=0;j<tCount;++j) -{var ccp=c.points[j];tMat=b1.m_R;tVec=ccp.localAnchor1;var r1X=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y -var r1Y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y -tMat=b2.m_R;tVec=ccp.localAnchor2;var r2X=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y -var r2Y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y -var p1X=b1_position.x+r1X;var p1Y=b1_position.y+r1Y;var p2X=b2_position.x+r2X;var p2Y=b2_position.y+r2Y;var dpX=p2X-p1X;var dpY=p2Y-p1Y;var separation=(dpX*normalX+dpY*normalY)+ccp.separation;minSeparation=b2Math.b2Min(minSeparation,separation);var C=beta*b2Math.b2Clamp(separation+b2Settings.b2_linearSlop,-b2Settings.b2_maxLinearCorrection,0.0);var dImpulse=-ccp.normalMass*C;var impulse0=ccp.positionImpulse;ccp.positionImpulse=b2Math.b2Max(impulse0+dImpulse,0.0);dImpulse=ccp.positionImpulse-impulse0;var impulseX=dImpulse*normalX;var impulseY=dImpulse*normalY;b1_position.x-=invMass1*impulseX;b1_position.y-=invMass1*impulseY;b1_rotation-=invI1*(r1X*impulseY-r1Y*impulseX);b1.m_R.Set(b1_rotation);b2_position.x+=invMass2*impulseX;b2_position.y+=invMass2*impulseY;b2_rotation+=invI2*(r2X*impulseY-r2Y*impulseX);b2.m_R.Set(b2_rotation);} -b1.m_rotation=b1_rotation;b2.m_rotation=b2_rotation;} -return minSeparation>=-b2Settings.b2_linearSlop;},PostSolve:function(){for(var i=0;i<this.m_constraintCount;++i) -{var c=this.m_constraints[i];var m=c.manifold;for(var j=0;j<c.pointCount;++j) -{var mPoint=m.points[j];var cPoint=c.points[j];mPoint.normalImpulse=cPoint.normalImpulse;mPoint.tangentImpulse=cPoint.tangentImpulse;}}},m_allocator:null,m_constraints:new Array(),m_constraintCount:0}; -var b2CircleContact=Class.create();Object.extend(b2CircleContact.prototype,b2Contact.prototype);Object.extend(b2CircleContact.prototype,{initialize:function(s1,s2){this.m_node1=new b2ContactNode();this.m_node2=new b2ContactNode();this.m_flags=0;if(!s1||!s2){this.m_shape1=null;this.m_shape2=null;return;} -this.m_shape1=s1;this.m_shape2=s2;this.m_manifoldCount=0;this.m_friction=Math.sqrt(this.m_shape1.m_friction*this.m_shape2.m_friction);this.m_restitution=b2Math.b2Max(this.m_shape1.m_restitution,this.m_shape2.m_restitution);this.m_prev=null;this.m_next=null;this.m_node1.contact=null;this.m_node1.prev=null;this.m_node1.next=null;this.m_node1.other=null;this.m_node2.contact=null;this.m_node2.prev=null;this.m_node2.next=null;this.m_node2.other=null;this.m_manifold=[new b2Manifold()];this.m_manifold[0].pointCount=0;this.m_manifold[0].points[0].normalImpulse=0.0;this.m_manifold[0].points[0].tangentImpulse=0.0;},Evaluate:function(){b2Collision.b2CollideCircle(this.m_manifold[0],this.m_shape1,this.m_shape2,false);if(this.m_manifold[0].pointCount>0) -{this.m_manifoldCount=1;} -else -{this.m_manifoldCount=0;}},GetManifolds:function() -{return this.m_manifold;},m_manifold:[new b2Manifold()]});b2CircleContact.Create=function(shape1,shape2,allocator){return new b2CircleContact(shape1,shape2);};b2CircleContact.Destroy=function(contact,allocator){}; -var b2Conservative=Class.create();b2Conservative.prototype={initialize:function(){}} -b2Conservative.R1=new b2Mat22();b2Conservative.R2=new b2Mat22();b2Conservative.x1=new b2Vec2();b2Conservative.x2=new b2Vec2();b2Conservative.Conservative=function(shape1,shape2){var body1=shape1.GetBody();var body2=shape2.GetBody();var v1X=body1.m_position.x-body1.m_position0.x;var v1Y=body1.m_position.y-body1.m_position0.y;var omega1=body1.m_rotation-body1.m_rotation0;var v2X=body2.m_position.x-body2.m_position0.x;var v2Y=body2.m_position.y-body2.m_position0.y;var omega2=body2.m_rotation-body2.m_rotation0;var r1=shape1.GetMaxRadius();var r2=shape2.GetMaxRadius();var p1StartX=body1.m_position0.x;var p1StartY=body1.m_position0.y;var a1Start=body1.m_rotation0;var p2StartX=body2.m_position0.x;var p2StartY=body2.m_position0.y;var a2Start=body2.m_rotation0;var p1X=p1StartX;var p1Y=p1StartY;var a1=a1Start;var p2X=p2StartX;var p2Y=p2StartY;var a2=a2Start;b2Conservative.R1.Set(a1);b2Conservative.R2.Set(a2);shape1.QuickSync(p1,b2Conservative.R1);shape2.QuickSync(p2,b2Conservative.R2);var s1=0.0;var maxIterations=10;var dX;var dY;var invRelativeVelocity=0.0;var hit=true;for(var iter=0;iter<maxIterations;++iter) -{var distance=b2Distance.Distance(b2Conservative.x1,b2Conservative.x2,shape1,shape2);if(distance<b2Settings.b2_linearSlop) -{if(iter==0) -{hit=false;} -else -{hit=true;} -break;} -if(iter==0) -{dX=b2Conservative.x2.x-b2Conservative.x1.x;dY=b2Conservative.x2.y-b2Conservative.x1.y;var dLen=Math.sqrt(dX*dX+dY*dY);var relativeVelocity=(dX*(v1X-v2X)+dY*(v1Y-v2Y))+Math.abs(omega1)*r1+Math.abs(omega2)*r2;if(Math.abs(relativeVelocity)<Number.MIN_VALUE) -{hit=false;break;} -invRelativeVelocity=1.0/relativeVelocity;} -var ds=distance*invRelativeVelocity;var s2=s1+ds;if(s2<0.0||1.0<s2) -{hit=false;break;} -if(s2<(1.0+100.0*Number.MIN_VALUE)*s1) -{hit=true;break;} -s1=s2;p1X=p1StartX+s1*v1.x;p1Y=p1StartY+s1*v1.y;a1=a1Start+s1*omega1;p2X=p2StartX+s1*v2.x;p2Y=p2StartY+s1*v2.y;a2=a2Start+s1*omega2;b2Conservative.R1.Set(a1);b2Conservative.R2.Set(a2);shape1.QuickSync(p1,b2Conservative.R1);shape2.QuickSync(p2,b2Conservative.R2);} -if(hit) -{dX=b2Conservative.x2.x-b2Conservative.x1.x;dY=b2Conservative.x2.y-b2Conservative.x1.y;var length=Math.sqrt(dX*dX+dY*dY);if(length>FLT_EPSILON) -{d*=b2_linearSlop/length;} -if(body1.IsStatic()) -{body1.m_position.x=p1X;body1.m_position.y=p1Y;} -else -{body1.m_position.x=p1X-dX;body1.m_position.y=p1Y-dY;} -body1.m_rotation=a1;body1.m_R.Set(a1);body1.QuickSyncShapes();if(body2.IsStatic()) -{body2.m_position.x=p2X;body2.m_position.y=p2Y;} -else -{body2.m_position.x=p2X+dX;body2.m_position.y=p2Y+dY;} -body2.m_position.x=p2X+dX;body2.m_position.y=p2Y+dY;body2.m_rotation=a2;body2.m_R.Set(a2);body2.QuickSyncShapes();return true;} -shape1.QuickSync(body1.m_position,body1.m_R);shape2.QuickSync(body2.m_position,body2.m_R);return false;}; -var b2NullContact=Class.create();Object.extend(b2NullContact.prototype,b2Contact.prototype);Object.extend(b2NullContact.prototype,{initialize:function(s1,s2){this.m_node1=new b2ContactNode();this.m_node2=new b2ContactNode();this.m_flags=0;if(!s1||!s2){this.m_shape1=null;this.m_shape2=null;return;} -this.m_shape1=s1;this.m_shape2=s2;this.m_manifoldCount=0;this.m_friction=Math.sqrt(this.m_shape1.m_friction*this.m_shape2.m_friction);this.m_restitution=b2Math.b2Max(this.m_shape1.m_restitution,this.m_shape2.m_restitution);this.m_prev=null;this.m_next=null;this.m_node1.contact=null;this.m_node1.prev=null;this.m_node1.next=null;this.m_node1.other=null;this.m_node2.contact=null;this.m_node2.prev=null;this.m_node2.next=null;this.m_node2.other=null;},Evaluate:function(){},GetManifolds:function(){return null;}}); -var b2PolyAndCircleContact=Class.create();Object.extend(b2PolyAndCircleContact.prototype,b2Contact.prototype);Object.extend(b2PolyAndCircleContact.prototype,{initialize:function(s1,s2){this.m_node1=new b2ContactNode();this.m_node2=new b2ContactNode();this.m_flags=0;if(!s1||!s2){this.m_shape1=null;this.m_shape2=null;return;} -this.m_shape1=s1;this.m_shape2=s2;this.m_manifoldCount=0;this.m_friction=Math.sqrt(this.m_shape1.m_friction*this.m_shape2.m_friction);this.m_restitution=b2Math.b2Max(this.m_shape1.m_restitution,this.m_shape2.m_restitution);this.m_prev=null;this.m_next=null;this.m_node1.contact=null;this.m_node1.prev=null;this.m_node1.next=null;this.m_node1.other=null;this.m_node2.contact=null;this.m_node2.prev=null;this.m_node2.next=null;this.m_node2.other=null;this.m_manifold=[new b2Manifold()];b2Settings.b2Assert(this.m_shape1.m_type==b2Shape.e_polyShape);b2Settings.b2Assert(this.m_shape2.m_type==b2Shape.e_circleShape);this.m_manifold[0].pointCount=0;this.m_manifold[0].points[0].normalImpulse=0.0;this.m_manifold[0].points[0].tangentImpulse=0.0;},Evaluate:function(){b2Collision.b2CollidePolyAndCircle(this.m_manifold[0],this.m_shape1,this.m_shape2,false);if(this.m_manifold[0].pointCount>0) -{this.m_manifoldCount=1;} -else -{this.m_manifoldCount=0;}},GetManifolds:function() -{return this.m_manifold;},m_manifold:[new b2Manifold()]}) -b2PolyAndCircleContact.Create=function(shape1,shape2,allocator){return new b2PolyAndCircleContact(shape1,shape2);};b2PolyAndCircleContact.Destroy=function(contact,allocator){}; -var b2PolyContact=Class.create();Object.extend(b2PolyContact.prototype,b2Contact.prototype);Object.extend(b2PolyContact.prototype,{initialize:function(s1,s2){this.m_node1=new b2ContactNode();this.m_node2=new b2ContactNode();this.m_flags=0;if(!s1||!s2){this.m_shape1=null;this.m_shape2=null;return;} -this.m_shape1=s1;this.m_shape2=s2;this.m_manifoldCount=0;this.m_friction=Math.sqrt(this.m_shape1.m_friction*this.m_shape2.m_friction);this.m_restitution=b2Math.b2Max(this.m_shape1.m_restitution,this.m_shape2.m_restitution);this.m_prev=null;this.m_next=null;this.m_node1.contact=null;this.m_node1.prev=null;this.m_node1.next=null;this.m_node1.other=null;this.m_node2.contact=null;this.m_node2.prev=null;this.m_node2.next=null;this.m_node2.other=null;this.m0=new b2Manifold();this.m_manifold=[new b2Manifold()];this.m_manifold[0].pointCount=0;},m0:new b2Manifold(),Evaluate:function(){var tMani=this.m_manifold[0];var tPoints=this.m0.points;for(var k=0;k<tMani.pointCount;k++){var tPoint=tPoints[k];var tPoint0=tMani.points[k];tPoint.normalImpulse=tPoint0.normalImpulse;tPoint.tangentImpulse=tPoint0.tangentImpulse;tPoint.id=tPoint0.id.Copy();} -this.m0.pointCount=tMani.pointCount;b2Collision.b2CollidePoly(tMani,this.m_shape1,this.m_shape2,false);if(tMani.pointCount>0) -{var match=[false,false];for(var i=0;i<tMani.pointCount;++i) -{var cp=tMani.points[i];cp.normalImpulse=0.0;cp.tangentImpulse=0.0;var idKey=cp.id.key;for(var j=0;j<this.m0.pointCount;++j) -{if(match[j]==true) -continue;var cp0=this.m0.points[j];var id0=cp0.id;if(id0.key==idKey) -{match[j]=true;cp.normalImpulse=cp0.normalImpulse;cp.tangentImpulse=cp0.tangentImpulse;break;}}} -this.m_manifoldCount=1;} -else -{this.m_manifoldCount=0;}},GetManifolds:function() -{return this.m_manifold;},m_manifold:[new b2Manifold()]});b2PolyContact.Create=function(shape1,shape2,allocator){return new b2PolyContact(shape1,shape2);};b2PolyContact.Destroy=function(contact,allocator){}; -var b2ContactManager=Class.create();Object.extend(b2ContactManager.prototype,b2PairCallback.prototype);Object.extend(b2ContactManager.prototype,{initialize:function(){this.m_nullContact=new b2NullContact();this.m_world=null;this.m_destroyImmediate=false;},PairAdded:function(proxyUserData1,proxyUserData2){var shape1=proxyUserData1;var shape2=proxyUserData2;var body1=shape1.m_body;var body2=shape2.m_body;if(body1.IsStatic()&&body2.IsStatic()) -{return this.m_nullContact;} -if(shape1.m_body==shape2.m_body) -{return this.m_nullContact;} -if(body2.IsConnected(body1)) -{return this.m_nullContact;} -if(this.m_world.m_filter!=null&&this.m_world.m_filter.ShouldCollide(shape1,shape2)==false) -{return this.m_nullContact;} -if(body2.m_invMass==0.0) -{var tempShape=shape1;shape1=shape2;shape2=tempShape;var tempBody=body1;body1=body2;body2=tempBody;} -var contact=b2Contact.Create(shape1,shape2,this.m_world.m_blockAllocator);if(contact==null) -{return this.m_nullContact;} -else -{contact.m_prev=null;contact.m_next=this.m_world.m_contactList;if(this.m_world.m_contactList!=null) -{this.m_world.m_contactList.m_prev=contact;} -this.m_world.m_contactList=contact;this.m_world.m_contactCount++;} -return contact;},PairRemoved:function(proxyUserData1,proxyUserData2,pairUserData){if(pairUserData==null) -{return;} -var c=pairUserData;if(c!=this.m_nullContact) -{if(this.m_destroyImmediate==true) -{this.DestroyContact(c);c=null;} -else -{c.m_flags|=b2Contact.e_destroyFlag;}}},DestroyContact:function(c) -{if(c.m_prev) -{c.m_prev.m_next=c.m_next;} -if(c.m_next) -{c.m_next.m_prev=c.m_prev;} -if(c==this.m_world.m_contactList) -{this.m_world.m_contactList=c.m_next;} -if(c.GetManifoldCount()>0) -{var body1=c.m_shape1.m_body;var body2=c.m_shape2.m_body;var node1=c.m_node1;var node2=c.m_node2;body1.WakeUp();body2.WakeUp();if(node1.prev) -{node1.prev.next=node1.next;} -if(node1.next) -{node1.next.prev=node1.prev;} -if(node1==body1.m_contactList) -{body1.m_contactList=node1.next;} -node1.prev=null;node1.next=null;if(node2.prev) -{node2.prev.next=node2.next;} -if(node2.next) -{node2.next.prev=node2.prev;} -if(node2==body2.m_contactList) -{body2.m_contactList=node2.next;} -node2.prev=null;node2.next=null;} -b2Contact.Destroy(c,this.m_world.m_blockAllocator);--this.m_world.m_contactCount;},CleanContactList:function() -{var c=this.m_world.m_contactList;while(c!=null) -{var c0=c;c=c.m_next;if(c0.m_flags&b2Contact.e_destroyFlag) -{this.DestroyContact(c0);c0=null;}}},Collide:function() -{var body1;var body2;var node1;var node2;for(var c=this.m_world.m_contactList;c!=null;c=c.m_next) -{if(c.m_shape1.m_body.IsSleeping()&&c.m_shape2.m_body.IsSleeping()) -{continue;} -var oldCount=c.GetManifoldCount();c.Evaluate();var newCount=c.GetManifoldCount();if(oldCount==0&&newCount>0) -{body1=c.m_shape1.m_body;body2=c.m_shape2.m_body;node1=c.m_node1;node2=c.m_node2;node1.contact=c;node1.other=body2;node1.prev=null;node1.next=body1.m_contactList;if(node1.next!=null) -{node1.next.prev=c.m_node1;} -body1.m_contactList=c.m_node1;node2.contact=c;node2.other=body1;node2.prev=null;node2.next=body2.m_contactList;if(node2.next!=null) -{node2.next.prev=node2;} -body2.m_contactList=node2;} -else if(oldCount>0&&newCount==0) -{body1=c.m_shape1.m_body;body2=c.m_shape2.m_body;node1=c.m_node1;node2=c.m_node2;if(node1.prev) -{node1.prev.next=node1.next;} -if(node1.next) -{node1.next.prev=node1.prev;} -if(node1==body1.m_contactList) -{body1.m_contactList=node1.next;} -node1.prev=null;node1.next=null;if(node2.prev) -{node2.prev.next=node2.next;} -if(node2.next) -{node2.next.prev=node2.prev;} -if(node2==body2.m_contactList) -{body2.m_contactList=node2.next;} -node2.prev=null;node2.next=null;}}},m_world:null,m_nullContact:new b2NullContact(),m_destroyImmediate:null}); -var b2World=Class.create();b2World.prototype={initialize:function(worldAABB,gravity,doSleep){this.step=new b2TimeStep();this.m_contactManager=new b2ContactManager();this.m_listener=null;this.m_filter=b2CollisionFilter.b2_defaultFilter;this.m_bodyList=null;this.m_contactList=null;this.m_jointList=null;this.m_bodyCount=0;this.m_contactCount=0;this.m_jointCount=0;this.m_bodyDestroyList=null;this.m_allowSleep=doSleep;this.m_gravity=gravity;this.m_contactManager.m_world=this;this.m_broadPhase=new b2BroadPhase(worldAABB,this.m_contactManager);var bd=new b2BodyDef();this.m_groundBody=this.CreateBody(bd);},SetListener:function(listener){this.m_listener=listener;},SetFilter:function(filter){this.m_filter=filter;},CreateBody:function(def){var b=new b2Body(def,this);b.m_prev=null;b.m_next=this.m_bodyList;if(this.m_bodyList) -{this.m_bodyList.m_prev=b;} -this.m_bodyList=b;++this.m_bodyCount;return b;},DestroyBody:function(b) -{if(b.m_flags&b2Body.e_destroyFlag) -{return;} -if(b.m_prev) -{b.m_prev.m_next=b.m_next;} -if(b.m_next) -{b.m_next.m_prev=b.m_prev;} -if(b==this.m_bodyList) -{this.m_bodyList=b.m_next;} -b.m_flags|=b2Body.e_destroyFlag;--this.m_bodyCount;b.m_prev=null;b.m_next=this.m_bodyDestroyList;this.m_bodyDestroyList=b;},CleanBodyList:function() -{this.m_contactManager.m_destroyImmediate=true;var b=this.m_bodyDestroyList;while(b) -{var b0=b;b=b.m_next;var jn=b0.m_jointList;while(jn) -{var jn0=jn;jn=jn.next;if(this.m_listener) -{this.m_listener.NotifyJointDestroyed(jn0.joint);} -this.DestroyJoint(jn0.joint);} -b0.Destroy();} -this.m_bodyDestroyList=null;this.m_contactManager.m_destroyImmediate=false;},CreateJoint:function(def){var j=b2Joint.Create(def,this.m_blockAllocator);j.m_prev=null;j.m_next=this.m_jointList;if(this.m_jointList) -{this.m_jointList.m_prev=j;} -this.m_jointList=j;++this.m_jointCount;j.m_node1.joint=j;j.m_node1.other=j.m_body2;j.m_node1.prev=null;j.m_node1.next=j.m_body1.m_jointList;if(j.m_body1.m_jointList)j.m_body1.m_jointList.prev=j.m_node1;j.m_body1.m_jointList=j.m_node1;j.m_node2.joint=j;j.m_node2.other=j.m_body1;j.m_node2.prev=null;j.m_node2.next=j.m_body2.m_jointList;if(j.m_body2.m_jointList)j.m_body2.m_jointList.prev=j.m_node2;j.m_body2.m_jointList=j.m_node2;if(def.collideConnected==false) -{var b=def.body1.m_shapeCount<def.body2.m_shapeCount?def.body1:def.body2;for(var s=b.m_shapeList;s;s=s.m_next) -{s.ResetProxy(this.m_broadPhase);}} -return j;},DestroyJoint:function(j) -{var collideConnected=j.m_collideConnected;if(j.m_prev) -{j.m_prev.m_next=j.m_next;} -if(j.m_next) -{j.m_next.m_prev=j.m_prev;} -if(j==this.m_jointList) -{this.m_jointList=j.m_next;} -var body1=j.m_body1;var body2=j.m_body2;body1.WakeUp();body2.WakeUp();if(j.m_node1.prev) -{j.m_node1.prev.next=j.m_node1.next;} -if(j.m_node1.next) -{j.m_node1.next.prev=j.m_node1.prev;} -if(j.m_node1==body1.m_jointList) -{body1.m_jointList=j.m_node1.next;} -j.m_node1.prev=null;j.m_node1.next=null;if(j.m_node2.prev) -{j.m_node2.prev.next=j.m_node2.next;} -if(j.m_node2.next) -{j.m_node2.next.prev=j.m_node2.prev;} -if(j.m_node2==body2.m_jointList) -{body2.m_jointList=j.m_node2.next;} -j.m_node2.prev=null;j.m_node2.next=null;b2Joint.Destroy(j,this.m_blockAllocator);--this.m_jointCount;if(collideConnected==false) -{var b=body1.m_shapeCount<body2.m_shapeCount?body1:body2;for(var s=b.m_shapeList;s;s=s.m_next) -{s.ResetProxy(this.m_broadPhase);}}},GetGroundBody:function(){return this.m_groundBody;},step:new b2TimeStep(),Step:function(dt,iterations){var b;var other;this.step.dt=dt;this.step.iterations=iterations;if(dt>0.0) -{this.step.inv_dt=1.0/dt;} -else -{this.step.inv_dt=0.0;} -this.m_positionIterationCount=0;this.m_contactManager.CleanContactList();this.CleanBodyList();this.m_contactManager.Collide();var island=new b2Island(this.m_bodyCount,this.m_contactCount,this.m_jointCount,this.m_stackAllocator);for(b=this.m_bodyList;b!=null;b=b.m_next) -{b.m_flags&=~b2Body.e_islandFlag;} -for(var c=this.m_contactList;c!=null;c=c.m_next) -{c.m_flags&=~b2Contact.e_islandFlag;} -for(var j=this.m_jointList;j!=null;j=j.m_next) -{j.m_islandFlag=false;} -var stackSize=this.m_bodyCount;var stack=new Array(this.m_bodyCount);for(var k=0;k<this.m_bodyCount;k++) -stack[k]=null;for(var seed=this.m_bodyList;seed!=null;seed=seed.m_next) -{if(seed.m_flags&(b2Body.e_staticFlag|b2Body.e_islandFlag|b2Body.e_sleepFlag|b2Body.e_frozenFlag)) -{continue;} -island.Clear();var stackCount=0;stack[stackCount++]=seed;seed.m_flags|=b2Body.e_islandFlag;;while(stackCount>0) -{b=stack[--stackCount];island.AddBody(b);b.m_flags&=~b2Body.e_sleepFlag;if(b.m_flags&b2Body.e_staticFlag) -{continue;} -for(var cn=b.m_contactList;cn!=null;cn=cn.next) -{if(cn.contact.m_flags&b2Contact.e_islandFlag) -{continue;} -island.AddContact(cn.contact);cn.contact.m_flags|=b2Contact.e_islandFlag;other=cn.other;if(other.m_flags&b2Body.e_islandFlag) -{continue;} -stack[stackCount++]=other;other.m_flags|=b2Body.e_islandFlag;} -for(var jn=b.m_jointList;jn!=null;jn=jn.next) -{if(jn.joint.m_islandFlag==true) -{continue;} -island.AddJoint(jn.joint);jn.joint.m_islandFlag=true;other=jn.other;if(other.m_flags&b2Body.e_islandFlag) -{continue;} -stack[stackCount++]=other;other.m_flags|=b2Body.e_islandFlag;}} -island.Solve(this.step,this.m_gravity);this.m_positionIterationCount=b2Math.b2Max(this.m_positionIterationCount,b2Island.m_positionIterationCount);if(this.m_allowSleep) -{island.UpdateSleep(dt);} -for(var i=0;i<island.m_bodyCount;++i) -{b=island.m_bodies[i];if(b.m_flags&b2Body.e_staticFlag) -{b.m_flags&=~b2Body.e_islandFlag;} -if(b.IsFrozen()&&this.m_listener) -{var response=this.m_listener.NotifyBoundaryViolated(b);if(response==b2WorldListener.b2_destroyBody) -{this.DestroyBody(b);b=null;island.m_bodies[i]=null;}}}} -this.m_broadPhase.Commit();},Query:function(aabb,shapes,maxCount){var results=new Array();var count=this.m_broadPhase.QueryAABB(aabb,results,maxCount);for(var i=0;i<count;++i) -{shapes[i]=results[i];} -return count;},GetBodyList:function(){return this.m_bodyList;},GetJointList:function(){return this.m_jointList;},GetContactList:function(){return this.m_contactList;},m_blockAllocator:null,m_stackAllocator:null,m_broadPhase:null,m_contactManager:new b2ContactManager(),m_bodyList:null,m_contactList:null,m_jointList:null,m_bodyCount:0,m_contactCount:0,m_jointCount:0,m_bodyDestroyList:null,m_gravity:null,m_allowSleep:null,m_groundBody:null,m_listener:null,m_filter:null,m_positionIterationCount:0};b2World.s_enablePositionCorrection=1;b2World.s_enableWarmStarting=1; -var b2WorldListener=Class.create();b2WorldListener.prototype={NotifyJointDestroyed:function(joint){},NotifyBoundaryViolated:function(body) -{return b2WorldListener.b2_freezeBody;},initialize:function(){}};b2WorldListener.b2_freezeBody=0;b2WorldListener.b2_destroyBody=1; -var b2JointNode=Class.create();b2JointNode.prototype={other:null,joint:null,prev:null,next:null,initialize:function(){}} - -var b2Joint=Class.create();b2Joint.prototype={GetType:function(){return this.m_type;},GetAnchor1:function(){return null},GetAnchor2:function(){return null},GetReactionForce:function(invTimeStep){return null},GetReactionTorque:function(invTimeStep){return 0.0},GetBody1:function() -{return this.m_body1;},GetBody2:function() -{return this.m_body2;},GetNext:function(){return this.m_next;},GetUserData:function(){return this.m_userData;},initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;},PrepareVelocitySolver:function(){},SolveVelocityConstraints:function(step){},PreparePositionSolver:function(){},SolvePositionConstraints:function(){return false},m_type:0,m_prev:null,m_next:null,m_node1:new b2JointNode(),m_node2:new b2JointNode(),m_body1:null,m_body2:null,m_islandFlag:null,m_collideConnected:null,m_userData:null};b2Joint.Create=function(def,allocator){var joint=null;switch(def.type) -{case b2Joint.e_distanceJoint:{joint=new b2DistanceJoint(def);} -break;case b2Joint.e_mouseJoint:{joint=new b2MouseJoint(def);} -break;case b2Joint.e_prismaticJoint:{joint=new b2PrismaticJoint(def);} -break;case b2Joint.e_revoluteJoint:{joint=new b2RevoluteJoint(def);} -break;case b2Joint.e_pulleyJoint:{joint=new b2PulleyJoint(def);} -break;case b2Joint.e_gearJoint:{joint=new b2GearJoint(def);} -break;default:break;} -return joint;};b2Joint.Destroy=function(joint,allocator){};b2Joint.e_unknownJoint=0;b2Joint.e_revoluteJoint=1;b2Joint.e_prismaticJoint=2;b2Joint.e_distanceJoint=3;b2Joint.e_pulleyJoint=4;b2Joint.e_mouseJoint=5;b2Joint.e_gearJoint=6;b2Joint.e_inactiveLimit=0;b2Joint.e_atLowerLimit=1;b2Joint.e_atUpperLimit=2;b2Joint.e_equalLimits=3; -var b2JointDef=Class.create();b2JointDef.prototype={initialize:function() -{this.type=b2Joint.e_unknownJoint;this.userData=null;this.body1=null;this.body2=null;this.collideConnected=false;},type:0,userData:null,body1:null,body2:null,collideConnected:null} - -var b2DistanceJoint=Class.create();Object.extend(b2DistanceJoint.prototype,b2Joint.prototype);Object.extend(b2DistanceJoint.prototype,{initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;this.m_localAnchor1=new b2Vec2();this.m_localAnchor2=new b2Vec2();this.m_u=new b2Vec2();var tMat;var tX;var tY;tMat=this.m_body1.m_R;tX=def.anchorPoint1.x-this.m_body1.m_position.x;tY=def.anchorPoint1.y-this.m_body1.m_position.y;this.m_localAnchor1.x=tX*tMat.col1.x+tY*tMat.col1.y;this.m_localAnchor1.y=tX*tMat.col2.x+tY*tMat.col2.y;tMat=this.m_body2.m_R;tX=def.anchorPoint2.x-this.m_body2.m_position.x;tY=def.anchorPoint2.y-this.m_body2.m_position.y;this.m_localAnchor2.x=tX*tMat.col1.x+tY*tMat.col1.y;this.m_localAnchor2.y=tX*tMat.col2.x+tY*tMat.col2.y;tX=def.anchorPoint2.x-def.anchorPoint1.x;tY=def.anchorPoint2.y-def.anchorPoint1.y;this.m_length=Math.sqrt(tX*tX+tY*tY);this.m_impulse=0.0;},PrepareVelocitySolver:function(){var tMat;tMat=this.m_body1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=this.m_body2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;this.m_u.x=this.m_body2.m_position.x+r2X-this.m_body1.m_position.x-r1X;this.m_u.y=this.m_body2.m_position.y+r2Y-this.m_body1.m_position.y-r1Y;var length=Math.sqrt(this.m_u.x*this.m_u.x+this.m_u.y*this.m_u.y);if(length>b2Settings.b2_linearSlop) -{this.m_u.Multiply(1.0/length);} -else -{this.m_u.SetZero();} -var cr1u=(r1X*this.m_u.y-r1Y*this.m_u.x);var cr2u=(r2X*this.m_u.y-r2Y*this.m_u.x);this.m_mass=this.m_body1.m_invMass+this.m_body1.m_invI*cr1u*cr1u+this.m_body2.m_invMass+this.m_body2.m_invI*cr2u*cr2u;this.m_mass=1.0/this.m_mass;if(b2World.s_enableWarmStarting) -{var PX=this.m_impulse*this.m_u.x;var PY=this.m_impulse*this.m_u.y;this.m_body1.m_linearVelocity.x-=this.m_body1.m_invMass*PX;this.m_body1.m_linearVelocity.y-=this.m_body1.m_invMass*PY;this.m_body1.m_angularVelocity-=this.m_body1.m_invI*(r1X*PY-r1Y*PX);this.m_body2.m_linearVelocity.x+=this.m_body2.m_invMass*PX;this.m_body2.m_linearVelocity.y+=this.m_body2.m_invMass*PY;this.m_body2.m_angularVelocity+=this.m_body2.m_invI*(r2X*PY-r2Y*PX);} -else -{this.m_impulse=0.0;}},SolveVelocityConstraints:function(step){var tMat;tMat=this.m_body1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=this.m_body2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var v1X=this.m_body1.m_linearVelocity.x+(-this.m_body1.m_angularVelocity*r1Y);var v1Y=this.m_body1.m_linearVelocity.y+(this.m_body1.m_angularVelocity*r1X);var v2X=this.m_body2.m_linearVelocity.x+(-this.m_body2.m_angularVelocity*r2Y);var v2Y=this.m_body2.m_linearVelocity.y+(this.m_body2.m_angularVelocity*r2X);var Cdot=(this.m_u.x*(v2X-v1X)+this.m_u.y*(v2Y-v1Y));var impulse=-this.m_mass*Cdot;this.m_impulse+=impulse;var PX=impulse*this.m_u.x;var PY=impulse*this.m_u.y;this.m_body1.m_linearVelocity.x-=this.m_body1.m_invMass*PX;this.m_body1.m_linearVelocity.y-=this.m_body1.m_invMass*PY;this.m_body1.m_angularVelocity-=this.m_body1.m_invI*(r1X*PY-r1Y*PX);this.m_body2.m_linearVelocity.x+=this.m_body2.m_invMass*PX;this.m_body2.m_linearVelocity.y+=this.m_body2.m_invMass*PY;this.m_body2.m_angularVelocity+=this.m_body2.m_invI*(r2X*PY-r2Y*PX);},SolvePositionConstraints:function(){var tMat;tMat=this.m_body1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=this.m_body2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var dX=this.m_body2.m_position.x+r2X-this.m_body1.m_position.x-r1X;var dY=this.m_body2.m_position.y+r2Y-this.m_body1.m_position.y-r1Y;var length=Math.sqrt(dX*dX+dY*dY);dX/=length;dY/=length;var C=length-this.m_length;C=b2Math.b2Clamp(C,-b2Settings.b2_maxLinearCorrection,b2Settings.b2_maxLinearCorrection);var impulse=-this.m_mass*C;this.m_u.Set(dX,dY);var PX=impulse*this.m_u.x;var PY=impulse*this.m_u.y;this.m_body1.m_position.x-=this.m_body1.m_invMass*PX;this.m_body1.m_position.y-=this.m_body1.m_invMass*PY;this.m_body1.m_rotation-=this.m_body1.m_invI*(r1X*PY-r1Y*PX);this.m_body2.m_position.x+=this.m_body2.m_invMass*PX;this.m_body2.m_position.y+=this.m_body2.m_invMass*PY;this.m_body2.m_rotation+=this.m_body2.m_invI*(r2X*PY-r2Y*PX);this.m_body1.m_R.Set(this.m_body1.m_rotation);this.m_body2.m_R.Set(this.m_body2.m_rotation);return b2Math.b2Abs(C)<b2Settings.b2_linearSlop;},GetAnchor1:function(){return b2Math.AddVV(this.m_body1.m_position,b2Math.b2MulMV(this.m_body1.m_R,this.m_localAnchor1));},GetAnchor2:function(){return b2Math.AddVV(this.m_body2.m_position,b2Math.b2MulMV(this.m_body2.m_R,this.m_localAnchor2));},GetReactionForce:function(invTimeStep) -{var F=new b2Vec2();F.SetV(this.m_u);F.Multiply(this.m_impulse*invTimeStep);return F;},GetReactionTorque:function(invTimeStep) -{return 0.0;},m_localAnchor1:new b2Vec2(),m_localAnchor2:new b2Vec2(),m_u:new b2Vec2(),m_impulse:null,m_mass:null,m_length:null}); -var b2DistanceJointDef=Class.create();Object.extend(b2DistanceJointDef.prototype,b2JointDef.prototype);Object.extend(b2DistanceJointDef.prototype,{initialize:function() -{this.type=b2Joint.e_unknownJoint;this.userData=null;this.body1=null;this.body2=null;this.collideConnected=false;this.anchorPoint1=new b2Vec2();this.anchorPoint2=new b2Vec2();this.type=b2Joint.e_distanceJoint;},anchorPoint1:new b2Vec2(),anchorPoint2:new b2Vec2()}); -var b2Jacobian=Class.create();b2Jacobian.prototype={linear1:new b2Vec2(),angular1:null,linear2:new b2Vec2(),angular2:null,SetZero:function(){this.linear1.SetZero();this.angular1=0.0;this.linear2.SetZero();this.angular2=0.0;},Set:function(x1,a1,x2,a2){this.linear1.SetV(x1);this.angular1=a1;this.linear2.SetV(x2);this.angular2=a2;},Compute:function(x1,a1,x2,a2){return(this.linear1.x*x1.x+this.linear1.y*x1.y)+this.angular1*a1+(this.linear2.x*x2.x+this.linear2.y*x2.y)+this.angular2*a2;},initialize:function(){this.linear1=new b2Vec2();this.linear2=new b2Vec2();}}; -var b2GearJoint=Class.create();Object.extend(b2GearJoint.prototype,b2Joint.prototype);Object.extend(b2GearJoint.prototype,{GetAnchor1:function(){var tMat=this.m_body1.m_R;return new b2Vec2(this.m_body1.m_position.x+(tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y),this.m_body1.m_position.y+(tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y));},GetAnchor2:function(){var tMat=this.m_body2.m_R;return new b2Vec2(this.m_body2.m_position.x+(tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y),this.m_body2.m_position.y+(tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y));},GetReactionForce:function(invTimeStep){return new b2Vec2();},GetReactionTorque:function(invTimeStep){return 0.0;},GetRatio:function(){return this.m_ratio;},initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;this.m_groundAnchor1=new b2Vec2();this.m_groundAnchor2=new b2Vec2();this.m_localAnchor1=new b2Vec2();this.m_localAnchor2=new b2Vec2();this.m_J=new b2Jacobian();this.m_revolute1=null;this.m_prismatic1=null;this.m_revolute2=null;this.m_prismatic2=null;var coordinate1;var coordinate2;this.m_ground1=def.joint1.m_body1;this.m_body1=def.joint1.m_body2;if(def.joint1.m_type==b2Joint.e_revoluteJoint) -{this.m_revolute1=def.joint1;this.m_groundAnchor1.SetV(this.m_revolute1.m_localAnchor1);this.m_localAnchor1.SetV(this.m_revolute1.m_localAnchor2);coordinate1=this.m_revolute1.GetJointAngle();} -else -{this.m_prismatic1=def.joint1;this.m_groundAnchor1.SetV(this.m_prismatic1.m_localAnchor1);this.m_localAnchor1.SetV(this.m_prismatic1.m_localAnchor2);coordinate1=this.m_prismatic1.GetJointTranslation();} -this.m_ground2=def.joint2.m_body1;this.m_body2=def.joint2.m_body2;if(def.joint2.m_type==b2Joint.e_revoluteJoint) -{this.m_revolute2=def.joint2;this.m_groundAnchor2.SetV(this.m_revolute2.m_localAnchor1);this.m_localAnchor2.SetV(this.m_revolute2.m_localAnchor2);coordinate2=this.m_revolute2.GetJointAngle();} -else -{this.m_prismatic2=def.joint2;this.m_groundAnchor2.SetV(this.m_prismatic2.m_localAnchor1);this.m_localAnchor2.SetV(this.m_prismatic2.m_localAnchor2);coordinate2=this.m_prismatic2.GetJointTranslation();} -this.m_ratio=def.ratio;this.m_constant=coordinate1+this.m_ratio*coordinate2;this.m_impulse=0.0;},PrepareVelocitySolver:function(){var g1=this.m_ground1;var g2=this.m_ground2;var b1=this.m_body1;var b2=this.m_body2;var ugX;var ugY;var rX;var rY;var tMat;var tVec;var crug;var K=0.0;this.m_J.SetZero();if(this.m_revolute1) -{this.m_J.angular1=-1.0;K+=b1.m_invI;} -else -{tMat=g1.m_R;tVec=this.m_prismatic1.m_localXAxis1;ugX=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y;ugY=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y;tMat=b1.m_R;rX=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;rY=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;crug=rX*ugY-rY*ugX;this.m_J.linear1.Set(-ugX,-ugY);this.m_J.angular1=-crug;K+=b1.m_invMass+b1.m_invI*crug*crug;} -if(this.m_revolute2) -{this.m_J.angular2=-this.m_ratio;K+=this.m_ratio*this.m_ratio*b2.m_invI;} -else -{tMat=g2.m_R;tVec=this.m_prismatic2.m_localXAxis1;ugX=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y;ugY=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y;tMat=b2.m_R;rX=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;rY=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;crug=rX*ugY-rY*ugX;this.m_J.linear2.Set(-this.m_ratio*ugX,-this.m_ratio*ugY);this.m_J.angular2=-this.m_ratio*crug;K+=this.m_ratio*this.m_ratio*(b2.m_invMass+b2.m_invI*crug*crug);} -this.m_mass=1.0/K;b1.m_linearVelocity.x+=b1.m_invMass*this.m_impulse*this.m_J.linear1.x;b1.m_linearVelocity.y+=b1.m_invMass*this.m_impulse*this.m_J.linear1.y;b1.m_angularVelocity+=b1.m_invI*this.m_impulse*this.m_J.angular1;b2.m_linearVelocity.x+=b2.m_invMass*this.m_impulse*this.m_J.linear2.x;b2.m_linearVelocity.y+=b2.m_invMass*this.m_impulse*this.m_J.linear2.y;b2.m_angularVelocity+=b2.m_invI*this.m_impulse*this.m_J.angular2;},SolveVelocityConstraints:function(step){var b1=this.m_body1;var b2=this.m_body2;var Cdot=this.m_J.Compute(b1.m_linearVelocity,b1.m_angularVelocity,b2.m_linearVelocity,b2.m_angularVelocity);var impulse=-this.m_mass*Cdot;this.m_impulse+=impulse;b1.m_linearVelocity.x+=b1.m_invMass*impulse*this.m_J.linear1.x;b1.m_linearVelocity.y+=b1.m_invMass*impulse*this.m_J.linear1.y;b1.m_angularVelocity+=b1.m_invI*impulse*this.m_J.angular1;b2.m_linearVelocity.x+=b2.m_invMass*impulse*this.m_J.linear2.x;b2.m_linearVelocity.y+=b2.m_invMass*impulse*this.m_J.linear2.y;b2.m_angularVelocity+=b2.m_invI*impulse*this.m_J.angular2;},SolvePositionConstraints:function(){var linearError=0.0;var b1=this.m_body1;var b2=this.m_body2;var coordinate1;var coordinate2;if(this.m_revolute1) -{coordinate1=this.m_revolute1.GetJointAngle();} -else -{coordinate1=this.m_prismatic1.GetJointTranslation();} -if(this.m_revolute2) -{coordinate2=this.m_revolute2.GetJointAngle();} -else -{coordinate2=this.m_prismatic2.GetJointTranslation();} -var C=this.m_constant-(coordinate1+this.m_ratio*coordinate2);var impulse=-this.m_mass*C;b1.m_position.x+=b1.m_invMass*impulse*this.m_J.linear1.x;b1.m_position.y+=b1.m_invMass*impulse*this.m_J.linear1.y;b1.m_rotation+=b1.m_invI*impulse*this.m_J.angular1;b2.m_position.x+=b2.m_invMass*impulse*this.m_J.linear2.x;b2.m_position.y+=b2.m_invMass*impulse*this.m_J.linear2.y;b2.m_rotation+=b2.m_invI*impulse*this.m_J.angular2;b1.m_R.Set(b1.m_rotation);b2.m_R.Set(b2.m_rotation);return linearError<b2Settings.b2_linearSlop;},m_ground1:null,m_ground2:null,m_revolute1:null,m_prismatic1:null,m_revolute2:null,m_prismatic2:null,m_groundAnchor1:new b2Vec2(),m_groundAnchor2:new b2Vec2(),m_localAnchor1:new b2Vec2(),m_localAnchor2:new b2Vec2(),m_J:new b2Jacobian(),m_constant:null,m_ratio:null,m_mass:null,m_impulse:null}); -var b2GearJointDef=Class.create();Object.extend(b2GearJointDef.prototype,b2JointDef.prototype);Object.extend(b2GearJointDef.prototype,{initialize:function() -{this.type=b2Joint.e_gearJoint;this.joint1=null;this.joint2=null;this.ratio=1.0;},joint1:null,joint2:null,ratio:null}); -var b2MouseJoint=Class.create();Object.extend(b2MouseJoint.prototype,b2Joint.prototype);Object.extend(b2MouseJoint.prototype,{GetAnchor1:function(){return this.m_target;},GetAnchor2:function(){var tVec=b2Math.b2MulMV(this.m_body2.m_R,this.m_localAnchor);tVec.Add(this.m_body2.m_position);return tVec;},GetReactionForce:function(invTimeStep) -{var F=new b2Vec2();F.SetV(this.m_impulse);F.Multiply(invTimeStep);return F;},GetReactionTorque:function(invTimeStep) -{return 0.0;},SetTarget:function(target){this.m_body2.WakeUp();this.m_target=target;},initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;this.K=new b2Mat22();this.K1=new b2Mat22();this.K2=new b2Mat22();this.m_localAnchor=new b2Vec2();this.m_target=new b2Vec2();this.m_impulse=new b2Vec2();this.m_ptpMass=new b2Mat22();this.m_C=new b2Vec2();this.m_target.SetV(def.target);var tX=this.m_target.x-this.m_body2.m_position.x;var tY=this.m_target.y-this.m_body2.m_position.y;this.m_localAnchor.x=(tX*this.m_body2.m_R.col1.x+tY*this.m_body2.m_R.col1.y);this.m_localAnchor.y=(tX*this.m_body2.m_R.col2.x+tY*this.m_body2.m_R.col2.y);this.m_maxForce=def.maxForce;this.m_impulse.SetZero();var mass=this.m_body2.m_mass;var omega=2.0*b2Settings.b2_pi*def.frequencyHz;var d=2.0*mass*def.dampingRatio*omega;var k=mass*omega*omega;this.m_gamma=1.0/(d+def.timeStep*k);this.m_beta=def.timeStep*k/(d+def.timeStep*k);},K:new b2Mat22(),K1:new b2Mat22(),K2:new b2Mat22(),PrepareVelocitySolver:function(){var b=this.m_body2;var tMat;tMat=b.m_R;var rX=tMat.col1.x*this.m_localAnchor.x+tMat.col2.x*this.m_localAnchor.y;var rY=tMat.col1.y*this.m_localAnchor.x+tMat.col2.y*this.m_localAnchor.y;var invMass=b.m_invMass;var invI=b.m_invI;this.K1.col1.x=invMass;this.K1.col2.x=0.0;this.K1.col1.y=0.0;this.K1.col2.y=invMass;this.K2.col1.x=invI*rY*rY;this.K2.col2.x=-invI*rX*rY;this.K2.col1.y=-invI*rX*rY;this.K2.col2.y=invI*rX*rX;this.K.SetM(this.K1);this.K.AddM(this.K2);this.K.col1.x+=this.m_gamma;this.K.col2.y+=this.m_gamma;this.K.Invert(this.m_ptpMass);this.m_C.x=b.m_position.x+rX-this.m_target.x;this.m_C.y=b.m_position.y+rY-this.m_target.y;b.m_angularVelocity*=0.98;var PX=this.m_impulse.x;var PY=this.m_impulse.y;b.m_linearVelocity.x+=invMass*PX;b.m_linearVelocity.y+=invMass*PY;b.m_angularVelocity+=invI*(rX*PY-rY*PX);},SolveVelocityConstraints:function(step){var body=this.m_body2;var tMat;tMat=body.m_R;var rX=tMat.col1.x*this.m_localAnchor.x+tMat.col2.x*this.m_localAnchor.y;var rY=tMat.col1.y*this.m_localAnchor.x+tMat.col2.y*this.m_localAnchor.y;var CdotX=body.m_linearVelocity.x+(-body.m_angularVelocity*rY);var CdotY=body.m_linearVelocity.y+(body.m_angularVelocity*rX);tMat=this.m_ptpMass;var tX=CdotX+(this.m_beta*step.inv_dt)*this.m_C.x+this.m_gamma*this.m_impulse.x;var tY=CdotY+(this.m_beta*step.inv_dt)*this.m_C.y+this.m_gamma*this.m_impulse.y;var impulseX=-(tMat.col1.x*tX+tMat.col2.x*tY);var impulseY=-(tMat.col1.y*tX+tMat.col2.y*tY);var oldImpulseX=this.m_impulse.x;var oldImpulseY=this.m_impulse.y;this.m_impulse.x+=impulseX;this.m_impulse.y+=impulseY;var length=this.m_impulse.Length();if(length>step.dt*this.m_maxForce) -{this.m_impulse.Multiply(step.dt*this.m_maxForce/length);} -impulseX=this.m_impulse.x-oldImpulseX;impulseY=this.m_impulse.y-oldImpulseY;body.m_linearVelocity.x+=body.m_invMass*impulseX;body.m_linearVelocity.y+=body.m_invMass*impulseY;body.m_angularVelocity+=body.m_invI*(rX*impulseY-rY*impulseX);},SolvePositionConstraints:function(){return true;},m_localAnchor:new b2Vec2(),m_target:new b2Vec2(),m_impulse:new b2Vec2(),m_ptpMass:new b2Mat22(),m_C:new b2Vec2(),m_maxForce:null,m_beta:null,m_gamma:null}); -var b2MouseJointDef=Class.create();Object.extend(b2MouseJointDef.prototype,b2JointDef.prototype);Object.extend(b2MouseJointDef.prototype,{initialize:function() -{this.type=b2Joint.e_unknownJoint;this.userData=null;this.body1=null;this.body2=null;this.collideConnected=false;this.target=new b2Vec2();this.type=b2Joint.e_mouseJoint;this.maxForce=0.0;this.frequencyHz=5.0;this.dampingRatio=0.7;this.timeStep=1.0/60.0;},target:new b2Vec2(),maxForce:null,frequencyHz:null,dampingRatio:null,timeStep:null}); -var b2PrismaticJoint=Class.create();Object.extend(b2PrismaticJoint.prototype,b2Joint.prototype);Object.extend(b2PrismaticJoint.prototype,{GetAnchor1:function(){var b1=this.m_body1;var tVec=new b2Vec2();tVec.SetV(this.m_localAnchor1);tVec.MulM(b1.m_R);tVec.Add(b1.m_position);return tVec;},GetAnchor2:function(){var b2=this.m_body2;var tVec=new b2Vec2();tVec.SetV(this.m_localAnchor2);tVec.MulM(b2.m_R);tVec.Add(b2.m_position);return tVec;},GetJointTranslation:function(){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var p1X=b1.m_position.x+r1X;var p1Y=b1.m_position.y+r1Y;var p2X=b2.m_position.x+r2X;var p2Y=b2.m_position.y+r2Y;var dX=p2X-p1X;var dY=p2Y-p1Y;tMat=b1.m_R;var ax1X=tMat.col1.x*this.m_localXAxis1.x+tMat.col2.x*this.m_localXAxis1.y;var ax1Y=tMat.col1.y*this.m_localXAxis1.x+tMat.col2.y*this.m_localXAxis1.y;var translation=ax1X*dX+ax1Y*dY;return translation;},GetJointSpeed:function(){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var p1X=b1.m_position.x+r1X;var p1Y=b1.m_position.y+r1Y;var p2X=b2.m_position.x+r2X;var p2Y=b2.m_position.y+r2Y;var dX=p2X-p1X;var dY=p2Y-p1Y;tMat=b1.m_R;var ax1X=tMat.col1.x*this.m_localXAxis1.x+tMat.col2.x*this.m_localXAxis1.y;var ax1Y=tMat.col1.y*this.m_localXAxis1.x+tMat.col2.y*this.m_localXAxis1.y;var v1=b1.m_linearVelocity;var v2=b2.m_linearVelocity;var w1=b1.m_angularVelocity;var w2=b2.m_angularVelocity;var speed=(dX*(-w1*ax1Y)+dY*(w1*ax1X))+(ax1X*(((v2.x+(-w2*r2Y))-v1.x)-(-w1*r1Y))+ax1Y*(((v2.y+(w2*r2X))-v1.y)-(w1*r1X)));return speed;},GetMotorForce:function(invTimeStep){return invTimeStep*this.m_motorImpulse;},SetMotorSpeed:function(speed) -{this.m_motorSpeed=speed;},SetMotorForce:function(force) -{this.m_maxMotorForce=force;},GetReactionForce:function(invTimeStep) -{var tImp=invTimeStep*this.m_limitImpulse;var tMat;tMat=this.m_body1.m_R;var ax1X=tImp*(tMat.col1.x*this.m_localXAxis1.x+tMat.col2.x*this.m_localXAxis1.y);var ax1Y=tImp*(tMat.col1.y*this.m_localXAxis1.x+tMat.col2.y*this.m_localXAxis1.y);var ay1X=tImp*(tMat.col1.x*this.m_localYAxis1.x+tMat.col2.x*this.m_localYAxis1.y);var ay1Y=tImp*(tMat.col1.y*this.m_localYAxis1.x+tMat.col2.y*this.m_localYAxis1.y);return new b2Vec2(ax1X+ay1X,ax1Y+ay1Y);},GetReactionTorque:function(invTimeStep) -{return invTimeStep*this.m_angularImpulse;},initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;this.m_localAnchor1=new b2Vec2();this.m_localAnchor2=new b2Vec2();this.m_localXAxis1=new b2Vec2();this.m_localYAxis1=new b2Vec2();this.m_linearJacobian=new b2Jacobian();this.m_motorJacobian=new b2Jacobian();var tMat;var tX;var tY;tMat=this.m_body1.m_R;tX=(def.anchorPoint.x-this.m_body1.m_position.x);tY=(def.anchorPoint.y-this.m_body1.m_position.y);this.m_localAnchor1.Set((tX*tMat.col1.x+tY*tMat.col1.y),(tX*tMat.col2.x+tY*tMat.col2.y));tMat=this.m_body2.m_R;tX=(def.anchorPoint.x-this.m_body2.m_position.x);tY=(def.anchorPoint.y-this.m_body2.m_position.y);this.m_localAnchor2.Set((tX*tMat.col1.x+tY*tMat.col1.y),(tX*tMat.col2.x+tY*tMat.col2.y));tMat=this.m_body1.m_R;tX=def.axis.x;tY=def.axis.y;this.m_localXAxis1.Set((tX*tMat.col1.x+tY*tMat.col1.y),(tX*tMat.col2.x+tY*tMat.col2.y));this.m_localYAxis1.x=-this.m_localXAxis1.y;this.m_localYAxis1.y=this.m_localXAxis1.x;this.m_initialAngle=this.m_body2.m_rotation-this.m_body1.m_rotation;this.m_linearJacobian.SetZero();this.m_linearMass=0.0;this.m_linearImpulse=0.0;this.m_angularMass=0.0;this.m_angularImpulse=0.0;this.m_motorJacobian.SetZero();this.m_motorMass=0.0;this.m_motorImpulse=0.0;this.m_limitImpulse=0.0;this.m_limitPositionImpulse=0.0;this.m_lowerTranslation=def.lowerTranslation;this.m_upperTranslation=def.upperTranslation;this.m_maxMotorForce=def.motorForce;this.m_motorSpeed=def.motorSpeed;this.m_enableLimit=def.enableLimit;this.m_enableMotor=def.enableMotor;},PrepareVelocitySolver:function(){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var invMass1=b1.m_invMass;var invMass2=b2.m_invMass;var invI1=b1.m_invI;var invI2=b2.m_invI;tMat=b1.m_R;var ay1X=tMat.col1.x*this.m_localYAxis1.x+tMat.col2.x*this.m_localYAxis1.y;var ay1Y=tMat.col1.y*this.m_localYAxis1.x+tMat.col2.y*this.m_localYAxis1.y;var eX=b2.m_position.x+r2X-b1.m_position.x;var eY=b2.m_position.y+r2Y-b1.m_position.y;this.m_linearJacobian.linear1.x=-ay1X;this.m_linearJacobian.linear1.y=-ay1Y;this.m_linearJacobian.linear2.x=ay1X;this.m_linearJacobian.linear2.y=ay1Y;this.m_linearJacobian.angular1=-(eX*ay1Y-eY*ay1X);this.m_linearJacobian.angular2=r2X*ay1Y-r2Y*ay1X;this.m_linearMass=invMass1+invI1*this.m_linearJacobian.angular1*this.m_linearJacobian.angular1+ -invMass2+invI2*this.m_linearJacobian.angular2*this.m_linearJacobian.angular2;this.m_linearMass=1.0/this.m_linearMass;this.m_angularMass=1.0/(invI1+invI2);if(this.m_enableLimit||this.m_enableMotor) -{tMat=b1.m_R;var ax1X=tMat.col1.x*this.m_localXAxis1.x+tMat.col2.x*this.m_localXAxis1.y;var ax1Y=tMat.col1.y*this.m_localXAxis1.x+tMat.col2.y*this.m_localXAxis1.y;this.m_motorJacobian.linear1.x=-ax1X;this.m_motorJacobian.linear1.y=-ax1Y;this.m_motorJacobian.linear2.x=ax1X;this.m_motorJacobian.linear2.y=ax1Y;this.m_motorJacobian.angular1=-(eX*ax1Y-eY*ax1X);this.m_motorJacobian.angular2=r2X*ax1Y-r2Y*ax1X;this.m_motorMass=invMass1+invI1*this.m_motorJacobian.angular1*this.m_motorJacobian.angular1+ -invMass2+invI2*this.m_motorJacobian.angular2*this.m_motorJacobian.angular2;this.m_motorMass=1.0/this.m_motorMass;if(this.m_enableLimit) -{var dX=eX-r1X;var dY=eY-r1Y;var jointTranslation=ax1X*dX+ax1Y*dY;if(b2Math.b2Abs(this.m_upperTranslation-this.m_lowerTranslation)<2.0*b2Settings.b2_linearSlop) -{this.m_limitState=b2Joint.e_equalLimits;} -else if(jointTranslation<=this.m_lowerTranslation) -{if(this.m_limitState!=b2Joint.e_atLowerLimit) -{this.m_limitImpulse=0.0;} -this.m_limitState=b2Joint.e_atLowerLimit;} -else if(jointTranslation>=this.m_upperTranslation) -{if(this.m_limitState!=b2Joint.e_atUpperLimit) -{this.m_limitImpulse=0.0;} -this.m_limitState=b2Joint.e_atUpperLimit;} -else -{this.m_limitState=b2Joint.e_inactiveLimit;this.m_limitImpulse=0.0;}}} -if(this.m_enableMotor==false) -{this.m_motorImpulse=0.0;} -if(this.m_enableLimit==false) -{this.m_limitImpulse=0.0;} -if(b2World.s_enableWarmStarting) -{var P1X=this.m_linearImpulse*this.m_linearJacobian.linear1.x+(this.m_motorImpulse+this.m_limitImpulse)*this.m_motorJacobian.linear1.x;var P1Y=this.m_linearImpulse*this.m_linearJacobian.linear1.y+(this.m_motorImpulse+this.m_limitImpulse)*this.m_motorJacobian.linear1.y;var P2X=this.m_linearImpulse*this.m_linearJacobian.linear2.x+(this.m_motorImpulse+this.m_limitImpulse)*this.m_motorJacobian.linear2.x;var P2Y=this.m_linearImpulse*this.m_linearJacobian.linear2.y+(this.m_motorImpulse+this.m_limitImpulse)*this.m_motorJacobian.linear2.y;var L1=this.m_linearImpulse*this.m_linearJacobian.angular1-this.m_angularImpulse+(this.m_motorImpulse+this.m_limitImpulse)*this.m_motorJacobian.angular1;var L2=this.m_linearImpulse*this.m_linearJacobian.angular2+this.m_angularImpulse+(this.m_motorImpulse+this.m_limitImpulse)*this.m_motorJacobian.angular2;b1.m_linearVelocity.x+=invMass1*P1X;b1.m_linearVelocity.y+=invMass1*P1Y;b1.m_angularVelocity+=invI1*L1;b2.m_linearVelocity.x+=invMass2*P2X;b2.m_linearVelocity.y+=invMass2*P2Y;b2.m_angularVelocity+=invI2*L2;} -else -{this.m_linearImpulse=0.0;this.m_angularImpulse=0.0;this.m_limitImpulse=0.0;this.m_motorImpulse=0.0;} -this.m_limitPositionImpulse=0.0;},SolveVelocityConstraints:function(step){var b1=this.m_body1;var b2=this.m_body2;var invMass1=b1.m_invMass;var invMass2=b2.m_invMass;var invI1=b1.m_invI;var invI2=b2.m_invI;var oldLimitImpulse;var linearCdot=this.m_linearJacobian.Compute(b1.m_linearVelocity,b1.m_angularVelocity,b2.m_linearVelocity,b2.m_angularVelocity);var linearImpulse=-this.m_linearMass*linearCdot;this.m_linearImpulse+=linearImpulse;b1.m_linearVelocity.x+=(invMass1*linearImpulse)*this.m_linearJacobian.linear1.x;b1.m_linearVelocity.y+=(invMass1*linearImpulse)*this.m_linearJacobian.linear1.y;b1.m_angularVelocity+=invI1*linearImpulse*this.m_linearJacobian.angular1;b2.m_linearVelocity.x+=(invMass2*linearImpulse)*this.m_linearJacobian.linear2.x;b2.m_linearVelocity.y+=(invMass2*linearImpulse)*this.m_linearJacobian.linear2.y;b2.m_angularVelocity+=invI2*linearImpulse*this.m_linearJacobian.angular2;var angularCdot=b2.m_angularVelocity-b1.m_angularVelocity;var angularImpulse=-this.m_angularMass*angularCdot;this.m_angularImpulse+=angularImpulse;b1.m_angularVelocity-=invI1*angularImpulse;b2.m_angularVelocity+=invI2*angularImpulse;if(this.m_enableMotor&&this.m_limitState!=b2Joint.e_equalLimits) -{var motorCdot=this.m_motorJacobian.Compute(b1.m_linearVelocity,b1.m_angularVelocity,b2.m_linearVelocity,b2.m_angularVelocity)-this.m_motorSpeed;var motorImpulse=-this.m_motorMass*motorCdot;var oldMotorImpulse=this.m_motorImpulse;this.m_motorImpulse=b2Math.b2Clamp(this.m_motorImpulse+motorImpulse,-step.dt*this.m_maxMotorForce,step.dt*this.m_maxMotorForce);motorImpulse=this.m_motorImpulse-oldMotorImpulse;b1.m_linearVelocity.x+=(invMass1*motorImpulse)*this.m_motorJacobian.linear1.x;b1.m_linearVelocity.y+=(invMass1*motorImpulse)*this.m_motorJacobian.linear1.y;b1.m_angularVelocity+=invI1*motorImpulse*this.m_motorJacobian.angular1;b2.m_linearVelocity.x+=(invMass2*motorImpulse)*this.m_motorJacobian.linear2.x;b2.m_linearVelocity.y+=(invMass2*motorImpulse)*this.m_motorJacobian.linear2.y;b2.m_angularVelocity+=invI2*motorImpulse*this.m_motorJacobian.angular2;} -if(this.m_enableLimit&&this.m_limitState!=b2Joint.e_inactiveLimit) -{var limitCdot=this.m_motorJacobian.Compute(b1.m_linearVelocity,b1.m_angularVelocity,b2.m_linearVelocity,b2.m_angularVelocity);var limitImpulse=-this.m_motorMass*limitCdot;if(this.m_limitState==b2Joint.e_equalLimits) -{this.m_limitImpulse+=limitImpulse;} -else if(this.m_limitState==b2Joint.e_atLowerLimit) -{oldLimitImpulse=this.m_limitImpulse;this.m_limitImpulse=b2Math.b2Max(this.m_limitImpulse+limitImpulse,0.0);limitImpulse=this.m_limitImpulse-oldLimitImpulse;} -else if(this.m_limitState==b2Joint.e_atUpperLimit) -{oldLimitImpulse=this.m_limitImpulse;this.m_limitImpulse=b2Math.b2Min(this.m_limitImpulse+limitImpulse,0.0);limitImpulse=this.m_limitImpulse-oldLimitImpulse;} -b1.m_linearVelocity.x+=(invMass1*limitImpulse)*this.m_motorJacobian.linear1.x;b1.m_linearVelocity.y+=(invMass1*limitImpulse)*this.m_motorJacobian.linear1.y;b1.m_angularVelocity+=invI1*limitImpulse*this.m_motorJacobian.angular1;b2.m_linearVelocity.x+=(invMass2*limitImpulse)*this.m_motorJacobian.linear2.x;b2.m_linearVelocity.y+=(invMass2*limitImpulse)*this.m_motorJacobian.linear2.y;b2.m_angularVelocity+=invI2*limitImpulse*this.m_motorJacobian.angular2;}},SolvePositionConstraints:function(){var limitC;var oldLimitImpulse;var b1=this.m_body1;var b2=this.m_body2;var invMass1=b1.m_invMass;var invMass2=b2.m_invMass;var invI1=b1.m_invI;var invI2=b2.m_invI;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var p1X=b1.m_position.x+r1X;var p1Y=b1.m_position.y+r1Y;var p2X=b2.m_position.x+r2X;var p2Y=b2.m_position.y+r2Y;var dX=p2X-p1X;var dY=p2Y-p1Y;tMat=b1.m_R;var ay1X=tMat.col1.x*this.m_localYAxis1.x+tMat.col2.x*this.m_localYAxis1.y;var ay1Y=tMat.col1.y*this.m_localYAxis1.x+tMat.col2.y*this.m_localYAxis1.y;var linearC=ay1X*dX+ay1Y*dY;linearC=b2Math.b2Clamp(linearC,-b2Settings.b2_maxLinearCorrection,b2Settings.b2_maxLinearCorrection);var linearImpulse=-this.m_linearMass*linearC;b1.m_position.x+=(invMass1*linearImpulse)*this.m_linearJacobian.linear1.x;b1.m_position.y+=(invMass1*linearImpulse)*this.m_linearJacobian.linear1.y;b1.m_rotation+=invI1*linearImpulse*this.m_linearJacobian.angular1;b2.m_position.x+=(invMass2*linearImpulse)*this.m_linearJacobian.linear2.x;b2.m_position.y+=(invMass2*linearImpulse)*this.m_linearJacobian.linear2.y;b2.m_rotation+=invI2*linearImpulse*this.m_linearJacobian.angular2;var positionError=b2Math.b2Abs(linearC);var angularC=b2.m_rotation-b1.m_rotation-this.m_initialAngle;angularC=b2Math.b2Clamp(angularC,-b2Settings.b2_maxAngularCorrection,b2Settings.b2_maxAngularCorrection);var angularImpulse=-this.m_angularMass*angularC;b1.m_rotation-=b1.m_invI*angularImpulse;b1.m_R.Set(b1.m_rotation);b2.m_rotation+=b2.m_invI*angularImpulse;b2.m_R.Set(b2.m_rotation);var angularError=b2Math.b2Abs(angularC);if(this.m_enableLimit&&this.m_limitState!=b2Joint.e_inactiveLimit) -{tMat=b1.m_R;r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;p1X=b1.m_position.x+r1X;p1Y=b1.m_position.y+r1Y;p2X=b2.m_position.x+r2X;p2Y=b2.m_position.y+r2Y;dX=p2X-p1X;dY=p2Y-p1Y;tMat=b1.m_R;var ax1X=tMat.col1.x*this.m_localXAxis1.x+tMat.col2.x*this.m_localXAxis1.y;var ax1Y=tMat.col1.y*this.m_localXAxis1.x+tMat.col2.y*this.m_localXAxis1.y;var translation=(ax1X*dX+ax1Y*dY);var limitImpulse=0.0;if(this.m_limitState==b2Joint.e_equalLimits) -{limitC=b2Math.b2Clamp(translation,-b2Settings.b2_maxLinearCorrection,b2Settings.b2_maxLinearCorrection);limitImpulse=-this.m_motorMass*limitC;positionError=b2Math.b2Max(positionError,b2Math.b2Abs(angularC));} -else if(this.m_limitState==b2Joint.e_atLowerLimit) -{limitC=translation-this.m_lowerTranslation;positionError=b2Math.b2Max(positionError,-limitC);limitC=b2Math.b2Clamp(limitC+b2Settings.b2_linearSlop,-b2Settings.b2_maxLinearCorrection,0.0);limitImpulse=-this.m_motorMass*limitC;oldLimitImpulse=this.m_limitPositionImpulse;this.m_limitPositionImpulse=b2Math.b2Max(this.m_limitPositionImpulse+limitImpulse,0.0);limitImpulse=this.m_limitPositionImpulse-oldLimitImpulse;} -else if(this.m_limitState==b2Joint.e_atUpperLimit) -{limitC=translation-this.m_upperTranslation;positionError=b2Math.b2Max(positionError,limitC);limitC=b2Math.b2Clamp(limitC-b2Settings.b2_linearSlop,0.0,b2Settings.b2_maxLinearCorrection);limitImpulse=-this.m_motorMass*limitC;oldLimitImpulse=this.m_limitPositionImpulse;this.m_limitPositionImpulse=b2Math.b2Min(this.m_limitPositionImpulse+limitImpulse,0.0);limitImpulse=this.m_limitPositionImpulse-oldLimitImpulse;} -b1.m_position.x+=(invMass1*limitImpulse)*this.m_motorJacobian.linear1.x;b1.m_position.y+=(invMass1*limitImpulse)*this.m_motorJacobian.linear1.y;b1.m_rotation+=invI1*limitImpulse*this.m_motorJacobian.angular1;b1.m_R.Set(b1.m_rotation);b2.m_position.x+=(invMass2*limitImpulse)*this.m_motorJacobian.linear2.x;b2.m_position.y+=(invMass2*limitImpulse)*this.m_motorJacobian.linear2.y;b2.m_rotation+=invI2*limitImpulse*this.m_motorJacobian.angular2;b2.m_R.Set(b2.m_rotation);} -return positionError<=b2Settings.b2_linearSlop&&angularError<=b2Settings.b2_angularSlop;},m_localAnchor1:new b2Vec2(),m_localAnchor2:new b2Vec2(),m_localXAxis1:new b2Vec2(),m_localYAxis1:new b2Vec2(),m_initialAngle:null,m_linearJacobian:new b2Jacobian(),m_linearMass:null,m_linearImpulse:null,m_angularMass:null,m_angularImpulse:null,m_motorJacobian:new b2Jacobian(),m_motorMass:null,m_motorImpulse:null,m_limitImpulse:null,m_limitPositionImpulse:null,m_lowerTranslation:null,m_upperTranslation:null,m_maxMotorForce:null,m_motorSpeed:null,m_enableLimit:null,m_enableMotor:null,m_limitState:0}); -var b2PrismaticJointDef=Class.create();Object.extend(b2PrismaticJointDef.prototype,b2JointDef.prototype);Object.extend(b2PrismaticJointDef.prototype,{initialize:function() -{this.type=b2Joint.e_unknownJoint;this.userData=null;this.body1=null;this.body2=null;this.collideConnected=false;this.type=b2Joint.e_prismaticJoint;this.anchorPoint=new b2Vec2(0.0,0.0);this.axis=new b2Vec2(0.0,0.0);this.lowerTranslation=0.0;this.upperTranslation=0.0;this.motorForce=0.0;this.motorSpeed=0.0;this.enableLimit=false;this.enableMotor=false;},anchorPoint:null,axis:null,lowerTranslation:null,upperTranslation:null,motorForce:null,motorSpeed:null,enableLimit:null,enableMotor:null}); -var b2PulleyJoint=Class.create();Object.extend(b2PulleyJoint.prototype,b2Joint.prototype);Object.extend(b2PulleyJoint.prototype,{GetAnchor1:function(){var tMat=this.m_body1.m_R;return new b2Vec2(this.m_body1.m_position.x+(tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y),this.m_body1.m_position.y+(tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y));},GetAnchor2:function(){var tMat=this.m_body2.m_R;return new b2Vec2(this.m_body2.m_position.x+(tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y),this.m_body2.m_position.y+(tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y));},GetGroundPoint1:function(){return new b2Vec2(this.m_ground.m_position.x+this.m_groundAnchor1.x,this.m_ground.m_position.y+this.m_groundAnchor1.y);},GetGroundPoint2:function(){return new b2Vec2(this.m_ground.m_position.x+this.m_groundAnchor2.x,this.m_ground.m_position.y+this.m_groundAnchor2.y);},GetReactionForce:function(invTimeStep){return new b2Vec2();},GetReactionTorque:function(invTimeStep){return 0.0;},GetLength1:function(){var tMat;tMat=this.m_body1.m_R;var pX=this.m_body1.m_position.x+(tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y);var pY=this.m_body1.m_position.y+(tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y);var dX=pX-(this.m_ground.m_position.x+this.m_groundAnchor1.x);var dY=pY-(this.m_ground.m_position.y+this.m_groundAnchor1.y);return Math.sqrt(dX*dX+dY*dY);},GetLength2:function(){var tMat;tMat=this.m_body2.m_R;var pX=this.m_body2.m_position.x+(tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y);var pY=this.m_body2.m_position.y+(tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y);var dX=pX-(this.m_ground.m_position.x+this.m_groundAnchor2.x);var dY=pY-(this.m_ground.m_position.y+this.m_groundAnchor2.y);return Math.sqrt(dX*dX+dY*dY);},GetRatio:function(){return this.m_ratio;},initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;this.m_groundAnchor1=new b2Vec2();this.m_groundAnchor2=new b2Vec2();this.m_localAnchor1=new b2Vec2();this.m_localAnchor2=new b2Vec2();this.m_u1=new b2Vec2();this.m_u2=new b2Vec2();var tMat;var tX;var tY;this.m_ground=this.m_body1.m_world.m_groundBody;this.m_groundAnchor1.x=def.groundPoint1.x-this.m_ground.m_position.x;this.m_groundAnchor1.y=def.groundPoint1.y-this.m_ground.m_position.y;this.m_groundAnchor2.x=def.groundPoint2.x-this.m_ground.m_position.x;this.m_groundAnchor2.y=def.groundPoint2.y-this.m_ground.m_position.y;tMat=this.m_body1.m_R;tX=def.anchorPoint1.x-this.m_body1.m_position.x;tY=def.anchorPoint1.y-this.m_body1.m_position.y;this.m_localAnchor1.x=tX*tMat.col1.x+tY*tMat.col1.y;this.m_localAnchor1.y=tX*tMat.col2.x+tY*tMat.col2.y;tMat=this.m_body2.m_R;tX=def.anchorPoint2.x-this.m_body2.m_position.x;tY=def.anchorPoint2.y-this.m_body2.m_position.y;this.m_localAnchor2.x=tX*tMat.col1.x+tY*tMat.col1.y;this.m_localAnchor2.y=tX*tMat.col2.x+tY*tMat.col2.y;this.m_ratio=def.ratio;tX=def.groundPoint1.x-def.anchorPoint1.x;tY=def.groundPoint1.y-def.anchorPoint1.y;var d1Len=Math.sqrt(tX*tX+tY*tY);tX=def.groundPoint2.x-def.anchorPoint2.x;tY=def.groundPoint2.y-def.anchorPoint2.y;var d2Len=Math.sqrt(tX*tX+tY*tY);var length1=b2Math.b2Max(0.5*b2PulleyJoint.b2_minPulleyLength,d1Len);var length2=b2Math.b2Max(0.5*b2PulleyJoint.b2_minPulleyLength,d2Len);this.m_constant=length1+this.m_ratio*length2;this.m_maxLength1=b2Math.b2Clamp(def.maxLength1,length1,this.m_constant-this.m_ratio*b2PulleyJoint.b2_minPulleyLength);this.m_maxLength2=b2Math.b2Clamp(def.maxLength2,length2,(this.m_constant-b2PulleyJoint.b2_minPulleyLength)/this.m_ratio);this.m_pulleyImpulse=0.0;this.m_limitImpulse1=0.0;this.m_limitImpulse2=0.0;},PrepareVelocitySolver:function(){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var p1X=b1.m_position.x+r1X;var p1Y=b1.m_position.y+r1Y;var p2X=b2.m_position.x+r2X;var p2Y=b2.m_position.y+r2Y;var s1X=this.m_ground.m_position.x+this.m_groundAnchor1.x;var s1Y=this.m_ground.m_position.y+this.m_groundAnchor1.y;var s2X=this.m_ground.m_position.x+this.m_groundAnchor2.x;var s2Y=this.m_ground.m_position.y+this.m_groundAnchor2.y;this.m_u1.Set(p1X-s1X,p1Y-s1Y);this.m_u2.Set(p2X-s2X,p2Y-s2Y);var length1=this.m_u1.Length();var length2=this.m_u2.Length();if(length1>b2Settings.b2_linearSlop) -{this.m_u1.Multiply(1.0/length1);} -else -{this.m_u1.SetZero();} -if(length2>b2Settings.b2_linearSlop) -{this.m_u2.Multiply(1.0/length2);} -else -{this.m_u2.SetZero();} -if(length1<this.m_maxLength1) -{this.m_limitState1=b2Joint.e_inactiveLimit;this.m_limitImpulse1=0.0;} -else -{this.m_limitState1=b2Joint.e_atUpperLimit;this.m_limitPositionImpulse1=0.0;} -if(length2<this.m_maxLength2) -{this.m_limitState2=b2Joint.e_inactiveLimit;this.m_limitImpulse2=0.0;} -else -{this.m_limitState2=b2Joint.e_atUpperLimit;this.m_limitPositionImpulse2=0.0;} -var cr1u1=r1X*this.m_u1.y-r1Y*this.m_u1.x;var cr2u2=r2X*this.m_u2.y-r2Y*this.m_u2.x;this.m_limitMass1=b1.m_invMass+b1.m_invI*cr1u1*cr1u1;this.m_limitMass2=b2.m_invMass+b2.m_invI*cr2u2*cr2u2;this.m_pulleyMass=this.m_limitMass1+this.m_ratio*this.m_ratio*this.m_limitMass2;this.m_limitMass1=1.0/this.m_limitMass1;this.m_limitMass2=1.0/this.m_limitMass2;this.m_pulleyMass=1.0/this.m_pulleyMass;var P1X=(-this.m_pulleyImpulse-this.m_limitImpulse1)*this.m_u1.x;var P1Y=(-this.m_pulleyImpulse-this.m_limitImpulse1)*this.m_u1.y;var P2X=(-this.m_ratio*this.m_pulleyImpulse-this.m_limitImpulse2)*this.m_u2.x;var P2Y=(-this.m_ratio*this.m_pulleyImpulse-this.m_limitImpulse2)*this.m_u2.y;b1.m_linearVelocity.x+=b1.m_invMass*P1X;b1.m_linearVelocity.y+=b1.m_invMass*P1Y;b1.m_angularVelocity+=b1.m_invI*(r1X*P1Y-r1Y*P1X);b2.m_linearVelocity.x+=b2.m_invMass*P2X;b2.m_linearVelocity.y+=b2.m_invMass*P2Y;b2.m_angularVelocity+=b2.m_invI*(r2X*P2Y-r2Y*P2X);},SolveVelocityConstraints:function(step){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var v1X;var v1Y;var v2X;var v2Y;var P1X;var P1Y;var P2X;var P2Y;var Cdot;var impulse;var oldLimitImpulse;v1X=b1.m_linearVelocity.x+(-b1.m_angularVelocity*r1Y);v1Y=b1.m_linearVelocity.y+(b1.m_angularVelocity*r1X);v2X=b2.m_linearVelocity.x+(-b2.m_angularVelocity*r2Y);v2Y=b2.m_linearVelocity.y+(b2.m_angularVelocity*r2X);Cdot=-(this.m_u1.x*v1X+this.m_u1.y*v1Y)-this.m_ratio*(this.m_u2.x*v2X+this.m_u2.y*v2Y);impulse=-this.m_pulleyMass*Cdot;this.m_pulleyImpulse+=impulse;P1X=-impulse*this.m_u1.x;P1Y=-impulse*this.m_u1.y;P2X=-this.m_ratio*impulse*this.m_u2.x;P2Y=-this.m_ratio*impulse*this.m_u2.y;b1.m_linearVelocity.x+=b1.m_invMass*P1X;b1.m_linearVelocity.y+=b1.m_invMass*P1Y;b1.m_angularVelocity+=b1.m_invI*(r1X*P1Y-r1Y*P1X);b2.m_linearVelocity.x+=b2.m_invMass*P2X;b2.m_linearVelocity.y+=b2.m_invMass*P2Y;b2.m_angularVelocity+=b2.m_invI*(r2X*P2Y-r2Y*P2X);if(this.m_limitState1==b2Joint.e_atUpperLimit) -{v1X=b1.m_linearVelocity.x+(-b1.m_angularVelocity*r1Y);v1Y=b1.m_linearVelocity.y+(b1.m_angularVelocity*r1X);Cdot=-(this.m_u1.x*v1X+this.m_u1.y*v1Y);impulse=-this.m_limitMass1*Cdot;oldLimitImpulse=this.m_limitImpulse1;this.m_limitImpulse1=b2Math.b2Max(0.0,this.m_limitImpulse1+impulse);impulse=this.m_limitImpulse1-oldLimitImpulse;P1X=-impulse*this.m_u1.x;P1Y=-impulse*this.m_u1.y;b1.m_linearVelocity.x+=b1.m_invMass*P1X;b1.m_linearVelocity.y+=b1.m_invMass*P1Y;b1.m_angularVelocity+=b1.m_invI*(r1X*P1Y-r1Y*P1X);} -if(this.m_limitState2==b2Joint.e_atUpperLimit) -{v2X=b2.m_linearVelocity.x+(-b2.m_angularVelocity*r2Y);v2Y=b2.m_linearVelocity.y+(b2.m_angularVelocity*r2X);Cdot=-(this.m_u2.x*v2X+this.m_u2.y*v2Y);impulse=-this.m_limitMass2*Cdot;oldLimitImpulse=this.m_limitImpulse2;this.m_limitImpulse2=b2Math.b2Max(0.0,this.m_limitImpulse2+impulse);impulse=this.m_limitImpulse2-oldLimitImpulse;P2X=-impulse*this.m_u2.x;P2Y=-impulse*this.m_u2.y;b2.m_linearVelocity.x+=b2.m_invMass*P2X;b2.m_linearVelocity.y+=b2.m_invMass*P2Y;b2.m_angularVelocity+=b2.m_invI*(r2X*P2Y-r2Y*P2X);}},SolvePositionConstraints:function(){var b1=this.m_body1;var b2=this.m_body2;var tMat;var s1X=this.m_ground.m_position.x+this.m_groundAnchor1.x;var s1Y=this.m_ground.m_position.y+this.m_groundAnchor1.y;var s2X=this.m_ground.m_position.x+this.m_groundAnchor2.x;var s2Y=this.m_ground.m_position.y+this.m_groundAnchor2.y;var r1X;var r1Y;var r2X;var r2Y;var p1X;var p1Y;var p2X;var p2Y;var length1;var length2;var C;var impulse;var oldLimitPositionImpulse;var linearError=0.0;{tMat=b1.m_R;r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;p1X=b1.m_position.x+r1X;p1Y=b1.m_position.y+r1Y;p2X=b2.m_position.x+r2X;p2Y=b2.m_position.y+r2Y;this.m_u1.Set(p1X-s1X,p1Y-s1Y);this.m_u2.Set(p2X-s2X,p2Y-s2Y);length1=this.m_u1.Length();length2=this.m_u2.Length();if(length1>b2Settings.b2_linearSlop) -{this.m_u1.Multiply(1.0/length1);} -else -{this.m_u1.SetZero();} -if(length2>b2Settings.b2_linearSlop) -{this.m_u2.Multiply(1.0/length2);} -else -{this.m_u2.SetZero();} -C=this.m_constant-length1-this.m_ratio*length2;linearError=b2Math.b2Max(linearError,Math.abs(C));C=b2Math.b2Clamp(C,-b2Settings.b2_maxLinearCorrection,b2Settings.b2_maxLinearCorrection);impulse=-this.m_pulleyMass*C;p1X=-impulse*this.m_u1.x;p1Y=-impulse*this.m_u1.y;p2X=-this.m_ratio*impulse*this.m_u2.x;p2Y=-this.m_ratio*impulse*this.m_u2.y;b1.m_position.x+=b1.m_invMass*p1X;b1.m_position.y+=b1.m_invMass*p1Y;b1.m_rotation+=b1.m_invI*(r1X*p1Y-r1Y*p1X);b2.m_position.x+=b2.m_invMass*p2X;b2.m_position.y+=b2.m_invMass*p2Y;b2.m_rotation+=b2.m_invI*(r2X*p2Y-r2Y*p2X);b1.m_R.Set(b1.m_rotation);b2.m_R.Set(b2.m_rotation);} -if(this.m_limitState1==b2Joint.e_atUpperLimit) -{tMat=b1.m_R;r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;p1X=b1.m_position.x+r1X;p1Y=b1.m_position.y+r1Y;this.m_u1.Set(p1X-s1X,p1Y-s1Y);length1=this.m_u1.Length();if(length1>b2Settings.b2_linearSlop) -{this.m_u1.x*=1.0/length1;this.m_u1.y*=1.0/length1;} -else -{this.m_u1.SetZero();} -C=this.m_maxLength1-length1;linearError=b2Math.b2Max(linearError,-C);C=b2Math.b2Clamp(C+b2Settings.b2_linearSlop,-b2Settings.b2_maxLinearCorrection,0.0);impulse=-this.m_limitMass1*C;oldLimitPositionImpulse=this.m_limitPositionImpulse1;this.m_limitPositionImpulse1=b2Math.b2Max(0.0,this.m_limitPositionImpulse1+impulse);impulse=this.m_limitPositionImpulse1-oldLimitPositionImpulse;p1X=-impulse*this.m_u1.x;p1Y=-impulse*this.m_u1.y;b1.m_position.x+=b1.m_invMass*p1X;b1.m_position.y+=b1.m_invMass*p1Y;b1.m_rotation+=b1.m_invI*(r1X*p1Y-r1Y*p1X);b1.m_R.Set(b1.m_rotation);} -if(this.m_limitState2==b2Joint.e_atUpperLimit) -{tMat=b2.m_R;r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;p2X=b2.m_position.x+r2X;p2Y=b2.m_position.y+r2Y;this.m_u2.Set(p2X-s2X,p2Y-s2Y);length2=this.m_u2.Length();if(length2>b2Settings.b2_linearSlop) -{this.m_u2.x*=1.0/length2;this.m_u2.y*=1.0/length2;} -else -{this.m_u2.SetZero();} -C=this.m_maxLength2-length2;linearError=b2Math.b2Max(linearError,-C);C=b2Math.b2Clamp(C+b2Settings.b2_linearSlop,-b2Settings.b2_maxLinearCorrection,0.0);impulse=-this.m_limitMass2*C;oldLimitPositionImpulse=this.m_limitPositionImpulse2;this.m_limitPositionImpulse2=b2Math.b2Max(0.0,this.m_limitPositionImpulse2+impulse);impulse=this.m_limitPositionImpulse2-oldLimitPositionImpulse;p2X=-impulse*this.m_u2.x;p2Y=-impulse*this.m_u2.y;b2.m_position.x+=b2.m_invMass*p2X;b2.m_position.y+=b2.m_invMass*p2Y;b2.m_rotation+=b2.m_invI*(r2X*p2Y-r2Y*p2X);b2.m_R.Set(b2.m_rotation);} -return linearError<b2Settings.b2_linearSlop;},m_ground:null,m_groundAnchor1:new b2Vec2(),m_groundAnchor2:new b2Vec2(),m_localAnchor1:new b2Vec2(),m_localAnchor2:new b2Vec2(),m_u1:new b2Vec2(),m_u2:new b2Vec2(),m_constant:null,m_ratio:null,m_maxLength1:null,m_maxLength2:null,m_pulleyMass:null,m_limitMass1:null,m_limitMass2:null,m_pulleyImpulse:null,m_limitImpulse1:null,m_limitImpulse2:null,m_limitPositionImpulse1:null,m_limitPositionImpulse2:null,m_limitState1:0,m_limitState2:0});b2PulleyJoint.b2_minPulleyLength=b2Settings.b2_lengthUnitsPerMeter; -var b2PulleyJointDef=Class.create();Object.extend(b2PulleyJointDef.prototype,b2JointDef.prototype);Object.extend(b2PulleyJointDef.prototype,{initialize:function() -{this.type=b2Joint.e_unknownJoint;this.userData=null;this.body1=null;this.body2=null;this.collideConnected=false;this.groundPoint1=new b2Vec2();this.groundPoint2=new b2Vec2();this.anchorPoint1=new b2Vec2();this.anchorPoint2=new b2Vec2();this.type=b2Joint.e_pulleyJoint;this.groundPoint1.Set(-1.0,1.0);this.groundPoint2.Set(1.0,1.0);this.anchorPoint1.Set(-1.0,0.0);this.anchorPoint2.Set(1.0,0.0);this.maxLength1=0.5*b2PulleyJoint.b2_minPulleyLength;this.maxLength2=0.5*b2PulleyJoint.b2_minPulleyLength;this.ratio=1.0;this.collideConnected=true;},groundPoint1:new b2Vec2(),groundPoint2:new b2Vec2(),anchorPoint1:new b2Vec2(),anchorPoint2:new b2Vec2(),maxLength1:null,maxLength2:null,ratio:null}); -var b2RevoluteJoint=Class.create();Object.extend(b2RevoluteJoint.prototype,b2Joint.prototype);Object.extend(b2RevoluteJoint.prototype,{GetAnchor1:function(){var tMat=this.m_body1.m_R;return new b2Vec2(this.m_body1.m_position.x+(tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y),this.m_body1.m_position.y+(tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y));},GetAnchor2:function(){var tMat=this.m_body2.m_R;return new b2Vec2(this.m_body2.m_position.x+(tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y),this.m_body2.m_position.y+(tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y));},GetJointAngle:function(){return this.m_body2.m_rotation-this.m_body1.m_rotation;},GetJointSpeed:function(){return this.m_body2.m_angularVelocity-this.m_body1.m_angularVelocity;},GetMotorTorque:function(invTimeStep){return invTimeStep*this.m_motorImpulse;},SetMotorSpeed:function(speed) -{this.m_motorSpeed=speed;},SetMotorTorque:function(torque) -{this.m_maxMotorTorque=torque;},GetReactionForce:function(invTimeStep) -{var tVec=this.m_ptpImpulse.Copy();tVec.Multiply(invTimeStep);return tVec;},GetReactionTorque:function(invTimeStep) -{return invTimeStep*this.m_limitImpulse;},initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;this.K=new b2Mat22();this.K1=new b2Mat22();this.K2=new b2Mat22();this.K3=new b2Mat22();this.m_localAnchor1=new b2Vec2();this.m_localAnchor2=new b2Vec2();this.m_ptpImpulse=new b2Vec2();this.m_ptpMass=new b2Mat22();var tMat;var tX;var tY;tMat=this.m_body1.m_R;tX=def.anchorPoint.x-this.m_body1.m_position.x;tY=def.anchorPoint.y-this.m_body1.m_position.y;this.m_localAnchor1.x=tX*tMat.col1.x+tY*tMat.col1.y;this.m_localAnchor1.y=tX*tMat.col2.x+tY*tMat.col2.y;tMat=this.m_body2.m_R;tX=def.anchorPoint.x-this.m_body2.m_position.x;tY=def.anchorPoint.y-this.m_body2.m_position.y;this.m_localAnchor2.x=tX*tMat.col1.x+tY*tMat.col1.y;this.m_localAnchor2.y=tX*tMat.col2.x+tY*tMat.col2.y;this.m_intialAngle=this.m_body2.m_rotation-this.m_body1.m_rotation;this.m_ptpImpulse.Set(0.0,0.0);this.m_motorImpulse=0.0;this.m_limitImpulse=0.0;this.m_limitPositionImpulse=0.0;this.m_lowerAngle=def.lowerAngle;this.m_upperAngle=def.upperAngle;this.m_maxMotorTorque=def.motorTorque;this.m_motorSpeed=def.motorSpeed;this.m_enableLimit=def.enableLimit;this.m_enableMotor=def.enableMotor;},K:new b2Mat22(),K1:new b2Mat22(),K2:new b2Mat22(),K3:new b2Mat22(),PrepareVelocitySolver:function(){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var invMass1=b1.m_invMass;var invMass2=b2.m_invMass;var invI1=b1.m_invI;var invI2=b2.m_invI;this.K1.col1.x=invMass1+invMass2;this.K1.col2.x=0.0;this.K1.col1.y=0.0;this.K1.col2.y=invMass1+invMass2;this.K2.col1.x=invI1*r1Y*r1Y;this.K2.col2.x=-invI1*r1X*r1Y;this.K2.col1.y=-invI1*r1X*r1Y;this.K2.col2.y=invI1*r1X*r1X;this.K3.col1.x=invI2*r2Y*r2Y;this.K3.col2.x=-invI2*r2X*r2Y;this.K3.col1.y=-invI2*r2X*r2Y;this.K3.col2.y=invI2*r2X*r2X;this.K.SetM(this.K1);this.K.AddM(this.K2);this.K.AddM(this.K3);this.K.Invert(this.m_ptpMass);this.m_motorMass=1.0/(invI1+invI2);if(this.m_enableMotor==false) -{this.m_motorImpulse=0.0;} -if(this.m_enableLimit) -{var jointAngle=b2.m_rotation-b1.m_rotation-this.m_intialAngle;if(b2Math.b2Abs(this.m_upperAngle-this.m_lowerAngle)<2.0*b2Settings.b2_angularSlop) -{this.m_limitState=b2Joint.e_equalLimits;} -else if(jointAngle<=this.m_lowerAngle) -{if(this.m_limitState!=b2Joint.e_atLowerLimit) -{this.m_limitImpulse=0.0;} -this.m_limitState=b2Joint.e_atLowerLimit;} -else if(jointAngle>=this.m_upperAngle) -{if(this.m_limitState!=b2Joint.e_atUpperLimit) -{this.m_limitImpulse=0.0;} -this.m_limitState=b2Joint.e_atUpperLimit;} -else -{this.m_limitState=b2Joint.e_inactiveLimit;this.m_limitImpulse=0.0;}} -else -{this.m_limitImpulse=0.0;} -if(b2World.s_enableWarmStarting) -{b1.m_linearVelocity.x-=invMass1*this.m_ptpImpulse.x;b1.m_linearVelocity.y-=invMass1*this.m_ptpImpulse.y;b1.m_angularVelocity-=invI1*((r1X*this.m_ptpImpulse.y-r1Y*this.m_ptpImpulse.x)+this.m_motorImpulse+this.m_limitImpulse);b2.m_linearVelocity.x+=invMass2*this.m_ptpImpulse.x;b2.m_linearVelocity.y+=invMass2*this.m_ptpImpulse.y;b2.m_angularVelocity+=invI2*((r2X*this.m_ptpImpulse.y-r2Y*this.m_ptpImpulse.x)+this.m_motorImpulse+this.m_limitImpulse);} -else{this.m_ptpImpulse.SetZero();this.m_motorImpulse=0.0;this.m_limitImpulse=0.0;} -this.m_limitPositionImpulse=0.0;},SolveVelocityConstraints:function(step){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var oldLimitImpulse;var ptpCdotX=b2.m_linearVelocity.x+(-b2.m_angularVelocity*r2Y)-b1.m_linearVelocity.x-(-b1.m_angularVelocity*r1Y);var ptpCdotY=b2.m_linearVelocity.y+(b2.m_angularVelocity*r2X)-b1.m_linearVelocity.y-(b1.m_angularVelocity*r1X);var ptpImpulseX=-(this.m_ptpMass.col1.x*ptpCdotX+this.m_ptpMass.col2.x*ptpCdotY);var ptpImpulseY=-(this.m_ptpMass.col1.y*ptpCdotX+this.m_ptpMass.col2.y*ptpCdotY);this.m_ptpImpulse.x+=ptpImpulseX;this.m_ptpImpulse.y+=ptpImpulseY;b1.m_linearVelocity.x-=b1.m_invMass*ptpImpulseX;b1.m_linearVelocity.y-=b1.m_invMass*ptpImpulseY;b1.m_angularVelocity-=b1.m_invI*(r1X*ptpImpulseY-r1Y*ptpImpulseX);b2.m_linearVelocity.x+=b2.m_invMass*ptpImpulseX;b2.m_linearVelocity.y+=b2.m_invMass*ptpImpulseY;b2.m_angularVelocity+=b2.m_invI*(r2X*ptpImpulseY-r2Y*ptpImpulseX);if(this.m_enableMotor&&this.m_limitState!=b2Joint.e_equalLimits) -{var motorCdot=b2.m_angularVelocity-b1.m_angularVelocity-this.m_motorSpeed;var motorImpulse=-this.m_motorMass*motorCdot;var oldMotorImpulse=this.m_motorImpulse;this.m_motorImpulse=b2Math.b2Clamp(this.m_motorImpulse+motorImpulse,-step.dt*this.m_maxMotorTorque,step.dt*this.m_maxMotorTorque);motorImpulse=this.m_motorImpulse-oldMotorImpulse;b1.m_angularVelocity-=b1.m_invI*motorImpulse;b2.m_angularVelocity+=b2.m_invI*motorImpulse;} -if(this.m_enableLimit&&this.m_limitState!=b2Joint.e_inactiveLimit) -{var limitCdot=b2.m_angularVelocity-b1.m_angularVelocity;var limitImpulse=-this.m_motorMass*limitCdot;if(this.m_limitState==b2Joint.e_equalLimits) -{this.m_limitImpulse+=limitImpulse;} -else if(this.m_limitState==b2Joint.e_atLowerLimit) -{oldLimitImpulse=this.m_limitImpulse;this.m_limitImpulse=b2Math.b2Max(this.m_limitImpulse+limitImpulse,0.0);limitImpulse=this.m_limitImpulse-oldLimitImpulse;} -else if(this.m_limitState==b2Joint.e_atUpperLimit) -{oldLimitImpulse=this.m_limitImpulse;this.m_limitImpulse=b2Math.b2Min(this.m_limitImpulse+limitImpulse,0.0);limitImpulse=this.m_limitImpulse-oldLimitImpulse;} -b1.m_angularVelocity-=b1.m_invI*limitImpulse;b2.m_angularVelocity+=b2.m_invI*limitImpulse;}},SolvePositionConstraints:function(){var oldLimitImpulse;var limitC;var b1=this.m_body1;var b2=this.m_body2;var positionError=0.0;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var p1X=b1.m_position.x+r1X;var p1Y=b1.m_position.y+r1Y;var p2X=b2.m_position.x+r2X;var p2Y=b2.m_position.y+r2Y;var ptpCX=p2X-p1X;var ptpCY=p2Y-p1Y;positionError=Math.sqrt(ptpCX*ptpCX+ptpCY*ptpCY);var invMass1=b1.m_invMass;var invMass2=b2.m_invMass;var invI1=b1.m_invI;var invI2=b2.m_invI;this.K1.col1.x=invMass1+invMass2;this.K1.col2.x=0.0;this.K1.col1.y=0.0;this.K1.col2.y=invMass1+invMass2;this.K2.col1.x=invI1*r1Y*r1Y;this.K2.col2.x=-invI1*r1X*r1Y;this.K2.col1.y=-invI1*r1X*r1Y;this.K2.col2.y=invI1*r1X*r1X;this.K3.col1.x=invI2*r2Y*r2Y;this.K3.col2.x=-invI2*r2X*r2Y;this.K3.col1.y=-invI2*r2X*r2Y;this.K3.col2.y=invI2*r2X*r2X;this.K.SetM(this.K1);this.K.AddM(this.K2);this.K.AddM(this.K3);this.K.Solve(b2RevoluteJoint.tImpulse,-ptpCX,-ptpCY);var impulseX=b2RevoluteJoint.tImpulse.x;var impulseY=b2RevoluteJoint.tImpulse.y;b1.m_position.x-=b1.m_invMass*impulseX;b1.m_position.y-=b1.m_invMass*impulseY;b1.m_rotation-=b1.m_invI*(r1X*impulseY-r1Y*impulseX);b1.m_R.Set(b1.m_rotation);b2.m_position.x+=b2.m_invMass*impulseX;b2.m_position.y+=b2.m_invMass*impulseY;b2.m_rotation+=b2.m_invI*(r2X*impulseY-r2Y*impulseX);b2.m_R.Set(b2.m_rotation);var angularError=0.0;if(this.m_enableLimit&&this.m_limitState!=b2Joint.e_inactiveLimit) -{var angle=b2.m_rotation-b1.m_rotation-this.m_intialAngle;var limitImpulse=0.0;if(this.m_limitState==b2Joint.e_equalLimits) -{limitC=b2Math.b2Clamp(angle,-b2Settings.b2_maxAngularCorrection,b2Settings.b2_maxAngularCorrection);limitImpulse=-this.m_motorMass*limitC;angularError=b2Math.b2Abs(limitC);} -else if(this.m_limitState==b2Joint.e_atLowerLimit) -{limitC=angle-this.m_lowerAngle;angularError=b2Math.b2Max(0.0,-limitC);limitC=b2Math.b2Clamp(limitC+b2Settings.b2_angularSlop,-b2Settings.b2_maxAngularCorrection,0.0);limitImpulse=-this.m_motorMass*limitC;oldLimitImpulse=this.m_limitPositionImpulse;this.m_limitPositionImpulse=b2Math.b2Max(this.m_limitPositionImpulse+limitImpulse,0.0);limitImpulse=this.m_limitPositionImpulse-oldLimitImpulse;} -else if(this.m_limitState==b2Joint.e_atUpperLimit) -{limitC=angle-this.m_upperAngle;angularError=b2Math.b2Max(0.0,limitC);limitC=b2Math.b2Clamp(limitC-b2Settings.b2_angularSlop,0.0,b2Settings.b2_maxAngularCorrection);limitImpulse=-this.m_motorMass*limitC;oldLimitImpulse=this.m_limitPositionImpulse;this.m_limitPositionImpulse=b2Math.b2Min(this.m_limitPositionImpulse+limitImpulse,0.0);limitImpulse=this.m_limitPositionImpulse-oldLimitImpulse;} -b1.m_rotation-=b1.m_invI*limitImpulse;b1.m_R.Set(b1.m_rotation);b2.m_rotation+=b2.m_invI*limitImpulse;b2.m_R.Set(b2.m_rotation);} -return positionError<=b2Settings.b2_linearSlop&&angularError<=b2Settings.b2_angularSlop;},m_localAnchor1:new b2Vec2(),m_localAnchor2:new b2Vec2(),m_ptpImpulse:new b2Vec2(),m_motorImpulse:null,m_limitImpulse:null,m_limitPositionImpulse:null,m_ptpMass:new b2Mat22(),m_motorMass:null,m_intialAngle:null,m_lowerAngle:null,m_upperAngle:null,m_maxMotorTorque:null,m_motorSpeed:null,m_enableLimit:null,m_enableMotor:null,m_limitState:0});b2RevoluteJoint.tImpulse=new b2Vec2(); -var b2RevoluteJointDef=Class.create();Object.extend(b2RevoluteJointDef.prototype,b2JointDef.prototype);Object.extend(b2RevoluteJointDef.prototype,{initialize:function() -{this.type=b2Joint.e_unknownJoint;this.userData=null;this.body1=null;this.body2=null;this.collideConnected=false;this.type=b2Joint.e_revoluteJoint;this.anchorPoint=new b2Vec2(0.0,0.0);this.lowerAngle=0.0;this.upperAngle=0.0;this.motorTorque=0.0;this.motorSpeed=0.0;this.enableLimit=false;this.enableMotor=false;},anchorPoint:null,lowerAngle:null,upperAngle:null,motorTorque:null,motorSpeed:null,enableLimit:null,enableMotor:null}); +/* + * Box2Djs (port of Box2DFlash 1.4.3.1) - http://box2d-js.sourceforge.net/ + * Single-filed and jsmined ( http://code.google.com/p/jsmin-php/ ) by Mr.doob + */ + +var b2Settings=Class.create();b2Settings.prototype={initialize:function(){}} +b2Settings.USHRT_MAX=0x0000ffff;b2Settings.b2_pi=Math.PI;b2Settings.b2_massUnitsPerKilogram=1.0;b2Settings.b2_timeUnitsPerSecond=1.0;b2Settings.b2_lengthUnitsPerMeter=30.0;b2Settings.b2_maxManifoldPoints=2;b2Settings.b2_maxShapesPerBody=64;b2Settings.b2_maxPolyVertices=8;b2Settings.b2_maxProxies=1024;b2Settings.b2_maxPairs=8*b2Settings.b2_maxProxies;b2Settings.b2_linearSlop=0.005*b2Settings.b2_lengthUnitsPerMeter;b2Settings.b2_angularSlop=2.0/180.0*b2Settings.b2_pi;b2Settings.b2_velocityThreshold=1.0*b2Settings.b2_lengthUnitsPerMeter/b2Settings.b2_timeUnitsPerSecond;b2Settings.b2_maxLinearCorrection=0.2*b2Settings.b2_lengthUnitsPerMeter;b2Settings.b2_maxAngularCorrection=8.0/180.0*b2Settings.b2_pi;b2Settings.b2_contactBaumgarte=0.2;b2Settings.b2_timeToSleep=0.5*b2Settings.b2_timeUnitsPerSecond;b2Settings.b2_linearSleepTolerance=0.01*b2Settings.b2_lengthUnitsPerMeter/b2Settings.b2_timeUnitsPerSecond;b2Settings.b2_angularSleepTolerance=2.0/180.0/b2Settings.b2_timeUnitsPerSecond;b2Settings.b2Assert=function(a) +{if(!a){var nullVec;nullVec.x++;}}; +var b2Vec2=Class.create();b2Vec2.prototype={initialize:function(x_,y_){this.x=x_;this.y=y_;},SetZero:function(){this.x=0.0;this.y=0.0;},Set:function(x_,y_){this.x=x_;this.y=y_;},SetV:function(v){this.x=v.x;this.y=v.y;},Negative:function(){return new b2Vec2(-this.x,-this.y);},Copy:function(){return new b2Vec2(this.x,this.y);},Add:function(v) +{this.x+=v.x;this.y+=v.y;},Subtract:function(v) +{this.x-=v.x;this.y-=v.y;},Multiply:function(a) +{this.x*=a;this.y*=a;},MulM:function(A) +{var tX=this.x;this.x=A.col1.x*tX+A.col2.x*this.y;this.y=A.col1.y*tX+A.col2.y*this.y;},MulTM:function(A) +{var tX=b2Math.b2Dot(this,A.col1);this.y=b2Math.b2Dot(this,A.col2);this.x=tX;},CrossVF:function(s) +{var tX=this.x;this.x=s*this.y;this.y=-s*tX;},CrossFV:function(s) +{var tX=this.x;this.x=-s*this.y;this.y=s*tX;},MinV:function(b) +{this.x=this.x<b.x?this.x:b.x;this.y=this.y<b.y?this.y:b.y;},MaxV:function(b) +{this.x=this.x>b.x?this.x:b.x;this.y=this.y>b.y?this.y:b.y;},Abs:function() +{this.x=Math.abs(this.x);this.y=Math.abs(this.y);},Length:function() +{return Math.sqrt(this.x*this.x+this.y*this.y);},Normalize:function() +{var length=this.Length();if(length<Number.MIN_VALUE) +{return 0.0;} +var invLength=1.0/length;this.x*=invLength;this.y*=invLength;return length;},IsValid:function() +{return b2Math.b2IsValid(this.x)&&b2Math.b2IsValid(this.y);},x:null,y:null};b2Vec2.Make=function(x_,y_) +{return new b2Vec2(x_,y_);}; +var b2Mat22=Class.create();b2Mat22.prototype={initialize:function(angle,c1,c2) +{if(angle==null)angle=0;this.col1=new b2Vec2();this.col2=new b2Vec2();if(c1!=null&&c2!=null){this.col1.SetV(c1);this.col2.SetV(c2);} +else{var c=Math.cos(angle);var s=Math.sin(angle);this.col1.x=c;this.col2.x=-s;this.col1.y=s;this.col2.y=c;}},Set:function(angle) +{var c=Math.cos(angle);var s=Math.sin(angle);this.col1.x=c;this.col2.x=-s;this.col1.y=s;this.col2.y=c;},SetVV:function(c1,c2) +{this.col1.SetV(c1);this.col2.SetV(c2);},Copy:function(){return new b2Mat22(0,this.col1,this.col2);},SetM:function(m) +{this.col1.SetV(m.col1);this.col2.SetV(m.col2);},AddM:function(m) +{this.col1.x+=m.col1.x;this.col1.y+=m.col1.y;this.col2.x+=m.col2.x;this.col2.y+=m.col2.y;},SetIdentity:function() +{this.col1.x=1.0;this.col2.x=0.0;this.col1.y=0.0;this.col2.y=1.0;},SetZero:function() +{this.col1.x=0.0;this.col2.x=0.0;this.col1.y=0.0;this.col2.y=0.0;},Invert:function(out) +{var a=this.col1.x;var b=this.col2.x;var c=this.col1.y;var d=this.col2.y;var det=a*d-b*c;det=1.0/det;out.col1.x=det*d;out.col2.x=-det*b;out.col1.y=-det*c;out.col2.y=det*a;return out;},Solve:function(out,bX,bY) +{var a11=this.col1.x;var a12=this.col2.x;var a21=this.col1.y;var a22=this.col2.y;var det=a11*a22-a12*a21;det=1.0/det;out.x=det*(a22*bX-a12*bY);out.y=det*(a11*bY-a21*bX);return out;},Abs:function() +{this.col1.Abs();this.col2.Abs();},col1:new b2Vec2(),col2:new b2Vec2()}; +var b2Math=Class.create();b2Math.prototype={initialize:function(){}} +b2Math.b2IsValid=function(x) +{return isFinite(x);};b2Math.b2Dot=function(a,b) +{return a.x*b.x+a.y*b.y;};b2Math.b2CrossVV=function(a,b) +{return a.x*b.y-a.y*b.x;};b2Math.b2CrossVF=function(a,s) +{var v=new b2Vec2(s*a.y,-s*a.x);return v;};b2Math.b2CrossFV=function(s,a) +{var v=new b2Vec2(-s*a.y,s*a.x);return v;};b2Math.b2MulMV=function(A,v) +{var u=new b2Vec2(A.col1.x*v.x+A.col2.x*v.y,A.col1.y*v.x+A.col2.y*v.y);return u;};b2Math.b2MulTMV=function(A,v) +{var u=new b2Vec2(b2Math.b2Dot(v,A.col1),b2Math.b2Dot(v,A.col2));return u;};b2Math.AddVV=function(a,b) +{var v=new b2Vec2(a.x+b.x,a.y+b.y);return v;};b2Math.SubtractVV=function(a,b) +{var v=new b2Vec2(a.x-b.x,a.y-b.y);return v;};b2Math.MulFV=function(s,a) +{var v=new b2Vec2(s*a.x,s*a.y);return v;};b2Math.AddMM=function(A,B) +{var C=new b2Mat22(0,b2Math.AddVV(A.col1,B.col1),b2Math.AddVV(A.col2,B.col2));return C;};b2Math.b2MulMM=function(A,B) +{var C=new b2Mat22(0,b2Math.b2MulMV(A,B.col1),b2Math.b2MulMV(A,B.col2));return C;};b2Math.b2MulTMM=function(A,B) +{var c1=new b2Vec2(b2Math.b2Dot(A.col1,B.col1),b2Math.b2Dot(A.col2,B.col1));var c2=new b2Vec2(b2Math.b2Dot(A.col1,B.col2),b2Math.b2Dot(A.col2,B.col2));var C=new b2Mat22(0,c1,c2);return C;};b2Math.b2Abs=function(a) +{return a>0.0?a:-a;};b2Math.b2AbsV=function(a) +{var b=new b2Vec2(b2Math.b2Abs(a.x),b2Math.b2Abs(a.y));return b;};b2Math.b2AbsM=function(A) +{var B=new b2Mat22(0,b2Math.b2AbsV(A.col1),b2Math.b2AbsV(A.col2));return B;};b2Math.b2Min=function(a,b) +{return a<b?a:b;};b2Math.b2MinV=function(a,b) +{var c=new b2Vec2(b2Math.b2Min(a.x,b.x),b2Math.b2Min(a.y,b.y));return c;};b2Math.b2Max=function(a,b) +{return a>b?a:b;};b2Math.b2MaxV=function(a,b) +{var c=new b2Vec2(b2Math.b2Max(a.x,b.x),b2Math.b2Max(a.y,b.y));return c;};b2Math.b2Clamp=function(a,low,high) +{return b2Math.b2Max(low,b2Math.b2Min(a,high));};b2Math.b2ClampV=function(a,low,high) +{return b2Math.b2MaxV(low,b2Math.b2MinV(a,high));};b2Math.b2Swap=function(a,b) +{var tmp=a[0];a[0]=b[0];b[0]=tmp;};b2Math.b2Random=function() +{return Math.random()*2-1;};b2Math.b2NextPowerOfTwo=function(x) +{x|=(x>>1)&0x7FFFFFFF;x|=(x>>2)&0x3FFFFFFF;x|=(x>>4)&0x0FFFFFFF;x|=(x>>8)&0x00FFFFFF;x|=(x>>16)&0x0000FFFF;return x+1;};b2Math.b2IsPowerOfTwo=function(x) +{var result=x>0&&(x&(x-1))==0;return result;};b2Math.tempVec2=new b2Vec2();b2Math.tempVec3=new b2Vec2();b2Math.tempVec4=new b2Vec2();b2Math.tempVec5=new b2Vec2();b2Math.tempMat=new b2Mat22(); +var b2AABB=Class.create();b2AABB.prototype={IsValid:function(){var dX=this.maxVertex.x;var dY=this.maxVertex.y;dX=this.maxVertex.x;dY=this.maxVertex.y;dX-=this.minVertex.x;dY-=this.minVertex.y;var valid=dX>=0.0&&dY>=0.0;valid=valid&&this.minVertex.IsValid()&&this.maxVertex.IsValid();return valid;},minVertex:new b2Vec2(),maxVertex:new b2Vec2(),initialize:function(){this.minVertex=new b2Vec2();this.maxVertex=new b2Vec2();}}; +var b2Bound=Class.create();b2Bound.prototype={IsLower:function(){return(this.value&1)==0;},IsUpper:function(){return(this.value&1)==1;},Swap:function(b){var tempValue=this.value;var tempProxyId=this.proxyId;var tempStabbingCount=this.stabbingCount;this.value=b.value;this.proxyId=b.proxyId;this.stabbingCount=b.stabbingCount;b.value=tempValue;b.proxyId=tempProxyId;b.stabbingCount=tempStabbingCount;},value:0,proxyId:0,stabbingCount:0,initialize:function(){}} + +var b2BoundValues=Class.create();b2BoundValues.prototype={lowerValues:[0,0],upperValues:[0,0],initialize:function(){this.lowerValues=[0,0];this.upperValues=[0,0];}} + +var b2Pair=Class.create();b2Pair.prototype={SetBuffered:function(){this.status|=b2Pair.e_pairBuffered;},ClearBuffered:function(){this.status&=~b2Pair.e_pairBuffered;},IsBuffered:function(){return(this.status&b2Pair.e_pairBuffered)==b2Pair.e_pairBuffered;},SetRemoved:function(){this.status|=b2Pair.e_pairRemoved;},ClearRemoved:function(){this.status&=~b2Pair.e_pairRemoved;},IsRemoved:function(){return(this.status&b2Pair.e_pairRemoved)==b2Pair.e_pairRemoved;},SetFinal:function(){this.status|=b2Pair.e_pairFinal;},IsFinal:function(){return(this.status&b2Pair.e_pairFinal)==b2Pair.e_pairFinal;},userData:null,proxyId1:0,proxyId2:0,next:0,status:0,initialize:function(){}};b2Pair.b2_nullPair=b2Settings.USHRT_MAX;b2Pair.b2_nullProxy=b2Settings.USHRT_MAX;b2Pair.b2_tableCapacity=b2Settings.b2_maxPairs;b2Pair.b2_tableMask=b2Pair.b2_tableCapacity-1;b2Pair.e_pairBuffered=0x0001;b2Pair.e_pairRemoved=0x0002;b2Pair.e_pairFinal=0x0004; +var b2PairCallback=Class.create();b2PairCallback.prototype={PairAdded:function(proxyUserData1,proxyUserData2){return null},PairRemoved:function(proxyUserData1,proxyUserData2,pairUserData){},initialize:function(){}}; +var b2BufferedPair=Class.create();b2BufferedPair.prototype={proxyId1:0,proxyId2:0,initialize:function(){}} + +var b2PairManager=Class.create();b2PairManager.prototype={initialize:function(){var i=0;this.m_hashTable=new Array(b2Pair.b2_tableCapacity);for(i=0;i<b2Pair.b2_tableCapacity;++i) +{this.m_hashTable[i]=b2Pair.b2_nullPair;} +this.m_pairs=new Array(b2Settings.b2_maxPairs);for(i=0;i<b2Settings.b2_maxPairs;++i) +{this.m_pairs[i]=new b2Pair();} +this.m_pairBuffer=new Array(b2Settings.b2_maxPairs);for(i=0;i<b2Settings.b2_maxPairs;++i) +{this.m_pairBuffer[i]=new b2BufferedPair();} +for(i=0;i<b2Settings.b2_maxPairs;++i) +{this.m_pairs[i].proxyId1=b2Pair.b2_nullProxy;this.m_pairs[i].proxyId2=b2Pair.b2_nullProxy;this.m_pairs[i].userData=null;this.m_pairs[i].status=0;this.m_pairs[i].next=(i+1);} +this.m_pairs[b2Settings.b2_maxPairs-1].next=b2Pair.b2_nullPair;this.m_pairCount=0;},Initialize:function(broadPhase,callback){this.m_broadPhase=broadPhase;this.m_callback=callback;},AddBufferedPair:function(proxyId1,proxyId2){var pair=this.AddPair(proxyId1,proxyId2);if(pair.IsBuffered()==false) +{pair.SetBuffered();this.m_pairBuffer[this.m_pairBufferCount].proxyId1=pair.proxyId1;this.m_pairBuffer[this.m_pairBufferCount].proxyId2=pair.proxyId2;++this.m_pairBufferCount;} +pair.ClearRemoved();if(b2BroadPhase.s_validate) +{this.ValidateBuffer();}},RemoveBufferedPair:function(proxyId1,proxyId2){var pair=this.Find(proxyId1,proxyId2);if(pair==null) +{return;} +if(pair.IsBuffered()==false) +{pair.SetBuffered();this.m_pairBuffer[this.m_pairBufferCount].proxyId1=pair.proxyId1;this.m_pairBuffer[this.m_pairBufferCount].proxyId2=pair.proxyId2;++this.m_pairBufferCount;} +pair.SetRemoved();if(b2BroadPhase.s_validate) +{this.ValidateBuffer();}},Commit:function(){var i=0;var removeCount=0;var proxies=this.m_broadPhase.m_proxyPool;for(i=0;i<this.m_pairBufferCount;++i) +{var pair=this.Find(this.m_pairBuffer[i].proxyId1,this.m_pairBuffer[i].proxyId2);pair.ClearBuffered();var proxy1=proxies[pair.proxyId1];var proxy2=proxies[pair.proxyId2];if(pair.IsRemoved()) +{if(pair.IsFinal()==true) +{this.m_callback.PairRemoved(proxy1.userData,proxy2.userData,pair.userData);} +this.m_pairBuffer[removeCount].proxyId1=pair.proxyId1;this.m_pairBuffer[removeCount].proxyId2=pair.proxyId2;++removeCount;} +else +{if(pair.IsFinal()==false) +{pair.userData=this.m_callback.PairAdded(proxy1.userData,proxy2.userData);pair.SetFinal();}}} +for(i=0;i<removeCount;++i) +{this.RemovePair(this.m_pairBuffer[i].proxyId1,this.m_pairBuffer[i].proxyId2);} +this.m_pairBufferCount=0;if(b2BroadPhase.s_validate) +{this.ValidateTable();}},AddPair:function(proxyId1,proxyId2){if(proxyId1>proxyId2){var temp=proxyId1;proxyId1=proxyId2;proxyId2=temp;} +var hash=b2PairManager.Hash(proxyId1,proxyId2)&b2Pair.b2_tableMask;var pair=pair=this.FindHash(proxyId1,proxyId2,hash);if(pair!=null) +{return pair;} +var pIndex=this.m_freePair;pair=this.m_pairs[pIndex];this.m_freePair=pair.next;pair.proxyId1=proxyId1;pair.proxyId2=proxyId2;pair.status=0;pair.userData=null;pair.next=this.m_hashTable[hash];this.m_hashTable[hash]=pIndex;++this.m_pairCount;return pair;},RemovePair:function(proxyId1,proxyId2){if(proxyId1>proxyId2){var temp=proxyId1;proxyId1=proxyId2;proxyId2=temp;} +var hash=b2PairManager.Hash(proxyId1,proxyId2)&b2Pair.b2_tableMask;var node=this.m_hashTable[hash];var pNode=null;while(node!=b2Pair.b2_nullPair) +{if(b2PairManager.Equals(this.m_pairs[node],proxyId1,proxyId2)) +{var index=node;if(pNode){pNode.next=this.m_pairs[node].next;} +else{this.m_hashTable[hash]=this.m_pairs[node].next;} +var pair=this.m_pairs[index];var userData=pair.userData;pair.next=this.m_freePair;pair.proxyId1=b2Pair.b2_nullProxy;pair.proxyId2=b2Pair.b2_nullProxy;pair.userData=null;pair.status=0;this.m_freePair=index;--this.m_pairCount;return userData;} +else +{pNode=this.m_pairs[node];node=pNode.next;}} +return null;},Find:function(proxyId1,proxyId2){if(proxyId1>proxyId2){var temp=proxyId1;proxyId1=proxyId2;proxyId2=temp;} +var hash=b2PairManager.Hash(proxyId1,proxyId2)&b2Pair.b2_tableMask;return this.FindHash(proxyId1,proxyId2,hash);},FindHash:function(proxyId1,proxyId2,hash){var index=this.m_hashTable[hash];while(index!=b2Pair.b2_nullPair&&b2PairManager.Equals(this.m_pairs[index],proxyId1,proxyId2)==false) +{index=this.m_pairs[index].next;} +if(index==b2Pair.b2_nullPair) +{return null;} +return this.m_pairs[index];},ValidateBuffer:function(){},ValidateTable:function(){},m_broadPhase:null,m_callback:null,m_pairs:null,m_freePair:0,m_pairCount:0,m_pairBuffer:null,m_pairBufferCount:0,m_hashTable:null};b2PairManager.Hash=function(proxyId1,proxyId2) +{var key=((proxyId2<<16)&0xffff0000)|proxyId1;key=~key+((key<<15)&0xFFFF8000);key=key^((key>>12)&0x000fffff);key=key+((key<<2)&0xFFFFFFFC);key=key^((key>>4)&0x0fffffff);key=key*2057;key=key^((key>>16)&0x0000ffff);return key;};b2PairManager.Equals=function(pair,proxyId1,proxyId2) +{return(pair.proxyId1==proxyId1&&pair.proxyId2==proxyId2);};b2PairManager.EqualsPair=function(pair1,pair2) +{return pair1.proxyId1==pair2.proxyId1&&pair1.proxyId2==pair2.proxyId2;}; +var b2BroadPhase=Class.create();b2BroadPhase.prototype={initialize:function(worldAABB,callback){this.m_pairManager=new b2PairManager();this.m_proxyPool=new Array(b2Settings.b2_maxPairs);this.m_bounds=new Array(2*b2Settings.b2_maxProxies);this.m_queryResults=new Array(b2Settings.b2_maxProxies);this.m_quantizationFactor=new b2Vec2();var i=0;this.m_pairManager.Initialize(this,callback);this.m_worldAABB=worldAABB;this.m_proxyCount=0;for(i=0;i<b2Settings.b2_maxProxies;i++){this.m_queryResults[i]=0;} +this.m_bounds=new Array(2);for(i=0;i<2;i++){this.m_bounds[i]=new Array(2*b2Settings.b2_maxProxies);for(var j=0;j<2*b2Settings.b2_maxProxies;j++){this.m_bounds[i][j]=new b2Bound();}} +var dX=worldAABB.maxVertex.x;var dY=worldAABB.maxVertex.y;dX-=worldAABB.minVertex.x;dY-=worldAABB.minVertex.y;this.m_quantizationFactor.x=b2Settings.USHRT_MAX/dX;this.m_quantizationFactor.y=b2Settings.USHRT_MAX/dY;var tProxy;for(i=0;i<b2Settings.b2_maxProxies-1;++i) +{tProxy=new b2Proxy();this.m_proxyPool[i]=tProxy;tProxy.SetNext(i+1);tProxy.timeStamp=0;tProxy.overlapCount=b2BroadPhase.b2_invalid;tProxy.userData=null;} +tProxy=new b2Proxy();this.m_proxyPool[b2Settings.b2_maxProxies-1]=tProxy;tProxy.SetNext(b2Pair.b2_nullProxy);tProxy.timeStamp=0;tProxy.overlapCount=b2BroadPhase.b2_invalid;tProxy.userData=null;this.m_freeProxy=0;this.m_timeStamp=1;this.m_queryResultCount=0;},InRange:function(aabb){var dX;var dY;var d2X;var d2Y;dX=aabb.minVertex.x;dY=aabb.minVertex.y;dX-=this.m_worldAABB.maxVertex.x;dY-=this.m_worldAABB.maxVertex.y;d2X=this.m_worldAABB.minVertex.x;d2Y=this.m_worldAABB.minVertex.y;d2X-=aabb.maxVertex.x;d2Y-=aabb.maxVertex.y;dX=b2Math.b2Max(dX,d2X);dY=b2Math.b2Max(dY,d2Y);return b2Math.b2Max(dX,dY)<0.0;},GetProxy:function(proxyId){if(proxyId==b2Pair.b2_nullProxy||this.m_proxyPool[proxyId].IsValid()==false) +{return null;} +return this.m_proxyPool[proxyId];},CreateProxy:function(aabb,userData){var index=0;var proxy;var proxyId=this.m_freeProxy;proxy=this.m_proxyPool[proxyId];this.m_freeProxy=proxy.GetNext();proxy.overlapCount=0;proxy.userData=userData;var boundCount=2*this.m_proxyCount;var lowerValues=new Array();var upperValues=new Array();this.ComputeBounds(lowerValues,upperValues,aabb);for(var axis=0;axis<2;++axis) +{var bounds=this.m_bounds[axis];var lowerIndex=0;var upperIndex=0;var lowerIndexOut=[lowerIndex];var upperIndexOut=[upperIndex];this.Query(lowerIndexOut,upperIndexOut,lowerValues[axis],upperValues[axis],bounds,boundCount,axis);lowerIndex=lowerIndexOut[0];upperIndex=upperIndexOut[0];var tArr=new Array();var j=0;var tEnd=boundCount-upperIndex +var tBound1;var tBound2;for(j=0;j<tEnd;j++){tArr[j]=new b2Bound();tBound1=tArr[j];tBound2=bounds[upperIndex+j];tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} +tEnd=tArr.length;var tIndex=upperIndex+2;for(j=0;j<tEnd;j++){tBound2=tArr[j];tBound1=bounds[tIndex+j] +tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} +tArr=new Array();tEnd=upperIndex-lowerIndex;for(j=0;j<tEnd;j++){tArr[j]=new b2Bound();tBound1=tArr[j];tBound2=bounds[lowerIndex+j];tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} +tEnd=tArr.length;tIndex=lowerIndex+1;for(j=0;j<tEnd;j++){tBound2=tArr[j];tBound1=bounds[tIndex+j] +tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} +++upperIndex;bounds[lowerIndex].value=lowerValues[axis];bounds[lowerIndex].proxyId=proxyId;bounds[upperIndex].value=upperValues[axis];bounds[upperIndex].proxyId=proxyId;bounds[lowerIndex].stabbingCount=lowerIndex==0?0:bounds[lowerIndex-1].stabbingCount;bounds[upperIndex].stabbingCount=bounds[upperIndex-1].stabbingCount;for(index=lowerIndex;index<upperIndex;++index) +{bounds[index].stabbingCount++;} +for(index=lowerIndex;index<boundCount+2;++index) +{var proxy2=this.m_proxyPool[bounds[index].proxyId];if(bounds[index].IsLower()) +{proxy2.lowerBounds[axis]=index;} +else +{proxy2.upperBounds[axis]=index;}}} +++this.m_proxyCount;for(var i=0;i<this.m_queryResultCount;++i) +{this.m_pairManager.AddBufferedPair(proxyId,this.m_queryResults[i]);} +this.m_pairManager.Commit();this.m_queryResultCount=0;this.IncrementTimeStamp();return proxyId;},DestroyProxy:function(proxyId){var proxy=this.m_proxyPool[proxyId];var boundCount=2*this.m_proxyCount;for(var axis=0;axis<2;++axis) +{var bounds=this.m_bounds[axis];var lowerIndex=proxy.lowerBounds[axis];var upperIndex=proxy.upperBounds[axis];var lowerValue=bounds[lowerIndex].value;var upperValue=bounds[upperIndex].value;var tArr=new Array();var j=0;var tEnd=upperIndex-lowerIndex-1;var tBound1;var tBound2;for(j=0;j<tEnd;j++){tArr[j]=new b2Bound();tBound1=tArr[j];tBound2=bounds[lowerIndex+1+j];tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} +tEnd=tArr.length;var tIndex=lowerIndex;for(j=0;j<tEnd;j++){tBound2=tArr[j];tBound1=bounds[tIndex+j] +tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} +tArr=new Array();tEnd=boundCount-upperIndex-1;for(j=0;j<tEnd;j++){tArr[j]=new b2Bound();tBound1=tArr[j];tBound2=bounds[upperIndex+1+j];tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} +tEnd=tArr.length;tIndex=upperIndex-1;for(j=0;j<tEnd;j++){tBound2=tArr[j];tBound1=bounds[tIndex+j] +tBound1.value=tBound2.value;tBound1.proxyId=tBound2.proxyId;tBound1.stabbingCount=tBound2.stabbingCount;} +tEnd=boundCount-2;for(var index=lowerIndex;index<tEnd;++index) +{var proxy2=this.m_proxyPool[bounds[index].proxyId];if(bounds[index].IsLower()) +{proxy2.lowerBounds[axis]=index;} +else +{proxy2.upperBounds[axis]=index;}} +tEnd=upperIndex-1;for(var index2=lowerIndex;index2<tEnd;++index2) +{bounds[index2].stabbingCount--;} +this.Query([0],[0],lowerValue,upperValue,bounds,boundCount-2,axis);} +for(var i=0;i<this.m_queryResultCount;++i) +{this.m_pairManager.RemoveBufferedPair(proxyId,this.m_queryResults[i]);} +this.m_pairManager.Commit();this.m_queryResultCount=0;this.IncrementTimeStamp();proxy.userData=null;proxy.overlapCount=b2BroadPhase.b2_invalid;proxy.lowerBounds[0]=b2BroadPhase.b2_invalid;proxy.lowerBounds[1]=b2BroadPhase.b2_invalid;proxy.upperBounds[0]=b2BroadPhase.b2_invalid;proxy.upperBounds[1]=b2BroadPhase.b2_invalid;proxy.SetNext(this.m_freeProxy);this.m_freeProxy=proxyId;--this.m_proxyCount;},MoveProxy:function(proxyId,aabb){var axis=0;var index=0;var bound;var prevBound +var nextBound +var nextProxyId=0;var nextProxy;if(proxyId==b2Pair.b2_nullProxy||b2Settings.b2_maxProxies<=proxyId) +{return;} +if(aabb.IsValid()==false) +{return;} +var boundCount=2*this.m_proxyCount;var proxy=this.m_proxyPool[proxyId];var newValues=new b2BoundValues();this.ComputeBounds(newValues.lowerValues,newValues.upperValues,aabb);var oldValues=new b2BoundValues();for(axis=0;axis<2;++axis) +{oldValues.lowerValues[axis]=this.m_bounds[axis][proxy.lowerBounds[axis]].value;oldValues.upperValues[axis]=this.m_bounds[axis][proxy.upperBounds[axis]].value;} +for(axis=0;axis<2;++axis) +{var bounds=this.m_bounds[axis];var lowerIndex=proxy.lowerBounds[axis];var upperIndex=proxy.upperBounds[axis];var lowerValue=newValues.lowerValues[axis];var upperValue=newValues.upperValues[axis];var deltaLower=lowerValue-bounds[lowerIndex].value;var deltaUpper=upperValue-bounds[upperIndex].value;bounds[lowerIndex].value=lowerValue;bounds[upperIndex].value=upperValue;if(deltaLower<0) +{index=lowerIndex;while(index>0&&lowerValue<bounds[index-1].value) +{bound=bounds[index];prevBound=bounds[index-1];var prevProxyId=prevBound.proxyId;var prevProxy=this.m_proxyPool[prevBound.proxyId];prevBound.stabbingCount++;if(prevBound.IsUpper()==true) +{if(this.TestOverlap(newValues,prevProxy)) +{this.m_pairManager.AddBufferedPair(proxyId,prevProxyId);} +prevProxy.upperBounds[axis]++;bound.stabbingCount++;} +else +{prevProxy.lowerBounds[axis]++;bound.stabbingCount--;} +proxy.lowerBounds[axis]--;bound.Swap(prevBound);--index;}} +if(deltaUpper>0) +{index=upperIndex;while(index<boundCount-1&&bounds[index+1].value<=upperValue) +{bound=bounds[index];nextBound=bounds[index+1];nextProxyId=nextBound.proxyId;nextProxy=this.m_proxyPool[nextProxyId];nextBound.stabbingCount++;if(nextBound.IsLower()==true) +{if(this.TestOverlap(newValues,nextProxy)) +{this.m_pairManager.AddBufferedPair(proxyId,nextProxyId);} +nextProxy.lowerBounds[axis]--;bound.stabbingCount++;} +else +{nextProxy.upperBounds[axis]--;bound.stabbingCount--;} +proxy.upperBounds[axis]++;bound.Swap(nextBound);index++;}} +if(deltaLower>0) +{index=lowerIndex;while(index<boundCount-1&&bounds[index+1].value<=lowerValue) +{bound=bounds[index];nextBound=bounds[index+1];nextProxyId=nextBound.proxyId;nextProxy=this.m_proxyPool[nextProxyId];nextBound.stabbingCount--;if(nextBound.IsUpper()) +{if(this.TestOverlap(oldValues,nextProxy)) +{this.m_pairManager.RemoveBufferedPair(proxyId,nextProxyId);} +nextProxy.upperBounds[axis]--;bound.stabbingCount--;} +else +{nextProxy.lowerBounds[axis]--;bound.stabbingCount++;} +proxy.lowerBounds[axis]++;bound.Swap(nextBound);index++;}} +if(deltaUpper<0) +{index=upperIndex;while(index>0&&upperValue<bounds[index-1].value) +{bound=bounds[index];prevBound=bounds[index-1];prevProxyId=prevBound.proxyId;prevProxy=this.m_proxyPool[prevProxyId];prevBound.stabbingCount--;if(prevBound.IsLower()==true) +{if(this.TestOverlap(oldValues,prevProxy)) +{this.m_pairManager.RemoveBufferedPair(proxyId,prevProxyId);} +prevProxy.lowerBounds[axis]++;bound.stabbingCount--;} +else +{prevProxy.upperBounds[axis]++;bound.stabbingCount++;} +proxy.upperBounds[axis]--;bound.Swap(prevBound);index--;}}}},Commit:function(){this.m_pairManager.Commit();},QueryAABB:function(aabb,userData,maxCount){var lowerValues=new Array();var upperValues=new Array();this.ComputeBounds(lowerValues,upperValues,aabb);var lowerIndex=0;var upperIndex=0;var lowerIndexOut=[lowerIndex];var upperIndexOut=[upperIndex];this.Query(lowerIndexOut,upperIndexOut,lowerValues[0],upperValues[0],this.m_bounds[0],2*this.m_proxyCount,0);this.Query(lowerIndexOut,upperIndexOut,lowerValues[1],upperValues[1],this.m_bounds[1],2*this.m_proxyCount,1);var count=0;for(var i=0;i<this.m_queryResultCount&&count<maxCount;++i,++count) +{var proxy=this.m_proxyPool[this.m_queryResults[i]];userData[i]=proxy.userData;} +this.m_queryResultCount=0;this.IncrementTimeStamp();return count;},Validate:function(){var pair;var proxy1;var proxy2;var overlap;for(var axis=0;axis<2;++axis) +{var bounds=this.m_bounds[axis];var boundCount=2*this.m_proxyCount;var stabbingCount=0;for(var i=0;i<boundCount;++i) +{var bound=bounds[i];if(bound.IsLower()==true) +{stabbingCount++;} +else +{stabbingCount--;}}}},ComputeBounds:function(lowerValues,upperValues,aabb) +{var minVertexX=aabb.minVertex.x;var minVertexY=aabb.minVertex.y;minVertexX=b2Math.b2Min(minVertexX,this.m_worldAABB.maxVertex.x);minVertexY=b2Math.b2Min(minVertexY,this.m_worldAABB.maxVertex.y);minVertexX=b2Math.b2Max(minVertexX,this.m_worldAABB.minVertex.x);minVertexY=b2Math.b2Max(minVertexY,this.m_worldAABB.minVertex.y);var maxVertexX=aabb.maxVertex.x;var maxVertexY=aabb.maxVertex.y;maxVertexX=b2Math.b2Min(maxVertexX,this.m_worldAABB.maxVertex.x);maxVertexY=b2Math.b2Min(maxVertexY,this.m_worldAABB.maxVertex.y);maxVertexX=b2Math.b2Max(maxVertexX,this.m_worldAABB.minVertex.x);maxVertexY=b2Math.b2Max(maxVertexY,this.m_worldAABB.minVertex.y);lowerValues[0]=(this.m_quantizationFactor.x*(minVertexX-this.m_worldAABB.minVertex.x))&(b2Settings.USHRT_MAX-1);upperValues[0]=((this.m_quantizationFactor.x*(maxVertexX-this.m_worldAABB.minVertex.x))&0x0000ffff)|1;lowerValues[1]=(this.m_quantizationFactor.y*(minVertexY-this.m_worldAABB.minVertex.y))&(b2Settings.USHRT_MAX-1);upperValues[1]=((this.m_quantizationFactor.y*(maxVertexY-this.m_worldAABB.minVertex.y))&0x0000ffff)|1;},TestOverlapValidate:function(p1,p2){for(var axis=0;axis<2;++axis) +{var bounds=this.m_bounds[axis];if(bounds[p1.lowerBounds[axis]].value>bounds[p2.upperBounds[axis]].value) +return false;if(bounds[p1.upperBounds[axis]].value<bounds[p2.lowerBounds[axis]].value) +return false;} +return true;},TestOverlap:function(b,p) +{for(var axis=0;axis<2;++axis) +{var bounds=this.m_bounds[axis];if(b.lowerValues[axis]>bounds[p.upperBounds[axis]].value) +return false;if(b.upperValues[axis]<bounds[p.lowerBounds[axis]].value) +return false;} +return true;},Query:function(lowerQueryOut,upperQueryOut,lowerValue,upperValue,bounds,boundCount,axis){var lowerQuery=b2BroadPhase.BinarySearch(bounds,boundCount,lowerValue);var upperQuery=b2BroadPhase.BinarySearch(bounds,boundCount,upperValue);for(var j=lowerQuery;j<upperQuery;++j) +{if(bounds[j].IsLower()) +{this.IncrementOverlapCount(bounds[j].proxyId);}} +if(lowerQuery>0) +{var i=lowerQuery-1;var s=bounds[i].stabbingCount;while(s) +{if(bounds[i].IsLower()) +{var proxy=this.m_proxyPool[bounds[i].proxyId];if(lowerQuery<=proxy.upperBounds[axis]) +{this.IncrementOverlapCount(bounds[i].proxyId);--s;}} +--i;}} +lowerQueryOut[0]=lowerQuery;upperQueryOut[0]=upperQuery;},IncrementOverlapCount:function(proxyId){var proxy=this.m_proxyPool[proxyId];if(proxy.timeStamp<this.m_timeStamp) +{proxy.timeStamp=this.m_timeStamp;proxy.overlapCount=1;} +else +{proxy.overlapCount=2;this.m_queryResults[this.m_queryResultCount]=proxyId;++this.m_queryResultCount;}},IncrementTimeStamp:function(){if(this.m_timeStamp==b2Settings.USHRT_MAX) +{for(var i=0;i<b2Settings.b2_maxProxies;++i) +{this.m_proxyPool[i].timeStamp=0;} +this.m_timeStamp=1;} +else +{++this.m_timeStamp;}},m_pairManager:new b2PairManager(),m_proxyPool:new Array(b2Settings.b2_maxPairs),m_freeProxy:0,m_bounds:new Array(2*b2Settings.b2_maxProxies),m_queryResults:new Array(b2Settings.b2_maxProxies),m_queryResultCount:0,m_worldAABB:null,m_quantizationFactor:new b2Vec2(),m_proxyCount:0,m_timeStamp:0};b2BroadPhase.s_validate=false;b2BroadPhase.b2_invalid=b2Settings.USHRT_MAX;b2BroadPhase.b2_nullEdge=b2Settings.USHRT_MAX;b2BroadPhase.BinarySearch=function(bounds,count,value) +{var low=0;var high=count-1;while(low<=high) +{var mid=Math.floor((low+high)/2);if(bounds[mid].value>value) +{high=mid-1;} +else if(bounds[mid].value<value) +{low=mid+1;} +else +{return(mid);}} +return(low);}; +var b2Collision=Class.create();b2Collision.prototype={initialize:function(){}} +b2Collision.b2_nullFeature=0x000000ff;b2Collision.ClipSegmentToLine=function(vOut,vIn,normal,offset) +{var numOut=0;var vIn0=vIn[0].v;var vIn1=vIn[1].v;var distance0=b2Math.b2Dot(normal,vIn[0].v)-offset;var distance1=b2Math.b2Dot(normal,vIn[1].v)-offset;if(distance0<=0.0)vOut[numOut++]=vIn[0];if(distance1<=0.0)vOut[numOut++]=vIn[1];if(distance0*distance1<0.0) +{var interp=distance0/(distance0-distance1);var tVec=vOut[numOut].v;tVec.x=vIn0.x+interp*(vIn1.x-vIn0.x);tVec.y=vIn0.y+interp*(vIn1.y-vIn0.y);if(distance0>0.0) +{vOut[numOut].id=vIn[0].id;} +else +{vOut[numOut].id=vIn[1].id;} +++numOut;} +return numOut;};b2Collision.EdgeSeparation=function(poly1,edge1,poly2) +{var vert1s=poly1.m_vertices;var count2=poly2.m_vertexCount;var vert2s=poly2.m_vertices;var normalX=poly1.m_normals[edge1].x;var normalY=poly1.m_normals[edge1].y;var tX=normalX;var tMat=poly1.m_R;normalX=tMat.col1.x*tX+tMat.col2.x*normalY;normalY=tMat.col1.y*tX+tMat.col2.y*normalY;var normalLocal2X=normalX;var normalLocal2Y=normalY;tMat=poly2.m_R;tX=normalLocal2X*tMat.col1.x+normalLocal2Y*tMat.col1.y;normalLocal2Y=normalLocal2X*tMat.col2.x+normalLocal2Y*tMat.col2.y;normalLocal2X=tX;var vertexIndex2=0;var minDot=Number.MAX_VALUE;for(var i=0;i<count2;++i) +{var tVec=vert2s[i];var dot=tVec.x*normalLocal2X+tVec.y*normalLocal2Y;if(dot<minDot) +{minDot=dot;vertexIndex2=i;}} +tMat=poly1.m_R;var v1X=poly1.m_position.x+(tMat.col1.x*vert1s[edge1].x+tMat.col2.x*vert1s[edge1].y) +var v1Y=poly1.m_position.y+(tMat.col1.y*vert1s[edge1].x+tMat.col2.y*vert1s[edge1].y) +tMat=poly2.m_R;var v2X=poly2.m_position.x+(tMat.col1.x*vert2s[vertexIndex2].x+tMat.col2.x*vert2s[vertexIndex2].y) +var v2Y=poly2.m_position.y+(tMat.col1.y*vert2s[vertexIndex2].x+tMat.col2.y*vert2s[vertexIndex2].y) +v2X-=v1X;v2Y-=v1Y;var separation=v2X*normalX+v2Y*normalY;return separation;};b2Collision.FindMaxSeparation=function(edgeIndex,poly1,poly2,conservative) +{var count1=poly1.m_vertexCount;var dX=poly2.m_position.x-poly1.m_position.x;var dY=poly2.m_position.y-poly1.m_position.y;var dLocal1X=(dX*poly1.m_R.col1.x+dY*poly1.m_R.col1.y);var dLocal1Y=(dX*poly1.m_R.col2.x+dY*poly1.m_R.col2.y);var edge=0;var maxDot=-Number.MAX_VALUE;for(var i=0;i<count1;++i) +{var dot=(poly1.m_normals[i].x*dLocal1X+poly1.m_normals[i].y*dLocal1Y);if(dot>maxDot) +{maxDot=dot;edge=i;}} +var s=b2Collision.EdgeSeparation(poly1,edge,poly2);if(s>0.0&&conservative==false) +{return s;} +var prevEdge=edge-1>=0?edge-1:count1-1;var sPrev=b2Collision.EdgeSeparation(poly1,prevEdge,poly2);if(sPrev>0.0&&conservative==false) +{return sPrev;} +var nextEdge=edge+1<count1?edge+1:0;var sNext=b2Collision.EdgeSeparation(poly1,nextEdge,poly2);if(sNext>0.0&&conservative==false) +{return sNext;} +var bestEdge=0;var bestSeparation;var increment=0;if(sPrev>s&&sPrev>sNext) +{increment=-1;bestEdge=prevEdge;bestSeparation=sPrev;} +else if(sNext>s) +{increment=1;bestEdge=nextEdge;bestSeparation=sNext;} +else +{edgeIndex[0]=edge;return s;} +while(true) +{if(increment==-1) +edge=bestEdge-1>=0?bestEdge-1:count1-1;else +edge=bestEdge+1<count1?bestEdge+1:0;s=b2Collision.EdgeSeparation(poly1,edge,poly2);if(s>0.0&&conservative==false) +{return s;} +if(s>bestSeparation) +{bestEdge=edge;bestSeparation=s;} +else +{break;}} +edgeIndex[0]=bestEdge;return bestSeparation;};b2Collision.FindIncidentEdge=function(c,poly1,edge1,poly2) +{var count1=poly1.m_vertexCount;var vert1s=poly1.m_vertices;var count2=poly2.m_vertexCount;var vert2s=poly2.m_vertices;var vertex11=edge1;var vertex12=edge1+1==count1?0:edge1+1;var tVec=vert1s[vertex12];var normal1Local1X=tVec.x;var normal1Local1Y=tVec.y;tVec=vert1s[vertex11];normal1Local1X-=tVec.x;normal1Local1Y-=tVec.y;var tX=normal1Local1X;normal1Local1X=normal1Local1Y;normal1Local1Y=-tX;var invLength=1.0/Math.sqrt(normal1Local1X*normal1Local1X+normal1Local1Y*normal1Local1Y);normal1Local1X*=invLength;normal1Local1Y*=invLength;var normal1X=normal1Local1X;var normal1Y=normal1Local1Y;tX=normal1X;var tMat=poly1.m_R;normal1X=tMat.col1.x*tX+tMat.col2.x*normal1Y;normal1Y=tMat.col1.y*tX+tMat.col2.y*normal1Y;var normal1Local2X=normal1X;var normal1Local2Y=normal1Y;tMat=poly2.m_R;tX=normal1Local2X*tMat.col1.x+normal1Local2Y*tMat.col1.y;normal1Local2Y=normal1Local2X*tMat.col2.x+normal1Local2Y*tMat.col2.y;normal1Local2X=tX;var vertex21=0;var vertex22=0;var minDot=Number.MAX_VALUE;for(var i=0;i<count2;++i) +{var i1=i;var i2=i+1<count2?i+1:0;tVec=vert2s[i2];var normal2Local2X=tVec.x;var normal2Local2Y=tVec.y;tVec=vert2s[i1];normal2Local2X-=tVec.x;normal2Local2Y-=tVec.y;tX=normal2Local2X;normal2Local2X=normal2Local2Y;normal2Local2Y=-tX;invLength=1.0/Math.sqrt(normal2Local2X*normal2Local2X+normal2Local2Y*normal2Local2Y);normal2Local2X*=invLength;normal2Local2Y*=invLength;var dot=normal2Local2X*normal1Local2X+normal2Local2Y*normal1Local2Y;if(dot<minDot) +{minDot=dot;vertex21=i1;vertex22=i2;}} +var tClip;tClip=c[0];tVec=tClip.v;tVec.SetV(vert2s[vertex21]);tVec.MulM(poly2.m_R);tVec.Add(poly2.m_position);tClip.id.features.referenceFace=edge1;tClip.id.features.incidentEdge=vertex21;tClip.id.features.incidentVertex=vertex21;tClip=c[1];tVec=tClip.v;tVec.SetV(vert2s[vertex22]);tVec.MulM(poly2.m_R);tVec.Add(poly2.m_position);tClip.id.features.referenceFace=edge1;tClip.id.features.incidentEdge=vertex21;tClip.id.features.incidentVertex=vertex22;};b2Collision.b2CollidePolyTempVec=new b2Vec2();b2Collision.b2CollidePoly=function(manifold,polyA,polyB,conservative) +{manifold.pointCount=0;var edgeA=0;var edgeAOut=[edgeA];var separationA=b2Collision.FindMaxSeparation(edgeAOut,polyA,polyB,conservative);edgeA=edgeAOut[0];if(separationA>0.0&&conservative==false) +return;var edgeB=0;var edgeBOut=[edgeB];var separationB=b2Collision.FindMaxSeparation(edgeBOut,polyB,polyA,conservative);edgeB=edgeBOut[0];if(separationB>0.0&&conservative==false) +return;var poly1;var poly2;var edge1=0;var flip=0;var k_relativeTol=0.98;var k_absoluteTol=0.001;if(separationB>k_relativeTol*separationA+k_absoluteTol) +{poly1=polyB;poly2=polyA;edge1=edgeB;flip=1;} +else +{poly1=polyA;poly2=polyB;edge1=edgeA;flip=0;} +var incidentEdge=[new ClipVertex(),new ClipVertex()];b2Collision.FindIncidentEdge(incidentEdge,poly1,edge1,poly2);var count1=poly1.m_vertexCount;var vert1s=poly1.m_vertices;var v11=vert1s[edge1];var v12=edge1+1<count1?vert1s[edge1+1]:vert1s[0];var dvX=v12.x-v11.x;var dvY=v12.y-v11.y;var sideNormalX=v12.x-v11.x;var sideNormalY=v12.y-v11.y;var tX=sideNormalX;var tMat=poly1.m_R;sideNormalX=tMat.col1.x*tX+tMat.col2.x*sideNormalY;sideNormalY=tMat.col1.y*tX+tMat.col2.y*sideNormalY;var invLength=1.0/Math.sqrt(sideNormalX*sideNormalX+sideNormalY*sideNormalY);sideNormalX*=invLength;sideNormalY*=invLength;var frontNormalX=sideNormalX;var frontNormalY=sideNormalY;tX=frontNormalX;frontNormalX=frontNormalY;frontNormalY=-tX;var v11X=v11.x;var v11Y=v11.y;tX=v11X;tMat=poly1.m_R;v11X=tMat.col1.x*tX+tMat.col2.x*v11Y;v11Y=tMat.col1.y*tX+tMat.col2.y*v11Y;v11X+=poly1.m_position.x;v11Y+=poly1.m_position.y;var v12X=v12.x;var v12Y=v12.y;tX=v12X;tMat=poly1.m_R;v12X=tMat.col1.x*tX+tMat.col2.x*v12Y;v12Y=tMat.col1.y*tX+tMat.col2.y*v12Y;v12X+=poly1.m_position.x;v12Y+=poly1.m_position.y;var frontOffset=frontNormalX*v11X+frontNormalY*v11Y;var sideOffset1=-(sideNormalX*v11X+sideNormalY*v11Y);var sideOffset2=sideNormalX*v12X+sideNormalY*v12Y;var clipPoints1=[new ClipVertex(),new ClipVertex()];var clipPoints2=[new ClipVertex(),new ClipVertex()];var np=0;b2Collision.b2CollidePolyTempVec.Set(-sideNormalX,-sideNormalY);np=b2Collision.ClipSegmentToLine(clipPoints1,incidentEdge,b2Collision.b2CollidePolyTempVec,sideOffset1);if(np<2) +return;b2Collision.b2CollidePolyTempVec.Set(sideNormalX,sideNormalY);np=b2Collision.ClipSegmentToLine(clipPoints2,clipPoints1,b2Collision.b2CollidePolyTempVec,sideOffset2);if(np<2) +return;if(flip){manifold.normal.Set(-frontNormalX,-frontNormalY);} +else{manifold.normal.Set(frontNormalX,frontNormalY);} +var pointCount=0;for(var i=0;i<b2Settings.b2_maxManifoldPoints;++i) +{var tVec=clipPoints2[i].v;var separation=(frontNormalX*tVec.x+frontNormalY*tVec.y)-frontOffset;if(separation<=0.0||conservative==true) +{var cp=manifold.points[pointCount];cp.separation=separation;cp.position.SetV(clipPoints2[i].v);cp.id.Set(clipPoints2[i].id);cp.id.features.flip=flip;++pointCount;}} +manifold.pointCount=pointCount;};b2Collision.b2CollideCircle=function(manifold,circle1,circle2,conservative) +{manifold.pointCount=0;var dX=circle2.m_position.x-circle1.m_position.x;var dY=circle2.m_position.y-circle1.m_position.y;var distSqr=dX*dX+dY*dY;var radiusSum=circle1.m_radius+circle2.m_radius;if(distSqr>radiusSum*radiusSum&&conservative==false) +{return;} +var separation;if(distSqr<Number.MIN_VALUE) +{separation=-radiusSum;manifold.normal.Set(0.0,1.0);} +else +{var dist=Math.sqrt(distSqr);separation=dist-radiusSum;var a=1.0/dist;manifold.normal.x=a*dX;manifold.normal.y=a*dY;} +manifold.pointCount=1;var tPoint=manifold.points[0];tPoint.id.set_key(0);tPoint.separation=separation;tPoint.position.x=circle2.m_position.x-(circle2.m_radius*manifold.normal.x);tPoint.position.y=circle2.m_position.y-(circle2.m_radius*manifold.normal.y);};b2Collision.b2CollidePolyAndCircle=function(manifold,poly,circle,conservative) +{manifold.pointCount=0;var tPoint;var dX;var dY;var xLocalX=circle.m_position.x-poly.m_position.x;var xLocalY=circle.m_position.y-poly.m_position.y;var tMat=poly.m_R;var tX=xLocalX*tMat.col1.x+xLocalY*tMat.col1.y;xLocalY=xLocalX*tMat.col2.x+xLocalY*tMat.col2.y;xLocalX=tX;var dist;var normalIndex=0;var separation=-Number.MAX_VALUE;var radius=circle.m_radius;for(var i=0;i<poly.m_vertexCount;++i) +{var s=poly.m_normals[i].x*(xLocalX-poly.m_vertices[i].x)+poly.m_normals[i].y*(xLocalY-poly.m_vertices[i].y);if(s>radius) +{return;} +if(s>separation) +{separation=s;normalIndex=i;}} +if(separation<Number.MIN_VALUE) +{manifold.pointCount=1;var tVec=poly.m_normals[normalIndex];manifold.normal.x=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y;manifold.normal.y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y;tPoint=manifold.points[0];tPoint.id.features.incidentEdge=normalIndex;tPoint.id.features.incidentVertex=b2Collision.b2_nullFeature;tPoint.id.features.referenceFace=b2Collision.b2_nullFeature;tPoint.id.features.flip=0;tPoint.position.x=circle.m_position.x-radius*manifold.normal.x;tPoint.position.y=circle.m_position.y-radius*manifold.normal.y;tPoint.separation=separation-radius;return;} +var vertIndex1=normalIndex;var vertIndex2=vertIndex1+1<poly.m_vertexCount?vertIndex1+1:0;var eX=poly.m_vertices[vertIndex2].x-poly.m_vertices[vertIndex1].x;var eY=poly.m_vertices[vertIndex2].y-poly.m_vertices[vertIndex1].y;var length=Math.sqrt(eX*eX+eY*eY);eX/=length;eY/=length;if(length<Number.MIN_VALUE) +{dX=xLocalX-poly.m_vertices[vertIndex1].x;dY=xLocalY-poly.m_vertices[vertIndex1].y;dist=Math.sqrt(dX*dX+dY*dY);dX/=dist;dY/=dist;if(dist>radius) +{return;} +manifold.pointCount=1;manifold.normal.Set(tMat.col1.x*dX+tMat.col2.x*dY,tMat.col1.y*dX+tMat.col2.y*dY);tPoint=manifold.points[0];tPoint.id.features.incidentEdge=b2Collision.b2_nullFeature;tPoint.id.features.incidentVertex=vertIndex1;tPoint.id.features.referenceFace=b2Collision.b2_nullFeature;tPoint.id.features.flip=0;tPoint.position.x=circle.m_position.x-radius*manifold.normal.x;tPoint.position.y=circle.m_position.y-radius*manifold.normal.y;tPoint.separation=dist-radius;return;} +var u=(xLocalX-poly.m_vertices[vertIndex1].x)*eX+(xLocalY-poly.m_vertices[vertIndex1].y)*eY;tPoint=manifold.points[0];tPoint.id.features.incidentEdge=b2Collision.b2_nullFeature;tPoint.id.features.incidentVertex=b2Collision.b2_nullFeature;tPoint.id.features.referenceFace=b2Collision.b2_nullFeature;tPoint.id.features.flip=0;var pX,pY;if(u<=0.0) +{pX=poly.m_vertices[vertIndex1].x;pY=poly.m_vertices[vertIndex1].y;tPoint.id.features.incidentVertex=vertIndex1;} +else if(u>=length) +{pX=poly.m_vertices[vertIndex2].x;pY=poly.m_vertices[vertIndex2].y;tPoint.id.features.incidentVertex=vertIndex2;} +else +{pX=eX*u+poly.m_vertices[vertIndex1].x;pY=eY*u+poly.m_vertices[vertIndex1].y;tPoint.id.features.incidentEdge=vertIndex1;} +dX=xLocalX-pX;dY=xLocalY-pY;dist=Math.sqrt(dX*dX+dY*dY);dX/=dist;dY/=dist;if(dist>radius) +{return;} +manifold.pointCount=1;manifold.normal.Set(tMat.col1.x*dX+tMat.col2.x*dY,tMat.col1.y*dX+tMat.col2.y*dY);tPoint.position.x=circle.m_position.x-radius*manifold.normal.x;tPoint.position.y=circle.m_position.y-radius*manifold.normal.y;tPoint.separation=dist-radius;};b2Collision.b2TestOverlap=function(a,b) +{var t1=b.minVertex;var t2=a.maxVertex;var d1X=t1.x-t2.x;var d1Y=t1.y-t2.y;t1=a.minVertex;t2=b.maxVertex;var d2X=t1.x-t2.x;var d2Y=t1.y-t2.y;if(d1X>0.0||d1Y>0.0) +return false;if(d2X>0.0||d2Y>0.0) +return false;return true;}; +var Features=Class.create();Features.prototype={set_referenceFace:function(value){this._referenceFace=value;this._m_id._key=(this._m_id._key&0xffffff00)|(this._referenceFace&0x000000ff)},get_referenceFace:function(){return this._referenceFace;},_referenceFace:0,set_incidentEdge:function(value){this._incidentEdge=value;this._m_id._key=(this._m_id._key&0xffff00ff)|((this._incidentEdge<<8)&0x0000ff00)},get_incidentEdge:function(){return this._incidentEdge;},_incidentEdge:0,set_incidentVertex:function(value){this._incidentVertex=value;this._m_id._key=(this._m_id._key&0xff00ffff)|((this._incidentVertex<<16)&0x00ff0000)},get_incidentVertex:function(){return this._incidentVertex;},_incidentVertex:0,set_flip:function(value){this._flip=value;this._m_id._key=(this._m_id._key&0x00ffffff)|((this._flip<<24)&0xff000000)},get_flip:function(){return this._flip;},_flip:0,_m_id:null,initialize:function(){}}; +var b2ContactID=Class.create();b2ContactID.prototype={initialize:function(){this.features=new Features();this.features._m_id=this;},Set:function(id){this.set_key(id._key);},Copy:function(){var id=new b2ContactID();id.set_key(this._key);return id;},get_key:function(){return this._key;},set_key:function(value){this._key=value;this.features._referenceFace=this._key&0x000000ff;this.features._incidentEdge=((this._key&0x0000ff00)>>8)&0x000000ff;this.features._incidentVertex=((this._key&0x00ff0000)>>16)&0x000000ff;this.features._flip=((this._key&0xff000000)>>24)&0x000000ff;},features:new Features(),_key:0}; +var b2ContactPoint=Class.create();b2ContactPoint.prototype={position:new b2Vec2(),separation:null,normalImpulse:null,tangentImpulse:null,id:new b2ContactID(),initialize:function(){this.position=new b2Vec2();this.id=new b2ContactID();}};var b2Distance=Class.create();b2Distance.prototype={initialize:function(){}};b2Distance.ProcessTwo=function(p1Out,p2Out,p1s,p2s,points) +{var rX=-points[1].x;var rY=-points[1].y;var dX=points[0].x-points[1].x;var dY=points[0].y-points[1].y;var length=Math.sqrt(dX*dX+dY*dY);dX/=length;dY/=length;var lambda=rX*dX+rY*dY;if(lambda<=0.0||length<Number.MIN_VALUE) +{p1Out.SetV(p1s[1]);p2Out.SetV(p2s[1]);p1s[0].SetV(p1s[1]);p2s[0].SetV(p2s[1]);points[0].SetV(points[1]);return 1;} +lambda/=length;p1Out.x=p1s[1].x+lambda*(p1s[0].x-p1s[1].x);p1Out.y=p1s[1].y+lambda*(p1s[0].y-p1s[1].y);p2Out.x=p2s[1].x+lambda*(p2s[0].x-p2s[1].x);p2Out.y=p2s[1].y+lambda*(p2s[0].y-p2s[1].y);return 2;};b2Distance.ProcessThree=function(p1Out,p2Out,p1s,p2s,points) +{var aX=points[0].x;var aY=points[0].y;var bX=points[1].x;var bY=points[1].y;var cX=points[2].x;var cY=points[2].y;var abX=bX-aX;var abY=bY-aY;var acX=cX-aX;var acY=cY-aY;var bcX=cX-bX;var bcY=cY-bY;var sn=-(aX*abX+aY*abY);var sd=(bX*abX+bY*abY);var tn=-(aX*acX+aY*acY);var td=(cX*acX+cY*acY);var un=-(bX*bcX+bY*bcY);var ud=(cX*bcX+cY*bcY);if(td<=0.0&&ud<=0.0) +{p1Out.SetV(p1s[2]);p2Out.SetV(p2s[2]);p1s[0].SetV(p1s[2]);p2s[0].SetV(p2s[2]);points[0].SetV(points[2]);return 1;} +var n=abX*acY-abY*acX;var vc=n*(aX*bY-aY*bX);var va=n*(bX*cY-bY*cX);if(va<=0.0&&un>=0.0&&ud>=0.0) +{var lambda=un/(un+ud);p1Out.x=p1s[1].x+lambda*(p1s[2].x-p1s[1].x);p1Out.y=p1s[1].y+lambda*(p1s[2].y-p1s[1].y);p2Out.x=p2s[1].x+lambda*(p2s[2].x-p2s[1].x);p2Out.y=p2s[1].y+lambda*(p2s[2].y-p2s[1].y);p1s[0].SetV(p1s[2]);p2s[0].SetV(p2s[2]);points[0].SetV(points[2]);return 2;} +var vb=n*(cX*aY-cY*aX);if(vb<=0.0&&tn>=0.0&&td>=0.0) +{var lambda=tn/(tn+td);p1Out.x=p1s[0].x+lambda*(p1s[2].x-p1s[0].x);p1Out.y=p1s[0].y+lambda*(p1s[2].y-p1s[0].y);p2Out.x=p2s[0].x+lambda*(p2s[2].x-p2s[0].x);p2Out.y=p2s[0].y+lambda*(p2s[2].y-p2s[0].y);p1s[1].SetV(p1s[2]);p2s[1].SetV(p2s[2]);points[1].SetV(points[2]);return 2;} +var denom=va+vb+vc;denom=1.0/denom;var u=va*denom;var v=vb*denom;var w=1.0-u-v;p1Out.x=u*p1s[0].x+v*p1s[1].x+w*p1s[2].x;p1Out.y=u*p1s[0].y+v*p1s[1].y+w*p1s[2].y;p2Out.x=u*p2s[0].x+v*p2s[1].x+w*p2s[2].x;p2Out.y=u*p2s[0].y+v*p2s[1].y+w*p2s[2].y;return 3;};b2Distance.InPoinsts=function(w,points,pointCount) +{for(var i=0;i<pointCount;++i) +{if(w.x==points[i].x&&w.y==points[i].y) +{return true;}} +return false;};b2Distance.Distance=function(p1Out,p2Out,shape1,shape2) +{var p1s=new Array(3);var p2s=new Array(3);var points=new Array(3);var pointCount=0;p1Out.SetV(shape1.m_position);p2Out.SetV(shape2.m_position);var vSqr=0.0;var maxIterations=20;for(var iter=0;iter<maxIterations;++iter) +{var vX=p2Out.x-p1Out.x;var vY=p2Out.y-p1Out.y;var w1=shape1.Support(vX,vY);var w2=shape2.Support(-vX,-vY);vSqr=(vX*vX+vY*vY);var wX=w2.x-w1.x;var wY=w2.y-w1.y;var vw=(vX*wX+vY*wY);if(vSqr-b2Dot(vX*wX+vY*wY)<=0.01*vSqr) +{if(pointCount==0) +{p1Out.SetV(w1);p2Out.SetV(w2);} +b2Distance.g_GJK_Iterations=iter;return Math.sqrt(vSqr);} +switch(pointCount) +{case 0:p1s[0].SetV(w1);p2s[0].SetV(w2);points[0]=w;p1Out.SetV(p1s[0]);p2Out.SetV(p2s[0]);++pointCount;break;case 1:p1s[1].SetV(w1);p2s[1].SetV(w2);points[1].x=wX;points[1].y=wY;pointCount=b2Distance.ProcessTwo(p1Out,p2Out,p1s,p2s,points);break;case 2:p1s[2].SetV(w1);p2s[2].SetV(w2);points[2].x=wX;points[2].y=wY;pointCount=b2Distance.ProcessThree(p1Out,p2Out,p1s,p2s,points);break;} +if(pointCount==3) +{b2Distance.g_GJK_Iterations=iter;return 0.0;} +var maxSqr=-Number.MAX_VALUE;for(var i=0;i<pointCount;++i) +{maxSqr=b2Math.b2Max(maxSqr,(points[i].x*points[i].x+points[i].y*points[i].y));} +if(pointCount==3||vSqr<=100.0*Number.MIN_VALUE*maxSqr) +{b2Distance.g_GJK_Iterations=iter;return Math.sqrt(vSqr);}} +b2Distance.g_GJK_Iterations=maxIterations;return Math.sqrt(vSqr);};b2Distance.g_GJK_Iterations=0; +var b2Manifold=Class.create();b2Manifold.prototype={initialize:function(){this.points=new Array(b2Settings.b2_maxManifoldPoints);for(var i=0;i<b2Settings.b2_maxManifoldPoints;i++){this.points[i]=new b2ContactPoint();} +this.normal=new b2Vec2();},points:null,normal:null,pointCount:0}; +var b2OBB=Class.create();b2OBB.prototype={R:new b2Mat22(),center:new b2Vec2(),extents:new b2Vec2(),initialize:function(){this.R=new b2Mat22();this.center=new b2Vec2();this.extents=new b2Vec2();}}; +var b2Proxy=Class.create();b2Proxy.prototype={GetNext:function(){return this.lowerBounds[0];},SetNext:function(next){this.lowerBounds[0]=next;},IsValid:function(){return this.overlapCount!=b2BroadPhase.b2_invalid;},lowerBounds:[(0),(0)],upperBounds:[(0),(0)],overlapCount:0,timeStamp:0,userData:null,initialize:function(){this.lowerBounds=[(0),(0)];this.upperBounds=[(0),(0)];}} + +var ClipVertex=Class.create();ClipVertex.prototype={v:new b2Vec2(),id:new b2ContactID(),initialize:function(){this.v=new b2Vec2();this.id=new b2ContactID();}};var b2Shape=Class.create();b2Shape.prototype={TestPoint:function(p){return false},GetUserData:function(){return this.m_userData;},GetType:function(){return this.m_type;},GetBody:function(){return this.m_body;},GetPosition:function(){return this.m_position;},GetRotationMatrix:function(){return this.m_R;},ResetProxy:function(broadPhase){},GetNext:function(){return this.m_next;},initialize:function(def,body){this.m_R=new b2Mat22();this.m_position=new b2Vec2();this.m_userData=def.userData;this.m_friction=def.friction;this.m_restitution=def.restitution;this.m_body=body;this.m_proxyId=b2Pair.b2_nullProxy;this.m_maxRadius=0.0;this.m_categoryBits=def.categoryBits;this.m_maskBits=def.maskBits;this.m_groupIndex=def.groupIndex;},DestroyProxy:function() +{if(this.m_proxyId!=b2Pair.b2_nullProxy) +{this.m_body.m_world.m_broadPhase.DestroyProxy(this.m_proxyId);this.m_proxyId=b2Pair.b2_nullProxy;}},Synchronize:function(position1,R1,position2,R2){},QuickSync:function(position,R){},Support:function(dX,dY,out){},GetMaxRadius:function(){return this.m_maxRadius;},m_next:null,m_R:new b2Mat22(),m_position:new b2Vec2(),m_type:0,m_userData:null,m_body:null,m_friction:null,m_restitution:null,m_maxRadius:null,m_proxyId:0,m_categoryBits:0,m_maskBits:0,m_groupIndex:0};b2Shape.Create=function(def,body,center){switch(def.type) +{case b2Shape.e_circleShape:{return new b2CircleShape(def,body,center);} +case b2Shape.e_boxShape:case b2Shape.e_polyShape:{return new b2PolyShape(def,body,center);}} +return null;};b2Shape.Destroy=function(shape) +{if(shape.m_proxyId!=b2Pair.b2_nullProxy) +shape.m_body.m_world.m_broadPhase.DestroyProxy(shape.m_proxyId);};b2Shape.e_unknownShape=-1;b2Shape.e_circleShape=0;b2Shape.e_boxShape=1;b2Shape.e_polyShape=2;b2Shape.e_meshShape=3;b2Shape.e_shapeTypeCount=4;b2Shape.PolyMass=function(massData,vs,count,rho) +{var center=new b2Vec2();center.SetZero();var area=0.0;var I=0.0;var pRef=new b2Vec2(0.0,0.0);var inv3=1.0/3.0;for(var i=0;i<count;++i) +{var p1=pRef;var p2=vs[i];var p3=i+1<count?vs[i+1]:vs[0];var e1=b2Math.SubtractVV(p2,p1);var e2=b2Math.SubtractVV(p3,p1);var D=b2Math.b2CrossVV(e1,e2);var triangleArea=0.5*D;area+=triangleArea;var tVec=new b2Vec2();tVec.SetV(p1);tVec.Add(p2);tVec.Add(p3);tVec.Multiply(inv3*triangleArea);center.Add(tVec);var px=p1.x;var py=p1.y;var ex1=e1.x;var ey1=e1.y;var ex2=e2.x;var ey2=e2.y;var intx2=inv3*(0.25*(ex1*ex1+ex2*ex1+ex2*ex2)+(px*ex1+px*ex2))+0.5*px*px;var inty2=inv3*(0.25*(ey1*ey1+ey2*ey1+ey2*ey2)+(py*ey1+py*ey2))+0.5*py*py;I+=D*(intx2+inty2);} +massData.mass=rho*area;center.Multiply(1.0/area);massData.center=center;I=rho*(I-area*b2Math.b2Dot(center,center));massData.I=I;};b2Shape.PolyCentroid=function(vs,count,out) +{var cX=0.0;var cY=0.0;var area=0.0;var pRefX=0.0;var pRefY=0.0;var inv3=1.0/3.0;for(var i=0;i<count;++i) +{var p1X=pRefX;var p1Y=pRefY;var p2X=vs[i].x;var p2Y=vs[i].y;var p3X=i+1<count?vs[i+1].x:vs[0].x;var p3Y=i+1<count?vs[i+1].y:vs[0].y;var e1X=p2X-p1X;var e1Y=p2Y-p1Y;var e2X=p3X-p1X;var e2Y=p3Y-p1Y;var D=(e1X*e2Y-e1Y*e2X);var triangleArea=0.5*D;area+=triangleArea;cX+=triangleArea*inv3*(p1X+p2X+p3X);cY+=triangleArea*inv3*(p1Y+p2Y+p3Y);} +cX*=1.0/area;cY*=1.0/area;out.Set(cX,cY);}; +var b2ShapeDef=Class.create();b2ShapeDef.prototype={initialize:function() +{this.type=b2Shape.e_unknownShape;this.userData=null;this.localPosition=new b2Vec2(0.0,0.0);this.localRotation=0.0;this.friction=0.2;this.restitution=0.0;this.density=0.0;this.categoryBits=0x0001;this.maskBits=0xFFFF;this.groupIndex=0;},ComputeMass:function(massData) +{massData.center=new b2Vec2(0.0,0.0) +if(this.density==0.0) +{massData.mass=0.0;massData.center.Set(0.0,0.0);massData.I=0.0;};switch(this.type) +{case b2Shape.e_circleShape:{var circle=this;massData.mass=this.density*b2Settings.b2_pi*circle.radius*circle.radius;massData.center.Set(0.0,0.0);massData.I=0.5*(massData.mass)*circle.radius*circle.radius;} +break;case b2Shape.e_boxShape:{var box=this;massData.mass=4.0*this.density*box.extents.x*box.extents.y;massData.center.Set(0.0,0.0);massData.I=massData.mass/3.0*b2Math.b2Dot(box.extents,box.extents);} +break;case b2Shape.e_polyShape:{var poly=this;b2Shape.PolyMass(massData,poly.vertices,poly.vertexCount,this.density);} +break;default:massData.mass=0.0;massData.center.Set(0.0,0.0);massData.I=0.0;break;}},type:0,userData:null,localPosition:null,localRotation:null,friction:null,restitution:null,density:null,categoryBits:0,maskBits:0,groupIndex:0}; +var b2BoxDef=Class.create();Object.extend(b2BoxDef.prototype,b2ShapeDef.prototype);Object.extend(b2BoxDef.prototype,{initialize:function() +{this.type=b2Shape.e_unknownShape;this.userData=null;this.localPosition=new b2Vec2(0.0,0.0);this.localRotation=0.0;this.friction=0.2;this.restitution=0.0;this.density=0.0;this.categoryBits=0x0001;this.maskBits=0xFFFF;this.groupIndex=0;this.type=b2Shape.e_boxShape;this.extents=new b2Vec2(1.0,1.0);},extents:null}); +var b2CircleDef=Class.create();Object.extend(b2CircleDef.prototype,b2ShapeDef.prototype);Object.extend(b2CircleDef.prototype,{initialize:function() +{this.type=b2Shape.e_unknownShape;this.userData=null;this.localPosition=new b2Vec2(0.0,0.0);this.localRotation=0.0;this.friction=0.2;this.restitution=0.0;this.density=0.0;this.categoryBits=0x0001;this.maskBits=0xFFFF;this.groupIndex=0;this.type=b2Shape.e_circleShape;this.radius=1.0;},radius:null});var b2CircleShape=Class.create();Object.extend(b2CircleShape.prototype,b2Shape.prototype);Object.extend(b2CircleShape.prototype,{TestPoint:function(p){var d=new b2Vec2();d.SetV(p);d.Subtract(this.m_position);return b2Math.b2Dot(d,d)<=this.m_radius*this.m_radius;},initialize:function(def,body,localCenter){this.m_R=new b2Mat22();this.m_position=new b2Vec2();this.m_userData=def.userData;this.m_friction=def.friction;this.m_restitution=def.restitution;this.m_body=body;this.m_proxyId=b2Pair.b2_nullProxy;this.m_maxRadius=0.0;this.m_categoryBits=def.categoryBits;this.m_maskBits=def.maskBits;this.m_groupIndex=def.groupIndex;this.m_localPosition=new b2Vec2();var circle=def;this.m_localPosition.Set(def.localPosition.x-localCenter.x,def.localPosition.y-localCenter.y);this.m_type=b2Shape.e_circleShape;this.m_radius=circle.radius;this.m_R.SetM(this.m_body.m_R);var rX=this.m_R.col1.x*this.m_localPosition.x+this.m_R.col2.x*this.m_localPosition.y;var rY=this.m_R.col1.y*this.m_localPosition.x+this.m_R.col2.y*this.m_localPosition.y;this.m_position.x=this.m_body.m_position.x+rX;this.m_position.y=this.m_body.m_position.y+rY;this.m_maxRadius=Math.sqrt(rX*rX+rY*rY)+this.m_radius;var aabb=new b2AABB();aabb.minVertex.Set(this.m_position.x-this.m_radius,this.m_position.y-this.m_radius);aabb.maxVertex.Set(this.m_position.x+this.m_radius,this.m_position.y+this.m_radius);var broadPhase=this.m_body.m_world.m_broadPhase;if(broadPhase.InRange(aabb)) +{this.m_proxyId=broadPhase.CreateProxy(aabb,this);} +else +{this.m_proxyId=b2Pair.b2_nullProxy;} +if(this.m_proxyId==b2Pair.b2_nullProxy) +{this.m_body.Freeze();}},Synchronize:function(position1,R1,position2,R2){this.m_R.SetM(R2);this.m_position.x=(R2.col1.x*this.m_localPosition.x+R2.col2.x*this.m_localPosition.y)+position2.x;this.m_position.y=(R2.col1.y*this.m_localPosition.x+R2.col2.y*this.m_localPosition.y)+position2.y;if(this.m_proxyId==b2Pair.b2_nullProxy) +{return;} +var p1X=position1.x+(R1.col1.x*this.m_localPosition.x+R1.col2.x*this.m_localPosition.y);var p1Y=position1.y+(R1.col1.y*this.m_localPosition.x+R1.col2.y*this.m_localPosition.y);var lowerX=Math.min(p1X,this.m_position.x);var lowerY=Math.min(p1Y,this.m_position.y);var upperX=Math.max(p1X,this.m_position.x);var upperY=Math.max(p1Y,this.m_position.y);var aabb=new b2AABB();aabb.minVertex.Set(lowerX-this.m_radius,lowerY-this.m_radius);aabb.maxVertex.Set(upperX+this.m_radius,upperY+this.m_radius);var broadPhase=this.m_body.m_world.m_broadPhase;if(broadPhase.InRange(aabb)) +{broadPhase.MoveProxy(this.m_proxyId,aabb);} +else +{this.m_body.Freeze();}},QuickSync:function(position,R){this.m_R.SetM(R);this.m_position.x=(R.col1.x*this.m_localPosition.x+R.col2.x*this.m_localPosition.y)+position.x;this.m_position.y=(R.col1.y*this.m_localPosition.x+R.col2.y*this.m_localPosition.y)+position.y;},ResetProxy:function(broadPhase) +{if(this.m_proxyId==b2Pair.b2_nullProxy) +{return;} +var proxy=broadPhase.GetProxy(this.m_proxyId);broadPhase.DestroyProxy(this.m_proxyId);proxy=null;var aabb=new b2AABB();aabb.minVertex.Set(this.m_position.x-this.m_radius,this.m_position.y-this.m_radius);aabb.maxVertex.Set(this.m_position.x+this.m_radius,this.m_position.y+this.m_radius);if(broadPhase.InRange(aabb)) +{this.m_proxyId=broadPhase.CreateProxy(aabb,this);} +else +{this.m_proxyId=b2Pair.b2_nullProxy;} +if(this.m_proxyId==b2Pair.b2_nullProxy) +{this.m_body.Freeze();}},Support:function(dX,dY,out) +{var len=Math.sqrt(dX*dX+dY*dY);dX/=len;dY/=len;out.Set(this.m_position.x+this.m_radius*dX,this.m_position.y+this.m_radius*dY);},m_localPosition:new b2Vec2(),m_radius:null}); +var b2MassData=Class.create();b2MassData.prototype={mass:0.0,center:new b2Vec2(0,0),I:0.0,initialize:function(){this.center=new b2Vec2(0,0);}} + +var b2PolyDef=Class.create();Object.extend(b2PolyDef.prototype,b2ShapeDef.prototype);Object.extend(b2PolyDef.prototype,{initialize:function() +{this.type=b2Shape.e_unknownShape;this.userData=null;this.localPosition=new b2Vec2(0.0,0.0);this.localRotation=0.0;this.friction=0.2;this.restitution=0.0;this.density=0.0;this.categoryBits=0x0001;this.maskBits=0xFFFF;this.groupIndex=0;this.vertices=new Array(b2Settings.b2_maxPolyVertices);this.type=b2Shape.e_polyShape;this.vertexCount=0;for(var i=0;i<b2Settings.b2_maxPolyVertices;i++){this.vertices[i]=new b2Vec2();}},vertices:new Array(b2Settings.b2_maxPolyVertices),vertexCount:0});var b2PolyShape=Class.create();Object.extend(b2PolyShape.prototype,b2Shape.prototype);Object.extend(b2PolyShape.prototype,{TestPoint:function(p){var pLocal=new b2Vec2();pLocal.SetV(p);pLocal.Subtract(this.m_position);pLocal.MulTM(this.m_R);for(var i=0;i<this.m_vertexCount;++i) +{var tVec=new b2Vec2();tVec.SetV(pLocal);tVec.Subtract(this.m_vertices[i]);var dot=b2Math.b2Dot(this.m_normals[i],tVec);if(dot>0.0) +{return false;}} +return true;},initialize:function(def,body,newOrigin){this.m_R=new b2Mat22();this.m_position=new b2Vec2();this.m_userData=def.userData;this.m_friction=def.friction;this.m_restitution=def.restitution;this.m_body=body;this.m_proxyId=b2Pair.b2_nullProxy;this.m_maxRadius=0.0;this.m_categoryBits=def.categoryBits;this.m_maskBits=def.maskBits;this.m_groupIndex=def.groupIndex;this.syncAABB=new b2AABB();this.syncMat=new b2Mat22();this.m_localCentroid=new b2Vec2();this.m_localOBB=new b2OBB();var i=0;var hX;var hY;var tVec;var aabb=new b2AABB();this.m_vertices=new Array(b2Settings.b2_maxPolyVertices);this.m_coreVertices=new Array(b2Settings.b2_maxPolyVertices);this.m_normals=new Array(b2Settings.b2_maxPolyVertices);this.m_type=b2Shape.e_polyShape;var localR=new b2Mat22(def.localRotation);if(def.type==b2Shape.e_boxShape) +{this.m_localCentroid.x=def.localPosition.x-newOrigin.x;this.m_localCentroid.y=def.localPosition.y-newOrigin.y;var box=def;this.m_vertexCount=4;hX=box.extents.x;hY=box.extents.y;var hcX=Math.max(0.0,hX-2.0*b2Settings.b2_linearSlop);var hcY=Math.max(0.0,hY-2.0*b2Settings.b2_linearSlop);tVec=this.m_vertices[0]=new b2Vec2();tVec.x=localR.col1.x*hX+localR.col2.x*hY;tVec.y=localR.col1.y*hX+localR.col2.y*hY;tVec=this.m_vertices[1]=new b2Vec2();tVec.x=localR.col1.x*-hX+localR.col2.x*hY;tVec.y=localR.col1.y*-hX+localR.col2.y*hY;tVec=this.m_vertices[2]=new b2Vec2();tVec.x=localR.col1.x*-hX+localR.col2.x*-hY;tVec.y=localR.col1.y*-hX+localR.col2.y*-hY;tVec=this.m_vertices[3]=new b2Vec2();tVec.x=localR.col1.x*hX+localR.col2.x*-hY;tVec.y=localR.col1.y*hX+localR.col2.y*-hY;tVec=this.m_coreVertices[0]=new b2Vec2();tVec.x=localR.col1.x*hcX+localR.col2.x*hcY;tVec.y=localR.col1.y*hcX+localR.col2.y*hcY;tVec=this.m_coreVertices[1]=new b2Vec2();tVec.x=localR.col1.x*-hcX+localR.col2.x*hcY;tVec.y=localR.col1.y*-hcX+localR.col2.y*hcY;tVec=this.m_coreVertices[2]=new b2Vec2();tVec.x=localR.col1.x*-hcX+localR.col2.x*-hcY;tVec.y=localR.col1.y*-hcX+localR.col2.y*-hcY;tVec=this.m_coreVertices[3]=new b2Vec2();tVec.x=localR.col1.x*hcX+localR.col2.x*-hcY;tVec.y=localR.col1.y*hcX+localR.col2.y*-hcY;} +else +{var poly=def;this.m_vertexCount=poly.vertexCount;b2Shape.PolyCentroid(poly.vertices,poly.vertexCount,b2PolyShape.tempVec);var centroidX=b2PolyShape.tempVec.x;var centroidY=b2PolyShape.tempVec.y;this.m_localCentroid.x=def.localPosition.x+(localR.col1.x*centroidX+localR.col2.x*centroidY)-newOrigin.x;this.m_localCentroid.y=def.localPosition.y+(localR.col1.y*centroidX+localR.col2.y*centroidY)-newOrigin.y;for(i=0;i<this.m_vertexCount;++i) +{this.m_vertices[i]=new b2Vec2();this.m_coreVertices[i]=new b2Vec2();hX=poly.vertices[i].x-centroidX;hY=poly.vertices[i].y-centroidY;this.m_vertices[i].x=localR.col1.x*hX+localR.col2.x*hY;this.m_vertices[i].y=localR.col1.y*hX+localR.col2.y*hY;var uX=this.m_vertices[i].x;var uY=this.m_vertices[i].y;var length=Math.sqrt(uX*uX+uY*uY);if(length>Number.MIN_VALUE) +{uX*=1.0/length;uY*=1.0/length;} +this.m_coreVertices[i].x=this.m_vertices[i].x-2.0*b2Settings.b2_linearSlop*uX;this.m_coreVertices[i].y=this.m_vertices[i].y-2.0*b2Settings.b2_linearSlop*uY;}} +var minVertexX=Number.MAX_VALUE;var minVertexY=Number.MAX_VALUE;var maxVertexX=-Number.MAX_VALUE;var maxVertexY=-Number.MAX_VALUE;this.m_maxRadius=0.0;for(i=0;i<this.m_vertexCount;++i) +{var v=this.m_vertices[i];minVertexX=Math.min(minVertexX,v.x);minVertexY=Math.min(minVertexY,v.y);maxVertexX=Math.max(maxVertexX,v.x);maxVertexY=Math.max(maxVertexY,v.y);this.m_maxRadius=Math.max(this.m_maxRadius,v.Length());} +this.m_localOBB.R.SetIdentity();this.m_localOBB.center.Set((minVertexX+maxVertexX)*0.5,(minVertexY+maxVertexY)*0.5);this.m_localOBB.extents.Set((maxVertexX-minVertexX)*0.5,(maxVertexY-minVertexY)*0.5);var i1=0;var i2=0;for(i=0;i<this.m_vertexCount;++i) +{this.m_normals[i]=new b2Vec2();i1=i;i2=i+1<this.m_vertexCount?i+1:0;this.m_normals[i].x=this.m_vertices[i2].y-this.m_vertices[i1].y;this.m_normals[i].y=-(this.m_vertices[i2].x-this.m_vertices[i1].x);this.m_normals[i].Normalize();} +for(i=0;i<this.m_vertexCount;++i) +{i1=i;i2=i+1<this.m_vertexCount?i+1:0;} +this.m_R.SetM(this.m_body.m_R);this.m_position.x=this.m_body.m_position.x+(this.m_R.col1.x*this.m_localCentroid.x+this.m_R.col2.x*this.m_localCentroid.y);this.m_position.y=this.m_body.m_position.y+(this.m_R.col1.y*this.m_localCentroid.x+this.m_R.col2.y*this.m_localCentroid.y);b2PolyShape.tAbsR.col1.x=this.m_R.col1.x*this.m_localOBB.R.col1.x+this.m_R.col2.x*this.m_localOBB.R.col1.y;b2PolyShape.tAbsR.col1.y=this.m_R.col1.y*this.m_localOBB.R.col1.x+this.m_R.col2.y*this.m_localOBB.R.col1.y;b2PolyShape.tAbsR.col2.x=this.m_R.col1.x*this.m_localOBB.R.col2.x+this.m_R.col2.x*this.m_localOBB.R.col2.y;b2PolyShape.tAbsR.col2.y=this.m_R.col1.y*this.m_localOBB.R.col2.x+this.m_R.col2.y*this.m_localOBB.R.col2.y;b2PolyShape.tAbsR.Abs() +hX=b2PolyShape.tAbsR.col1.x*this.m_localOBB.extents.x+b2PolyShape.tAbsR.col2.x*this.m_localOBB.extents.y;hY=b2PolyShape.tAbsR.col1.y*this.m_localOBB.extents.x+b2PolyShape.tAbsR.col2.y*this.m_localOBB.extents.y;var positionX=this.m_position.x+(this.m_R.col1.x*this.m_localOBB.center.x+this.m_R.col2.x*this.m_localOBB.center.y);var positionY=this.m_position.y+(this.m_R.col1.y*this.m_localOBB.center.x+this.m_R.col2.y*this.m_localOBB.center.y);aabb.minVertex.x=positionX-hX;aabb.minVertex.y=positionY-hY;aabb.maxVertex.x=positionX+hX;aabb.maxVertex.y=positionY+hY;var broadPhase=this.m_body.m_world.m_broadPhase;if(broadPhase.InRange(aabb)) +{this.m_proxyId=broadPhase.CreateProxy(aabb,this);} +else +{this.m_proxyId=b2Pair.b2_nullProxy;} +if(this.m_proxyId==b2Pair.b2_nullProxy) +{this.m_body.Freeze();}},syncAABB:new b2AABB(),syncMat:new b2Mat22(),Synchronize:function(position1,R1,position2,R2){this.m_R.SetM(R2);this.m_position.x=this.m_body.m_position.x+(R2.col1.x*this.m_localCentroid.x+R2.col2.x*this.m_localCentroid.y);this.m_position.y=this.m_body.m_position.y+(R2.col1.y*this.m_localCentroid.x+R2.col2.y*this.m_localCentroid.y);if(this.m_proxyId==b2Pair.b2_nullProxy) +{return;} +var hX;var hY;var v1=R1.col1;var v2=R1.col2;var v3=this.m_localOBB.R.col1;var v4=this.m_localOBB.R.col2;this.syncMat.col1.x=v1.x*v3.x+v2.x*v3.y;this.syncMat.col1.y=v1.y*v3.x+v2.y*v3.y;this.syncMat.col2.x=v1.x*v4.x+v2.x*v4.y;this.syncMat.col2.y=v1.y*v4.x+v2.y*v4.y;this.syncMat.Abs();hX=this.m_localCentroid.x+this.m_localOBB.center.x;hY=this.m_localCentroid.y+this.m_localOBB.center.y;var centerX=position1.x+(R1.col1.x*hX+R1.col2.x*hY);var centerY=position1.y+(R1.col1.y*hX+R1.col2.y*hY);hX=this.syncMat.col1.x*this.m_localOBB.extents.x+this.syncMat.col2.x*this.m_localOBB.extents.y;hY=this.syncMat.col1.y*this.m_localOBB.extents.x+this.syncMat.col2.y*this.m_localOBB.extents.y;this.syncAABB.minVertex.x=centerX-hX;this.syncAABB.minVertex.y=centerY-hY;this.syncAABB.maxVertex.x=centerX+hX;this.syncAABB.maxVertex.y=centerY+hY;v1=R2.col1;v2=R2.col2;v3=this.m_localOBB.R.col1;v4=this.m_localOBB.R.col2;this.syncMat.col1.x=v1.x*v3.x+v2.x*v3.y;this.syncMat.col1.y=v1.y*v3.x+v2.y*v3.y;this.syncMat.col2.x=v1.x*v4.x+v2.x*v4.y;this.syncMat.col2.y=v1.y*v4.x+v2.y*v4.y;this.syncMat.Abs();hX=this.m_localCentroid.x+this.m_localOBB.center.x;hY=this.m_localCentroid.y+this.m_localOBB.center.y;centerX=position2.x+(R2.col1.x*hX+R2.col2.x*hY);centerY=position2.y+(R2.col1.y*hX+R2.col2.y*hY);hX=this.syncMat.col1.x*this.m_localOBB.extents.x+this.syncMat.col2.x*this.m_localOBB.extents.y;hY=this.syncMat.col1.y*this.m_localOBB.extents.x+this.syncMat.col2.y*this.m_localOBB.extents.y;this.syncAABB.minVertex.x=Math.min(this.syncAABB.minVertex.x,centerX-hX);this.syncAABB.minVertex.y=Math.min(this.syncAABB.minVertex.y,centerY-hY);this.syncAABB.maxVertex.x=Math.max(this.syncAABB.maxVertex.x,centerX+hX);this.syncAABB.maxVertex.y=Math.max(this.syncAABB.maxVertex.y,centerY+hY);var broadPhase=this.m_body.m_world.m_broadPhase;if(broadPhase.InRange(this.syncAABB)) +{broadPhase.MoveProxy(this.m_proxyId,this.syncAABB);} +else +{this.m_body.Freeze();}},QuickSync:function(position,R){this.m_R.SetM(R);this.m_position.x=position.x+(R.col1.x*this.m_localCentroid.x+R.col2.x*this.m_localCentroid.y);this.m_position.y=position.y+(R.col1.y*this.m_localCentroid.x+R.col2.y*this.m_localCentroid.y);},ResetProxy:function(broadPhase){if(this.m_proxyId==b2Pair.b2_nullProxy) +{return;} +var proxy=broadPhase.GetProxy(this.m_proxyId);broadPhase.DestroyProxy(this.m_proxyId);proxy=null;var R=b2Math.b2MulMM(this.m_R,this.m_localOBB.R);var absR=b2Math.b2AbsM(R);var h=b2Math.b2MulMV(absR,this.m_localOBB.extents);var position=b2Math.b2MulMV(this.m_R,this.m_localOBB.center);position.Add(this.m_position);var aabb=new b2AABB();aabb.minVertex.SetV(position);aabb.minVertex.Subtract(h);aabb.maxVertex.SetV(position);aabb.maxVertex.Add(h);if(broadPhase.InRange(aabb)) +{this.m_proxyId=broadPhase.CreateProxy(aabb,this);} +else +{this.m_proxyId=b2Pair.b2_nullProxy;} +if(this.m_proxyId==b2Pair.b2_nullProxy) +{this.m_body.Freeze();}},Support:function(dX,dY,out) +{var dLocalX=(dX*this.m_R.col1.x+dY*this.m_R.col1.y);var dLocalY=(dX*this.m_R.col2.x+dY*this.m_R.col2.y);var bestIndex=0;var bestValue=(this.m_coreVertices[0].x*dLocalX+this.m_coreVertices[0].y*dLocalY);for(var i=1;i<this.m_vertexCount;++i) +{var value=(this.m_coreVertices[i].x*dLocalX+this.m_coreVertices[i].y*dLocalY);if(value>bestValue) +{bestIndex=i;bestValue=value;}} +out.Set(this.m_position.x+(this.m_R.col1.x*this.m_coreVertices[bestIndex].x+this.m_R.col2.x*this.m_coreVertices[bestIndex].y),this.m_position.y+(this.m_R.col1.y*this.m_coreVertices[bestIndex].x+this.m_R.col2.y*this.m_coreVertices[bestIndex].y));},m_localCentroid:new b2Vec2(),m_localOBB:new b2OBB(),m_vertices:null,m_coreVertices:null,m_vertexCount:0,m_normals:null});b2PolyShape.tempVec=new b2Vec2();b2PolyShape.tAbsR=new b2Mat22(); +var b2Body=Class.create();b2Body.prototype={SetOriginPosition:function(position,rotation){if(this.IsFrozen()) +{return;} +this.m_rotation=rotation;this.m_R.Set(this.m_rotation);this.m_position=b2Math.AddVV(position,b2Math.b2MulMV(this.m_R,this.m_center));this.m_position0.SetV(this.m_position);this.m_rotation0=this.m_rotation;for(var s=this.m_shapeList;s!=null;s=s.m_next) +{s.Synchronize(this.m_position,this.m_R,this.m_position,this.m_R);} +this.m_world.m_broadPhase.Commit();},GetOriginPosition:function(){return b2Math.SubtractVV(this.m_position,b2Math.b2MulMV(this.m_R,this.m_center));},SetCenterPosition:function(position,rotation){if(this.IsFrozen()) +{return;} +this.m_rotation=rotation;this.m_R.Set(this.m_rotation);this.m_position.SetV(position);this.m_position0.SetV(this.m_position);this.m_rotation0=this.m_rotation;for(var s=this.m_shapeList;s!=null;s=s.m_next) +{s.Synchronize(this.m_position,this.m_R,this.m_position,this.m_R);} +this.m_world.m_broadPhase.Commit();},GetCenterPosition:function(){return this.m_position;},GetRotation:function(){return this.m_rotation;},GetRotationMatrix:function(){return this.m_R;},SetLinearVelocity:function(v){this.m_linearVelocity.SetV(v);},GetLinearVelocity:function(){return this.m_linearVelocity;},SetAngularVelocity:function(w){this.m_angularVelocity=w;},GetAngularVelocity:function(){return this.m_angularVelocity;},ApplyForce:function(force,point) +{if(this.IsSleeping()==false) +{this.m_force.Add(force);this.m_torque+=b2Math.b2CrossVV(b2Math.SubtractVV(point,this.m_position),force);}},ApplyTorque:function(torque) +{if(this.IsSleeping()==false) +{this.m_torque+=torque;}},ApplyImpulse:function(impulse,point) +{if(this.IsSleeping()==false) +{this.m_linearVelocity.Add(b2Math.MulFV(this.m_invMass,impulse));this.m_angularVelocity+=(this.m_invI*b2Math.b2CrossVV(b2Math.SubtractVV(point,this.m_position),impulse));}},GetMass:function(){return this.m_mass;},GetInertia:function(){return this.m_I;},GetWorldPoint:function(localPoint){return b2Math.AddVV(this.m_position,b2Math.b2MulMV(this.m_R,localPoint));},GetWorldVector:function(localVector){return b2Math.b2MulMV(this.m_R,localVector);},GetLocalPoint:function(worldPoint){return b2Math.b2MulTMV(this.m_R,b2Math.SubtractVV(worldPoint,this.m_position));},GetLocalVector:function(worldVector){return b2Math.b2MulTMV(this.m_R,worldVector);},IsStatic:function(){return(this.m_flags&b2Body.e_staticFlag)==b2Body.e_staticFlag;},IsFrozen:function() +{return(this.m_flags&b2Body.e_frozenFlag)==b2Body.e_frozenFlag;},IsSleeping:function(){return(this.m_flags&b2Body.e_sleepFlag)==b2Body.e_sleepFlag;},AllowSleeping:function(flag) +{if(flag) +{this.m_flags|=b2Body.e_allowSleepFlag;} +else +{this.m_flags&=~b2Body.e_allowSleepFlag;this.WakeUp();}},WakeUp:function(){this.m_flags&=~b2Body.e_sleepFlag;this.m_sleepTime=0.0;},GetShapeList:function(){return this.m_shapeList;},GetContactList:function() +{return this.m_contactList;},GetJointList:function() +{return this.m_jointList;},GetNext:function(){return this.m_next;},GetUserData:function(){return this.m_userData;},initialize:function(bd,world){this.sMat0=new b2Mat22();this.m_position=new b2Vec2();this.m_R=new b2Mat22(0);this.m_position0=new b2Vec2();var i=0;var sd;var massData;this.m_flags=0;this.m_position.SetV(bd.position);this.m_rotation=bd.rotation;this.m_R.Set(this.m_rotation);this.m_position0.SetV(this.m_position);this.m_rotation0=this.m_rotation;this.m_world=world;this.m_linearDamping=b2Math.b2Clamp(1.0-bd.linearDamping,0.0,1.0);this.m_angularDamping=b2Math.b2Clamp(1.0-bd.angularDamping,0.0,1.0);this.m_force=new b2Vec2(0.0,0.0);this.m_torque=0.0;this.m_mass=0.0;var massDatas=new Array(b2Settings.b2_maxShapesPerBody);for(i=0;i<b2Settings.b2_maxShapesPerBody;i++){massDatas[i]=new b2MassData();} +this.m_shapeCount=0;this.m_center=new b2Vec2(0.0,0.0);for(i=0;i<b2Settings.b2_maxShapesPerBody;++i) +{sd=bd.shapes[i];if(sd==null)break;massData=massDatas[i];sd.ComputeMass(massData);this.m_mass+=massData.mass;this.m_center.x+=massData.mass*(sd.localPosition.x+massData.center.x);this.m_center.y+=massData.mass*(sd.localPosition.y+massData.center.y);++this.m_shapeCount;} +if(this.m_mass>0.0) +{this.m_center.Multiply(1.0/this.m_mass);this.m_position.Add(b2Math.b2MulMV(this.m_R,this.m_center));} +else +{this.m_flags|=b2Body.e_staticFlag;} +this.m_I=0.0;for(i=0;i<this.m_shapeCount;++i) +{sd=bd.shapes[i];massData=massDatas[i];this.m_I+=massData.I;var r=b2Math.SubtractVV(b2Math.AddVV(sd.localPosition,massData.center),this.m_center);this.m_I+=massData.mass*b2Math.b2Dot(r,r);} +if(this.m_mass>0.0) +{this.m_invMass=1.0/this.m_mass;} +else +{this.m_invMass=0.0;} +if(this.m_I>0.0&&bd.preventRotation==false) +{this.m_invI=1.0/this.m_I;} +else +{this.m_I=0.0;this.m_invI=0.0;} +this.m_linearVelocity=b2Math.AddVV(bd.linearVelocity,b2Math.b2CrossFV(bd.angularVelocity,this.m_center));this.m_angularVelocity=bd.angularVelocity;this.m_jointList=null;this.m_contactList=null;this.m_prev=null;this.m_next=null;this.m_shapeList=null;for(i=0;i<this.m_shapeCount;++i) +{sd=bd.shapes[i];var shape=b2Shape.Create(sd,this,this.m_center);shape.m_next=this.m_shapeList;this.m_shapeList=shape;} +this.m_sleepTime=0.0;if(bd.allowSleep) +{this.m_flags|=b2Body.e_allowSleepFlag;} +if(bd.isSleeping) +{this.m_flags|=b2Body.e_sleepFlag;} +if((this.m_flags&b2Body.e_sleepFlag)||this.m_invMass==0.0) +{this.m_linearVelocity.Set(0.0,0.0);this.m_angularVelocity=0.0;} +this.m_userData=bd.userData;},Destroy:function(){var s=this.m_shapeList;while(s) +{var s0=s;s=s.m_next;b2Shape.Destroy(s0);}},sMat0:new b2Mat22(),SynchronizeShapes:function(){this.sMat0.Set(this.m_rotation0);for(var s=this.m_shapeList;s!=null;s=s.m_next) +{s.Synchronize(this.m_position0,this.sMat0,this.m_position,this.m_R);}},QuickSyncShapes:function(){for(var s=this.m_shapeList;s!=null;s=s.m_next) +{s.QuickSync(this.m_position,this.m_R);}},IsConnected:function(other){for(var jn=this.m_jointList;jn!=null;jn=jn.next) +{if(jn.other==other) +return jn.joint.m_collideConnected==false;} +return false;},Freeze:function(){this.m_flags|=b2Body.e_frozenFlag;this.m_linearVelocity.SetZero();this.m_angularVelocity=0.0;for(var s=this.m_shapeList;s!=null;s=s.m_next) +{s.DestroyProxy();}},m_flags:0,m_position:new b2Vec2(),m_rotation:null,m_R:new b2Mat22(0),m_position0:new b2Vec2(),m_rotation0:null,m_linearVelocity:null,m_angularVelocity:null,m_force:null,m_torque:null,m_center:null,m_world:null,m_prev:null,m_next:null,m_shapeList:null,m_shapeCount:0,m_jointList:null,m_contactList:null,m_mass:null,m_invMass:null,m_I:null,m_invI:null,m_linearDamping:null,m_angularDamping:null,m_sleepTime:null,m_userData:null};b2Body.e_staticFlag=0x0001;b2Body.e_frozenFlag=0x0002;b2Body.e_islandFlag=0x0004;b2Body.e_sleepFlag=0x0008;b2Body.e_allowSleepFlag=0x0010;b2Body.e_destroyFlag=0x0020; +var b2BodyDef=Class.create();b2BodyDef.prototype={initialize:function() +{this.shapes=new Array();this.userData=null;for(var i=0;i<b2Settings.b2_maxShapesPerBody;i++){this.shapes[i]=null;} +this.position=new b2Vec2(0.0,0.0);this.rotation=0.0;this.linearVelocity=new b2Vec2(0.0,0.0);this.angularVelocity=0.0;this.linearDamping=0.0;this.angularDamping=0.0;this.allowSleep=true;this.isSleeping=false;this.preventRotation=false;},userData:null,shapes:new Array(),position:null,rotation:null,linearVelocity:null,angularVelocity:null,linearDamping:null,angularDamping:null,allowSleep:null,isSleeping:null,preventRotation:null,AddShape:function(shape) +{for(var i=0;i<b2Settings.b2_maxShapesPerBody;++i) +{if(this.shapes[i]==null) +{this.shapes[i]=shape;break;}}}}; +var b2CollisionFilter=Class.create();b2CollisionFilter.prototype={ShouldCollide:function(shape1,shape2){if(shape1.m_groupIndex==shape2.m_groupIndex&&shape1.m_groupIndex!=0) +{return shape1.m_groupIndex>0;} +var collide=(shape1.m_maskBits&shape2.m_categoryBits)!=0&&(shape1.m_categoryBits&shape2.m_maskBits)!=0;return collide;},initialize:function(){}};b2CollisionFilter.b2_defaultFilter=new b2CollisionFilter; +var b2Island=Class.create();b2Island.prototype={initialize:function(bodyCapacity,contactCapacity,jointCapacity,allocator) +{var i=0;this.m_bodyCapacity=bodyCapacity;this.m_contactCapacity=contactCapacity;this.m_jointCapacity=jointCapacity;this.m_bodyCount=0;this.m_contactCount=0;this.m_jointCount=0;this.m_bodies=new Array(bodyCapacity);for(i=0;i<bodyCapacity;i++) +this.m_bodies[i]=null;this.m_contacts=new Array(contactCapacity);for(i=0;i<contactCapacity;i++) +this.m_contacts[i]=null;this.m_joints=new Array(jointCapacity);for(i=0;i<jointCapacity;i++) +this.m_joints[i]=null;this.m_allocator=allocator;},Clear:function() +{this.m_bodyCount=0;this.m_contactCount=0;this.m_jointCount=0;},Solve:function(step,gravity) +{var i=0;var b;for(i=0;i<this.m_bodyCount;++i) +{b=this.m_bodies[i];if(b.m_invMass==0.0) +continue;b.m_linearVelocity.Add(b2Math.MulFV(step.dt,b2Math.AddVV(gravity,b2Math.MulFV(b.m_invMass,b.m_force))));b.m_angularVelocity+=step.dt*b.m_invI*b.m_torque;b.m_linearVelocity.Multiply(b.m_linearDamping);b.m_angularVelocity*=b.m_angularDamping;b.m_position0.SetV(b.m_position);b.m_rotation0=b.m_rotation;} +var contactSolver=new b2ContactSolver(this.m_contacts,this.m_contactCount,this.m_allocator);contactSolver.PreSolve();for(i=0;i<this.m_jointCount;++i) +{this.m_joints[i].PrepareVelocitySolver();} +for(i=0;i<step.iterations;++i) +{contactSolver.SolveVelocityConstraints();for(var j=0;j<this.m_jointCount;++j) +{this.m_joints[j].SolveVelocityConstraints(step);}} +for(i=0;i<this.m_bodyCount;++i) +{b=this.m_bodies[i];if(b.m_invMass==0.0) +continue;b.m_position.x+=step.dt*b.m_linearVelocity.x;b.m_position.y+=step.dt*b.m_linearVelocity.y;b.m_rotation+=step.dt*b.m_angularVelocity;b.m_R.Set(b.m_rotation);} +for(i=0;i<this.m_jointCount;++i) +{this.m_joints[i].PreparePositionSolver();} +if(b2World.s_enablePositionCorrection) +{for(b2Island.m_positionIterationCount=0;b2Island.m_positionIterationCount<step.iterations;++b2Island.m_positionIterationCount) +{var contactsOkay=contactSolver.SolvePositionConstraints(b2Settings.b2_contactBaumgarte);var jointsOkay=true;for(i=0;i<this.m_jointCount;++i) +{var jointOkay=this.m_joints[i].SolvePositionConstraints();jointsOkay=jointsOkay&&jointOkay;} +if(contactsOkay&&jointsOkay) +{break;}}} +contactSolver.PostSolve();for(i=0;i<this.m_bodyCount;++i) +{b=this.m_bodies[i];if(b.m_invMass==0.0) +continue;b.m_R.Set(b.m_rotation);b.SynchronizeShapes();b.m_force.Set(0.0,0.0);b.m_torque=0.0;}},UpdateSleep:function(dt) +{var i=0;var b;var minSleepTime=Number.MAX_VALUE;var linTolSqr=b2Settings.b2_linearSleepTolerance*b2Settings.b2_linearSleepTolerance;var angTolSqr=b2Settings.b2_angularSleepTolerance*b2Settings.b2_angularSleepTolerance;for(i=0;i<this.m_bodyCount;++i) +{b=this.m_bodies[i];if(b.m_invMass==0.0) +{continue;} +if((b.m_flags&b2Body.e_allowSleepFlag)==0) +{b.m_sleepTime=0.0;minSleepTime=0.0;} +if((b.m_flags&b2Body.e_allowSleepFlag)==0||b.m_angularVelocity*b.m_angularVelocity>angTolSqr||b2Math.b2Dot(b.m_linearVelocity,b.m_linearVelocity)>linTolSqr) +{b.m_sleepTime=0.0;minSleepTime=0.0;} +else +{b.m_sleepTime+=dt;minSleepTime=b2Math.b2Min(minSleepTime,b.m_sleepTime);}} +if(minSleepTime>=b2Settings.b2_timeToSleep) +{for(i=0;i<this.m_bodyCount;++i) +{b=this.m_bodies[i];b.m_flags|=b2Body.e_sleepFlag;}}},AddBody:function(body) +{this.m_bodies[this.m_bodyCount++]=body;},AddContact:function(contact) +{this.m_contacts[this.m_contactCount++]=contact;},AddJoint:function(joint) +{this.m_joints[this.m_jointCount++]=joint;},m_allocator:null,m_bodies:null,m_contacts:null,m_joints:null,m_bodyCount:0,m_jointCount:0,m_contactCount:0,m_bodyCapacity:0,m_contactCapacity:0,m_jointCapacity:0,m_positionError:null};b2Island.m_positionIterationCount=0; +var b2TimeStep=Class.create();b2TimeStep.prototype={dt:null,inv_dt:null,iterations:0,initialize:function(){}}; +var b2ContactNode=Class.create();b2ContactNode.prototype={other:null,contact:null,prev:null,next:null,initialize:function(){}}; +var b2Contact=Class.create();b2Contact.prototype={GetManifolds:function(){return null},GetManifoldCount:function() +{return this.m_manifoldCount;},GetNext:function(){return this.m_next;},GetShape1:function(){return this.m_shape1;},GetShape2:function(){return this.m_shape2;},initialize:function(s1,s2) +{this.m_node1=new b2ContactNode();this.m_node2=new b2ContactNode();this.m_flags=0;if(!s1||!s2){this.m_shape1=null;this.m_shape2=null;return;} +this.m_shape1=s1;this.m_shape2=s2;this.m_manifoldCount=0;this.m_friction=Math.sqrt(this.m_shape1.m_friction*this.m_shape2.m_friction);this.m_restitution=b2Math.b2Max(this.m_shape1.m_restitution,this.m_shape2.m_restitution);this.m_prev=null;this.m_next=null;this.m_node1.contact=null;this.m_node1.prev=null;this.m_node1.next=null;this.m_node1.other=null;this.m_node2.contact=null;this.m_node2.prev=null;this.m_node2.next=null;this.m_node2.other=null;},Evaluate:function(){},m_flags:0,m_prev:null,m_next:null,m_node1:new b2ContactNode(),m_node2:new b2ContactNode(),m_shape1:null,m_shape2:null,m_manifoldCount:0,m_friction:null,m_restitution:null};b2Contact.e_islandFlag=0x0001;b2Contact.e_destroyFlag=0x0002;b2Contact.AddType=function(createFcn,destroyFcn,type1,type2) +{b2Contact.s_registers[type1][type2].createFcn=createFcn;b2Contact.s_registers[type1][type2].destroyFcn=destroyFcn;b2Contact.s_registers[type1][type2].primary=true;if(type1!=type2) +{b2Contact.s_registers[type2][type1].createFcn=createFcn;b2Contact.s_registers[type2][type1].destroyFcn=destroyFcn;b2Contact.s_registers[type2][type1].primary=false;}};b2Contact.InitializeRegisters=function(){b2Contact.s_registers=new Array(b2Shape.e_shapeTypeCount);for(var i=0;i<b2Shape.e_shapeTypeCount;i++){b2Contact.s_registers[i]=new Array(b2Shape.e_shapeTypeCount);for(var j=0;j<b2Shape.e_shapeTypeCount;j++){b2Contact.s_registers[i][j]=new b2ContactRegister();}} +b2Contact.AddType(b2CircleContact.Create,b2CircleContact.Destroy,b2Shape.e_circleShape,b2Shape.e_circleShape);b2Contact.AddType(b2PolyAndCircleContact.Create,b2PolyAndCircleContact.Destroy,b2Shape.e_polyShape,b2Shape.e_circleShape);b2Contact.AddType(b2PolyContact.Create,b2PolyContact.Destroy,b2Shape.e_polyShape,b2Shape.e_polyShape);};b2Contact.Create=function(shape1,shape2,allocator){if(b2Contact.s_initialized==false) +{b2Contact.InitializeRegisters();b2Contact.s_initialized=true;} +var type1=shape1.m_type;var type2=shape2.m_type;var createFcn=b2Contact.s_registers[type1][type2].createFcn;if(createFcn) +{if(b2Contact.s_registers[type1][type2].primary) +{return createFcn(shape1,shape2,allocator);} +else +{var c=createFcn(shape2,shape1,allocator);for(var i=0;i<c.GetManifoldCount();++i) +{var m=c.GetManifolds()[i];m.normal=m.normal.Negative();} +return c;}} +else +{return null;}};b2Contact.Destroy=function(contact,allocator){if(contact.GetManifoldCount()>0) +{contact.m_shape1.m_body.WakeUp();contact.m_shape2.m_body.WakeUp();} +var type1=contact.m_shape1.m_type;var type2=contact.m_shape2.m_type;var destroyFcn=b2Contact.s_registers[type1][type2].destroyFcn;destroyFcn(contact,allocator);};b2Contact.s_registers=null;b2Contact.s_initialized=false; +var b2ContactConstraint=Class.create();b2ContactConstraint.prototype={initialize:function(){this.normal=new b2Vec2();this.points=new Array(b2Settings.b2_maxManifoldPoints);for(var i=0;i<b2Settings.b2_maxManifoldPoints;i++){this.points[i]=new b2ContactConstraintPoint();}},points:null,normal:new b2Vec2(),manifold:null,body1:null,body2:null,friction:null,restitution:null,pointCount:0}; +var b2ContactConstraintPoint=Class.create();b2ContactConstraintPoint.prototype={localAnchor1:new b2Vec2(),localAnchor2:new b2Vec2(),normalImpulse:null,tangentImpulse:null,positionImpulse:null,normalMass:null,tangentMass:null,separation:null,velocityBias:null,initialize:function(){this.localAnchor1=new b2Vec2();this.localAnchor2=new b2Vec2();}}; +var b2ContactRegister=Class.create();b2ContactRegister.prototype={createFcn:null,destroyFcn:null,primary:null,initialize:function(){}}; +var b2ContactSolver=Class.create();b2ContactSolver.prototype={initialize:function(contacts,contactCount,allocator){this.m_constraints=new Array();this.m_allocator=allocator;var i=0;var tVec;var tMat;this.m_constraintCount=0;for(i=0;i<contactCount;++i) +{this.m_constraintCount+=contacts[i].GetManifoldCount();} +for(i=0;i<this.m_constraintCount;i++){this.m_constraints[i]=new b2ContactConstraint();} +var count=0;for(i=0;i<contactCount;++i) +{var contact=contacts[i];var b1=contact.m_shape1.m_body;var b2=contact.m_shape2.m_body;var manifoldCount=contact.GetManifoldCount();var manifolds=contact.GetManifolds();var friction=contact.m_friction;var restitution=contact.m_restitution;var v1X=b1.m_linearVelocity.x;var v1Y=b1.m_linearVelocity.y;var v2X=b2.m_linearVelocity.x;var v2Y=b2.m_linearVelocity.y;var w1=b1.m_angularVelocity;var w2=b2.m_angularVelocity;for(var j=0;j<manifoldCount;++j) +{var manifold=manifolds[j];var normalX=manifold.normal.x;var normalY=manifold.normal.y;var c=this.m_constraints[count];c.body1=b1;c.body2=b2;c.manifold=manifold;c.normal.x=normalX;c.normal.y=normalY;c.pointCount=manifold.pointCount;c.friction=friction;c.restitution=restitution;for(var k=0;k<c.pointCount;++k) +{var cp=manifold.points[k];var ccp=c.points[k];ccp.normalImpulse=cp.normalImpulse;ccp.tangentImpulse=cp.tangentImpulse;ccp.separation=cp.separation;var r1X=cp.position.x-b1.m_position.x;var r1Y=cp.position.y-b1.m_position.y;var r2X=cp.position.x-b2.m_position.x;var r2Y=cp.position.y-b2.m_position.y;tVec=ccp.localAnchor1;tMat=b1.m_R;tVec.x=r1X*tMat.col1.x+r1Y*tMat.col1.y;tVec.y=r1X*tMat.col2.x+r1Y*tMat.col2.y;tVec=ccp.localAnchor2;tMat=b2.m_R;tVec.x=r2X*tMat.col1.x+r2Y*tMat.col1.y;tVec.y=r2X*tMat.col2.x+r2Y*tMat.col2.y;var r1Sqr=r1X*r1X+r1Y*r1Y;var r2Sqr=r2X*r2X+r2Y*r2Y;var rn1=r1X*normalX+r1Y*normalY;var rn2=r2X*normalX+r2Y*normalY;var kNormal=b1.m_invMass+b2.m_invMass;kNormal+=b1.m_invI*(r1Sqr-rn1*rn1)+b2.m_invI*(r2Sqr-rn2*rn2);ccp.normalMass=1.0/kNormal;var tangentX=normalY +var tangentY=-normalX;var rt1=r1X*tangentX+r1Y*tangentY;var rt2=r2X*tangentX+r2Y*tangentY;var kTangent=b1.m_invMass+b2.m_invMass;kTangent+=b1.m_invI*(r1Sqr-rt1*rt1)+b2.m_invI*(r2Sqr-rt2*rt2);ccp.tangentMass=1.0/kTangent;ccp.velocityBias=0.0;if(ccp.separation>0.0) +{ccp.velocityBias=-60.0*ccp.separation;} +var tX=v2X+(-w2*r2Y)-v1X-(-w1*r1Y);var tY=v2Y+(w2*r2X)-v1Y-(w1*r1X);var vRel=c.normal.x*tX+c.normal.y*tY;if(vRel<-b2Settings.b2_velocityThreshold) +{ccp.velocityBias+=-c.restitution*vRel;}} +++count;}}},PreSolve:function(){var tVec;var tVec2;var tMat;for(var i=0;i<this.m_constraintCount;++i) +{var c=this.m_constraints[i];var b1=c.body1;var b2=c.body2;var invMass1=b1.m_invMass;var invI1=b1.m_invI;var invMass2=b2.m_invMass;var invI2=b2.m_invI;var normalX=c.normal.x;var normalY=c.normal.y;var tangentX=normalY;var tangentY=-normalX;var j=0;var tCount=0;if(b2World.s_enableWarmStarting) +{tCount=c.pointCount;for(j=0;j<tCount;++j) +{var ccp=c.points[j];var PX=ccp.normalImpulse*normalX+ccp.tangentImpulse*tangentX;var PY=ccp.normalImpulse*normalY+ccp.tangentImpulse*tangentY;tMat=b1.m_R;tVec=ccp.localAnchor1;var r1X=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y;var r1Y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y;tMat=b2.m_R;tVec=ccp.localAnchor2;var r2X=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y;var r2Y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y;b1.m_angularVelocity-=invI1*(r1X*PY-r1Y*PX);b1.m_linearVelocity.x-=invMass1*PX;b1.m_linearVelocity.y-=invMass1*PY;b2.m_angularVelocity+=invI2*(r2X*PY-r2Y*PX);b2.m_linearVelocity.x+=invMass2*PX;b2.m_linearVelocity.y+=invMass2*PY;ccp.positionImpulse=0.0;}} +else{tCount=c.pointCount;for(j=0;j<tCount;++j) +{var ccp2=c.points[j];ccp2.normalImpulse=0.0;ccp2.tangentImpulse=0.0;ccp2.positionImpulse=0.0;}}}},SolveVelocityConstraints:function(){var j=0;var ccp;var r1X;var r1Y;var r2X;var r2Y;var dvX;var dvY;var lambda;var newImpulse;var PX;var PY;var tMat;var tVec;for(var i=0;i<this.m_constraintCount;++i) +{var c=this.m_constraints[i];var b1=c.body1;var b2=c.body2;var b1_angularVelocity=b1.m_angularVelocity;var b1_linearVelocity=b1.m_linearVelocity;var b2_angularVelocity=b2.m_angularVelocity;var b2_linearVelocity=b2.m_linearVelocity;var invMass1=b1.m_invMass;var invI1=b1.m_invI;var invMass2=b2.m_invMass;var invI2=b2.m_invI;var normalX=c.normal.x;var normalY=c.normal.y;var tangentX=normalY;var tangentY=-normalX;var tCount=c.pointCount;for(j=0;j<tCount;++j) +{ccp=c.points[j];tMat=b1.m_R;tVec=ccp.localAnchor1;r1X=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y +r1Y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y +tMat=b2.m_R;tVec=ccp.localAnchor2;r2X=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y +r2Y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y +dvX=b2_linearVelocity.x+(-b2_angularVelocity*r2Y)-b1_linearVelocity.x-(-b1_angularVelocity*r1Y);dvY=b2_linearVelocity.y+(b2_angularVelocity*r2X)-b1_linearVelocity.y-(b1_angularVelocity*r1X);var vn=dvX*normalX+dvY*normalY;lambda=-ccp.normalMass*(vn-ccp.velocityBias);newImpulse=b2Math.b2Max(ccp.normalImpulse+lambda,0.0);lambda=newImpulse-ccp.normalImpulse;PX=lambda*normalX;PY=lambda*normalY;b1_linearVelocity.x-=invMass1*PX;b1_linearVelocity.y-=invMass1*PY;b1_angularVelocity-=invI1*(r1X*PY-r1Y*PX);b2_linearVelocity.x+=invMass2*PX;b2_linearVelocity.y+=invMass2*PY;b2_angularVelocity+=invI2*(r2X*PY-r2Y*PX);ccp.normalImpulse=newImpulse;dvX=b2_linearVelocity.x+(-b2_angularVelocity*r2Y)-b1_linearVelocity.x-(-b1_angularVelocity*r1Y);dvY=b2_linearVelocity.y+(b2_angularVelocity*r2X)-b1_linearVelocity.y-(b1_angularVelocity*r1X);var vt=dvX*tangentX+dvY*tangentY;lambda=ccp.tangentMass*(-vt);var maxFriction=c.friction*ccp.normalImpulse;newImpulse=b2Math.b2Clamp(ccp.tangentImpulse+lambda,-maxFriction,maxFriction);lambda=newImpulse-ccp.tangentImpulse;PX=lambda*tangentX;PY=lambda*tangentY;b1_linearVelocity.x-=invMass1*PX;b1_linearVelocity.y-=invMass1*PY;b1_angularVelocity-=invI1*(r1X*PY-r1Y*PX);b2_linearVelocity.x+=invMass2*PX;b2_linearVelocity.y+=invMass2*PY;b2_angularVelocity+=invI2*(r2X*PY-r2Y*PX);ccp.tangentImpulse=newImpulse;} +b1.m_angularVelocity=b1_angularVelocity;b2.m_angularVelocity=b2_angularVelocity;}},SolvePositionConstraints:function(beta){var minSeparation=0.0;var tMat;var tVec;for(var i=0;i<this.m_constraintCount;++i) +{var c=this.m_constraints[i];var b1=c.body1;var b2=c.body2;var b1_position=b1.m_position;var b1_rotation=b1.m_rotation;var b2_position=b2.m_position;var b2_rotation=b2.m_rotation;var invMass1=b1.m_invMass;var invI1=b1.m_invI;var invMass2=b2.m_invMass;var invI2=b2.m_invI;var normalX=c.normal.x;var normalY=c.normal.y;var tangentX=normalY;var tangentY=-normalX;var tCount=c.pointCount;for(var j=0;j<tCount;++j) +{var ccp=c.points[j];tMat=b1.m_R;tVec=ccp.localAnchor1;var r1X=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y +var r1Y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y +tMat=b2.m_R;tVec=ccp.localAnchor2;var r2X=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y +var r2Y=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y +var p1X=b1_position.x+r1X;var p1Y=b1_position.y+r1Y;var p2X=b2_position.x+r2X;var p2Y=b2_position.y+r2Y;var dpX=p2X-p1X;var dpY=p2Y-p1Y;var separation=(dpX*normalX+dpY*normalY)+ccp.separation;minSeparation=b2Math.b2Min(minSeparation,separation);var C=beta*b2Math.b2Clamp(separation+b2Settings.b2_linearSlop,-b2Settings.b2_maxLinearCorrection,0.0);var dImpulse=-ccp.normalMass*C;var impulse0=ccp.positionImpulse;ccp.positionImpulse=b2Math.b2Max(impulse0+dImpulse,0.0);dImpulse=ccp.positionImpulse-impulse0;var impulseX=dImpulse*normalX;var impulseY=dImpulse*normalY;b1_position.x-=invMass1*impulseX;b1_position.y-=invMass1*impulseY;b1_rotation-=invI1*(r1X*impulseY-r1Y*impulseX);b1.m_R.Set(b1_rotation);b2_position.x+=invMass2*impulseX;b2_position.y+=invMass2*impulseY;b2_rotation+=invI2*(r2X*impulseY-r2Y*impulseX);b2.m_R.Set(b2_rotation);} +b1.m_rotation=b1_rotation;b2.m_rotation=b2_rotation;} +return minSeparation>=-b2Settings.b2_linearSlop;},PostSolve:function(){for(var i=0;i<this.m_constraintCount;++i) +{var c=this.m_constraints[i];var m=c.manifold;for(var j=0;j<c.pointCount;++j) +{var mPoint=m.points[j];var cPoint=c.points[j];mPoint.normalImpulse=cPoint.normalImpulse;mPoint.tangentImpulse=cPoint.tangentImpulse;}}},m_allocator:null,m_constraints:new Array(),m_constraintCount:0}; +var b2CircleContact=Class.create();Object.extend(b2CircleContact.prototype,b2Contact.prototype);Object.extend(b2CircleContact.prototype,{initialize:function(s1,s2){this.m_node1=new b2ContactNode();this.m_node2=new b2ContactNode();this.m_flags=0;if(!s1||!s2){this.m_shape1=null;this.m_shape2=null;return;} +this.m_shape1=s1;this.m_shape2=s2;this.m_manifoldCount=0;this.m_friction=Math.sqrt(this.m_shape1.m_friction*this.m_shape2.m_friction);this.m_restitution=b2Math.b2Max(this.m_shape1.m_restitution,this.m_shape2.m_restitution);this.m_prev=null;this.m_next=null;this.m_node1.contact=null;this.m_node1.prev=null;this.m_node1.next=null;this.m_node1.other=null;this.m_node2.contact=null;this.m_node2.prev=null;this.m_node2.next=null;this.m_node2.other=null;this.m_manifold=[new b2Manifold()];this.m_manifold[0].pointCount=0;this.m_manifold[0].points[0].normalImpulse=0.0;this.m_manifold[0].points[0].tangentImpulse=0.0;},Evaluate:function(){b2Collision.b2CollideCircle(this.m_manifold[0],this.m_shape1,this.m_shape2,false);if(this.m_manifold[0].pointCount>0) +{this.m_manifoldCount=1;} +else +{this.m_manifoldCount=0;}},GetManifolds:function() +{return this.m_manifold;},m_manifold:[new b2Manifold()]});b2CircleContact.Create=function(shape1,shape2,allocator){return new b2CircleContact(shape1,shape2);};b2CircleContact.Destroy=function(contact,allocator){}; +var b2Conservative=Class.create();b2Conservative.prototype={initialize:function(){}} +b2Conservative.R1=new b2Mat22();b2Conservative.R2=new b2Mat22();b2Conservative.x1=new b2Vec2();b2Conservative.x2=new b2Vec2();b2Conservative.Conservative=function(shape1,shape2){var body1=shape1.GetBody();var body2=shape2.GetBody();var v1X=body1.m_position.x-body1.m_position0.x;var v1Y=body1.m_position.y-body1.m_position0.y;var omega1=body1.m_rotation-body1.m_rotation0;var v2X=body2.m_position.x-body2.m_position0.x;var v2Y=body2.m_position.y-body2.m_position0.y;var omega2=body2.m_rotation-body2.m_rotation0;var r1=shape1.GetMaxRadius();var r2=shape2.GetMaxRadius();var p1StartX=body1.m_position0.x;var p1StartY=body1.m_position0.y;var a1Start=body1.m_rotation0;var p2StartX=body2.m_position0.x;var p2StartY=body2.m_position0.y;var a2Start=body2.m_rotation0;var p1X=p1StartX;var p1Y=p1StartY;var a1=a1Start;var p2X=p2StartX;var p2Y=p2StartY;var a2=a2Start;b2Conservative.R1.Set(a1);b2Conservative.R2.Set(a2);shape1.QuickSync(p1,b2Conservative.R1);shape2.QuickSync(p2,b2Conservative.R2);var s1=0.0;var maxIterations=10;var dX;var dY;var invRelativeVelocity=0.0;var hit=true;for(var iter=0;iter<maxIterations;++iter) +{var distance=b2Distance.Distance(b2Conservative.x1,b2Conservative.x2,shape1,shape2);if(distance<b2Settings.b2_linearSlop) +{if(iter==0) +{hit=false;} +else +{hit=true;} +break;} +if(iter==0) +{dX=b2Conservative.x2.x-b2Conservative.x1.x;dY=b2Conservative.x2.y-b2Conservative.x1.y;var dLen=Math.sqrt(dX*dX+dY*dY);var relativeVelocity=(dX*(v1X-v2X)+dY*(v1Y-v2Y))+Math.abs(omega1)*r1+Math.abs(omega2)*r2;if(Math.abs(relativeVelocity)<Number.MIN_VALUE) +{hit=false;break;} +invRelativeVelocity=1.0/relativeVelocity;} +var ds=distance*invRelativeVelocity;var s2=s1+ds;if(s2<0.0||1.0<s2) +{hit=false;break;} +if(s2<(1.0+100.0*Number.MIN_VALUE)*s1) +{hit=true;break;} +s1=s2;p1X=p1StartX+s1*v1.x;p1Y=p1StartY+s1*v1.y;a1=a1Start+s1*omega1;p2X=p2StartX+s1*v2.x;p2Y=p2StartY+s1*v2.y;a2=a2Start+s1*omega2;b2Conservative.R1.Set(a1);b2Conservative.R2.Set(a2);shape1.QuickSync(p1,b2Conservative.R1);shape2.QuickSync(p2,b2Conservative.R2);} +if(hit) +{dX=b2Conservative.x2.x-b2Conservative.x1.x;dY=b2Conservative.x2.y-b2Conservative.x1.y;var length=Math.sqrt(dX*dX+dY*dY);if(length>FLT_EPSILON) +{d*=b2_linearSlop/length;} +if(body1.IsStatic()) +{body1.m_position.x=p1X;body1.m_position.y=p1Y;} +else +{body1.m_position.x=p1X-dX;body1.m_position.y=p1Y-dY;} +body1.m_rotation=a1;body1.m_R.Set(a1);body1.QuickSyncShapes();if(body2.IsStatic()) +{body2.m_position.x=p2X;body2.m_position.y=p2Y;} +else +{body2.m_position.x=p2X+dX;body2.m_position.y=p2Y+dY;} +body2.m_position.x=p2X+dX;body2.m_position.y=p2Y+dY;body2.m_rotation=a2;body2.m_R.Set(a2);body2.QuickSyncShapes();return true;} +shape1.QuickSync(body1.m_position,body1.m_R);shape2.QuickSync(body2.m_position,body2.m_R);return false;}; +var b2NullContact=Class.create();Object.extend(b2NullContact.prototype,b2Contact.prototype);Object.extend(b2NullContact.prototype,{initialize:function(s1,s2){this.m_node1=new b2ContactNode();this.m_node2=new b2ContactNode();this.m_flags=0;if(!s1||!s2){this.m_shape1=null;this.m_shape2=null;return;} +this.m_shape1=s1;this.m_shape2=s2;this.m_manifoldCount=0;this.m_friction=Math.sqrt(this.m_shape1.m_friction*this.m_shape2.m_friction);this.m_restitution=b2Math.b2Max(this.m_shape1.m_restitution,this.m_shape2.m_restitution);this.m_prev=null;this.m_next=null;this.m_node1.contact=null;this.m_node1.prev=null;this.m_node1.next=null;this.m_node1.other=null;this.m_node2.contact=null;this.m_node2.prev=null;this.m_node2.next=null;this.m_node2.other=null;},Evaluate:function(){},GetManifolds:function(){return null;}}); +var b2PolyAndCircleContact=Class.create();Object.extend(b2PolyAndCircleContact.prototype,b2Contact.prototype);Object.extend(b2PolyAndCircleContact.prototype,{initialize:function(s1,s2){this.m_node1=new b2ContactNode();this.m_node2=new b2ContactNode();this.m_flags=0;if(!s1||!s2){this.m_shape1=null;this.m_shape2=null;return;} +this.m_shape1=s1;this.m_shape2=s2;this.m_manifoldCount=0;this.m_friction=Math.sqrt(this.m_shape1.m_friction*this.m_shape2.m_friction);this.m_restitution=b2Math.b2Max(this.m_shape1.m_restitution,this.m_shape2.m_restitution);this.m_prev=null;this.m_next=null;this.m_node1.contact=null;this.m_node1.prev=null;this.m_node1.next=null;this.m_node1.other=null;this.m_node2.contact=null;this.m_node2.prev=null;this.m_node2.next=null;this.m_node2.other=null;this.m_manifold=[new b2Manifold()];b2Settings.b2Assert(this.m_shape1.m_type==b2Shape.e_polyShape);b2Settings.b2Assert(this.m_shape2.m_type==b2Shape.e_circleShape);this.m_manifold[0].pointCount=0;this.m_manifold[0].points[0].normalImpulse=0.0;this.m_manifold[0].points[0].tangentImpulse=0.0;},Evaluate:function(){b2Collision.b2CollidePolyAndCircle(this.m_manifold[0],this.m_shape1,this.m_shape2,false);if(this.m_manifold[0].pointCount>0) +{this.m_manifoldCount=1;} +else +{this.m_manifoldCount=0;}},GetManifolds:function() +{return this.m_manifold;},m_manifold:[new b2Manifold()]}) +b2PolyAndCircleContact.Create=function(shape1,shape2,allocator){return new b2PolyAndCircleContact(shape1,shape2);};b2PolyAndCircleContact.Destroy=function(contact,allocator){}; +var b2PolyContact=Class.create();Object.extend(b2PolyContact.prototype,b2Contact.prototype);Object.extend(b2PolyContact.prototype,{initialize:function(s1,s2){this.m_node1=new b2ContactNode();this.m_node2=new b2ContactNode();this.m_flags=0;if(!s1||!s2){this.m_shape1=null;this.m_shape2=null;return;} +this.m_shape1=s1;this.m_shape2=s2;this.m_manifoldCount=0;this.m_friction=Math.sqrt(this.m_shape1.m_friction*this.m_shape2.m_friction);this.m_restitution=b2Math.b2Max(this.m_shape1.m_restitution,this.m_shape2.m_restitution);this.m_prev=null;this.m_next=null;this.m_node1.contact=null;this.m_node1.prev=null;this.m_node1.next=null;this.m_node1.other=null;this.m_node2.contact=null;this.m_node2.prev=null;this.m_node2.next=null;this.m_node2.other=null;this.m0=new b2Manifold();this.m_manifold=[new b2Manifold()];this.m_manifold[0].pointCount=0;},m0:new b2Manifold(),Evaluate:function(){var tMani=this.m_manifold[0];var tPoints=this.m0.points;for(var k=0;k<tMani.pointCount;k++){var tPoint=tPoints[k];var tPoint0=tMani.points[k];tPoint.normalImpulse=tPoint0.normalImpulse;tPoint.tangentImpulse=tPoint0.tangentImpulse;tPoint.id=tPoint0.id.Copy();} +this.m0.pointCount=tMani.pointCount;b2Collision.b2CollidePoly(tMani,this.m_shape1,this.m_shape2,false);if(tMani.pointCount>0) +{var match=[false,false];for(var i=0;i<tMani.pointCount;++i) +{var cp=tMani.points[i];cp.normalImpulse=0.0;cp.tangentImpulse=0.0;var idKey=cp.id.key;for(var j=0;j<this.m0.pointCount;++j) +{if(match[j]==true) +continue;var cp0=this.m0.points[j];var id0=cp0.id;if(id0.key==idKey) +{match[j]=true;cp.normalImpulse=cp0.normalImpulse;cp.tangentImpulse=cp0.tangentImpulse;break;}}} +this.m_manifoldCount=1;} +else +{this.m_manifoldCount=0;}},GetManifolds:function() +{return this.m_manifold;},m_manifold:[new b2Manifold()]});b2PolyContact.Create=function(shape1,shape2,allocator){return new b2PolyContact(shape1,shape2);};b2PolyContact.Destroy=function(contact,allocator){}; +var b2ContactManager=Class.create();Object.extend(b2ContactManager.prototype,b2PairCallback.prototype);Object.extend(b2ContactManager.prototype,{initialize:function(){this.m_nullContact=new b2NullContact();this.m_world=null;this.m_destroyImmediate=false;},PairAdded:function(proxyUserData1,proxyUserData2){var shape1=proxyUserData1;var shape2=proxyUserData2;var body1=shape1.m_body;var body2=shape2.m_body;if(body1.IsStatic()&&body2.IsStatic()) +{return this.m_nullContact;} +if(shape1.m_body==shape2.m_body) +{return this.m_nullContact;} +if(body2.IsConnected(body1)) +{return this.m_nullContact;} +if(this.m_world.m_filter!=null&&this.m_world.m_filter.ShouldCollide(shape1,shape2)==false) +{return this.m_nullContact;} +if(body2.m_invMass==0.0) +{var tempShape=shape1;shape1=shape2;shape2=tempShape;var tempBody=body1;body1=body2;body2=tempBody;} +var contact=b2Contact.Create(shape1,shape2,this.m_world.m_blockAllocator);if(contact==null) +{return this.m_nullContact;} +else +{contact.m_prev=null;contact.m_next=this.m_world.m_contactList;if(this.m_world.m_contactList!=null) +{this.m_world.m_contactList.m_prev=contact;} +this.m_world.m_contactList=contact;this.m_world.m_contactCount++;} +return contact;},PairRemoved:function(proxyUserData1,proxyUserData2,pairUserData){if(pairUserData==null) +{return;} +var c=pairUserData;if(c!=this.m_nullContact) +{if(this.m_destroyImmediate==true) +{this.DestroyContact(c);c=null;} +else +{c.m_flags|=b2Contact.e_destroyFlag;}}},DestroyContact:function(c) +{if(c.m_prev) +{c.m_prev.m_next=c.m_next;} +if(c.m_next) +{c.m_next.m_prev=c.m_prev;} +if(c==this.m_world.m_contactList) +{this.m_world.m_contactList=c.m_next;} +if(c.GetManifoldCount()>0) +{var body1=c.m_shape1.m_body;var body2=c.m_shape2.m_body;var node1=c.m_node1;var node2=c.m_node2;body1.WakeUp();body2.WakeUp();if(node1.prev) +{node1.prev.next=node1.next;} +if(node1.next) +{node1.next.prev=node1.prev;} +if(node1==body1.m_contactList) +{body1.m_contactList=node1.next;} +node1.prev=null;node1.next=null;if(node2.prev) +{node2.prev.next=node2.next;} +if(node2.next) +{node2.next.prev=node2.prev;} +if(node2==body2.m_contactList) +{body2.m_contactList=node2.next;} +node2.prev=null;node2.next=null;} +b2Contact.Destroy(c,this.m_world.m_blockAllocator);--this.m_world.m_contactCount;},CleanContactList:function() +{var c=this.m_world.m_contactList;while(c!=null) +{var c0=c;c=c.m_next;if(c0.m_flags&b2Contact.e_destroyFlag) +{this.DestroyContact(c0);c0=null;}}},Collide:function() +{var body1;var body2;var node1;var node2;for(var c=this.m_world.m_contactList;c!=null;c=c.m_next) +{if(c.m_shape1.m_body.IsSleeping()&&c.m_shape2.m_body.IsSleeping()) +{continue;} +var oldCount=c.GetManifoldCount();c.Evaluate();var newCount=c.GetManifoldCount();if(oldCount==0&&newCount>0) +{body1=c.m_shape1.m_body;body2=c.m_shape2.m_body;node1=c.m_node1;node2=c.m_node2;node1.contact=c;node1.other=body2;node1.prev=null;node1.next=body1.m_contactList;if(node1.next!=null) +{node1.next.prev=c.m_node1;} +body1.m_contactList=c.m_node1;node2.contact=c;node2.other=body1;node2.prev=null;node2.next=body2.m_contactList;if(node2.next!=null) +{node2.next.prev=node2;} +body2.m_contactList=node2;} +else if(oldCount>0&&newCount==0) +{body1=c.m_shape1.m_body;body2=c.m_shape2.m_body;node1=c.m_node1;node2=c.m_node2;if(node1.prev) +{node1.prev.next=node1.next;} +if(node1.next) +{node1.next.prev=node1.prev;} +if(node1==body1.m_contactList) +{body1.m_contactList=node1.next;} +node1.prev=null;node1.next=null;if(node2.prev) +{node2.prev.next=node2.next;} +if(node2.next) +{node2.next.prev=node2.prev;} +if(node2==body2.m_contactList) +{body2.m_contactList=node2.next;} +node2.prev=null;node2.next=null;}}},m_world:null,m_nullContact:new b2NullContact(),m_destroyImmediate:null}); +var b2World=Class.create();b2World.prototype={initialize:function(worldAABB,gravity,doSleep){this.step=new b2TimeStep();this.m_contactManager=new b2ContactManager();this.m_listener=null;this.m_filter=b2CollisionFilter.b2_defaultFilter;this.m_bodyList=null;this.m_contactList=null;this.m_jointList=null;this.m_bodyCount=0;this.m_contactCount=0;this.m_jointCount=0;this.m_bodyDestroyList=null;this.m_allowSleep=doSleep;this.m_gravity=gravity;this.m_contactManager.m_world=this;this.m_broadPhase=new b2BroadPhase(worldAABB,this.m_contactManager);var bd=new b2BodyDef();this.m_groundBody=this.CreateBody(bd);},SetListener:function(listener){this.m_listener=listener;},SetFilter:function(filter){this.m_filter=filter;},CreateBody:function(def){var b=new b2Body(def,this);b.m_prev=null;b.m_next=this.m_bodyList;if(this.m_bodyList) +{this.m_bodyList.m_prev=b;} +this.m_bodyList=b;++this.m_bodyCount;return b;},DestroyBody:function(b) +{if(b.m_flags&b2Body.e_destroyFlag) +{return;} +if(b.m_prev) +{b.m_prev.m_next=b.m_next;} +if(b.m_next) +{b.m_next.m_prev=b.m_prev;} +if(b==this.m_bodyList) +{this.m_bodyList=b.m_next;} +b.m_flags|=b2Body.e_destroyFlag;--this.m_bodyCount;b.m_prev=null;b.m_next=this.m_bodyDestroyList;this.m_bodyDestroyList=b;},CleanBodyList:function() +{this.m_contactManager.m_destroyImmediate=true;var b=this.m_bodyDestroyList;while(b) +{var b0=b;b=b.m_next;var jn=b0.m_jointList;while(jn) +{var jn0=jn;jn=jn.next;if(this.m_listener) +{this.m_listener.NotifyJointDestroyed(jn0.joint);} +this.DestroyJoint(jn0.joint);} +b0.Destroy();} +this.m_bodyDestroyList=null;this.m_contactManager.m_destroyImmediate=false;},CreateJoint:function(def){var j=b2Joint.Create(def,this.m_blockAllocator);j.m_prev=null;j.m_next=this.m_jointList;if(this.m_jointList) +{this.m_jointList.m_prev=j;} +this.m_jointList=j;++this.m_jointCount;j.m_node1.joint=j;j.m_node1.other=j.m_body2;j.m_node1.prev=null;j.m_node1.next=j.m_body1.m_jointList;if(j.m_body1.m_jointList)j.m_body1.m_jointList.prev=j.m_node1;j.m_body1.m_jointList=j.m_node1;j.m_node2.joint=j;j.m_node2.other=j.m_body1;j.m_node2.prev=null;j.m_node2.next=j.m_body2.m_jointList;if(j.m_body2.m_jointList)j.m_body2.m_jointList.prev=j.m_node2;j.m_body2.m_jointList=j.m_node2;if(def.collideConnected==false) +{var b=def.body1.m_shapeCount<def.body2.m_shapeCount?def.body1:def.body2;for(var s=b.m_shapeList;s;s=s.m_next) +{s.ResetProxy(this.m_broadPhase);}} +return j;},DestroyJoint:function(j) +{var collideConnected=j.m_collideConnected;if(j.m_prev) +{j.m_prev.m_next=j.m_next;} +if(j.m_next) +{j.m_next.m_prev=j.m_prev;} +if(j==this.m_jointList) +{this.m_jointList=j.m_next;} +var body1=j.m_body1;var body2=j.m_body2;body1.WakeUp();body2.WakeUp();if(j.m_node1.prev) +{j.m_node1.prev.next=j.m_node1.next;} +if(j.m_node1.next) +{j.m_node1.next.prev=j.m_node1.prev;} +if(j.m_node1==body1.m_jointList) +{body1.m_jointList=j.m_node1.next;} +j.m_node1.prev=null;j.m_node1.next=null;if(j.m_node2.prev) +{j.m_node2.prev.next=j.m_node2.next;} +if(j.m_node2.next) +{j.m_node2.next.prev=j.m_node2.prev;} +if(j.m_node2==body2.m_jointList) +{body2.m_jointList=j.m_node2.next;} +j.m_node2.prev=null;j.m_node2.next=null;b2Joint.Destroy(j,this.m_blockAllocator);--this.m_jointCount;if(collideConnected==false) +{var b=body1.m_shapeCount<body2.m_shapeCount?body1:body2;for(var s=b.m_shapeList;s;s=s.m_next) +{s.ResetProxy(this.m_broadPhase);}}},GetGroundBody:function(){return this.m_groundBody;},step:new b2TimeStep(),Step:function(dt,iterations){var b;var other;this.step.dt=dt;this.step.iterations=iterations;if(dt>0.0) +{this.step.inv_dt=1.0/dt;} +else +{this.step.inv_dt=0.0;} +this.m_positionIterationCount=0;this.m_contactManager.CleanContactList();this.CleanBodyList();this.m_contactManager.Collide();var island=new b2Island(this.m_bodyCount,this.m_contactCount,this.m_jointCount,this.m_stackAllocator);for(b=this.m_bodyList;b!=null;b=b.m_next) +{b.m_flags&=~b2Body.e_islandFlag;} +for(var c=this.m_contactList;c!=null;c=c.m_next) +{c.m_flags&=~b2Contact.e_islandFlag;} +for(var j=this.m_jointList;j!=null;j=j.m_next) +{j.m_islandFlag=false;} +var stackSize=this.m_bodyCount;var stack=new Array(this.m_bodyCount);for(var k=0;k<this.m_bodyCount;k++) +stack[k]=null;for(var seed=this.m_bodyList;seed!=null;seed=seed.m_next) +{if(seed.m_flags&(b2Body.e_staticFlag|b2Body.e_islandFlag|b2Body.e_sleepFlag|b2Body.e_frozenFlag)) +{continue;} +island.Clear();var stackCount=0;stack[stackCount++]=seed;seed.m_flags|=b2Body.e_islandFlag;;while(stackCount>0) +{b=stack[--stackCount];island.AddBody(b);b.m_flags&=~b2Body.e_sleepFlag;if(b.m_flags&b2Body.e_staticFlag) +{continue;} +for(var cn=b.m_contactList;cn!=null;cn=cn.next) +{if(cn.contact.m_flags&b2Contact.e_islandFlag) +{continue;} +island.AddContact(cn.contact);cn.contact.m_flags|=b2Contact.e_islandFlag;other=cn.other;if(other.m_flags&b2Body.e_islandFlag) +{continue;} +stack[stackCount++]=other;other.m_flags|=b2Body.e_islandFlag;} +for(var jn=b.m_jointList;jn!=null;jn=jn.next) +{if(jn.joint.m_islandFlag==true) +{continue;} +island.AddJoint(jn.joint);jn.joint.m_islandFlag=true;other=jn.other;if(other.m_flags&b2Body.e_islandFlag) +{continue;} +stack[stackCount++]=other;other.m_flags|=b2Body.e_islandFlag;}} +island.Solve(this.step,this.m_gravity);this.m_positionIterationCount=b2Math.b2Max(this.m_positionIterationCount,b2Island.m_positionIterationCount);if(this.m_allowSleep) +{island.UpdateSleep(dt);} +for(var i=0;i<island.m_bodyCount;++i) +{b=island.m_bodies[i];if(b.m_flags&b2Body.e_staticFlag) +{b.m_flags&=~b2Body.e_islandFlag;} +if(b.IsFrozen()&&this.m_listener) +{var response=this.m_listener.NotifyBoundaryViolated(b);if(response==b2WorldListener.b2_destroyBody) +{this.DestroyBody(b);b=null;island.m_bodies[i]=null;}}}} +this.m_broadPhase.Commit();},Query:function(aabb,shapes,maxCount){var results=new Array();var count=this.m_broadPhase.QueryAABB(aabb,results,maxCount);for(var i=0;i<count;++i) +{shapes[i]=results[i];} +return count;},GetBodyList:function(){return this.m_bodyList;},GetJointList:function(){return this.m_jointList;},GetContactList:function(){return this.m_contactList;},m_blockAllocator:null,m_stackAllocator:null,m_broadPhase:null,m_contactManager:new b2ContactManager(),m_bodyList:null,m_contactList:null,m_jointList:null,m_bodyCount:0,m_contactCount:0,m_jointCount:0,m_bodyDestroyList:null,m_gravity:null,m_allowSleep:null,m_groundBody:null,m_listener:null,m_filter:null,m_positionIterationCount:0};b2World.s_enablePositionCorrection=1;b2World.s_enableWarmStarting=1; +var b2WorldListener=Class.create();b2WorldListener.prototype={NotifyJointDestroyed:function(joint){},NotifyBoundaryViolated:function(body) +{return b2WorldListener.b2_freezeBody;},initialize:function(){}};b2WorldListener.b2_freezeBody=0;b2WorldListener.b2_destroyBody=1; +var b2JointNode=Class.create();b2JointNode.prototype={other:null,joint:null,prev:null,next:null,initialize:function(){}} + +var b2Joint=Class.create();b2Joint.prototype={GetType:function(){return this.m_type;},GetAnchor1:function(){return null},GetAnchor2:function(){return null},GetReactionForce:function(invTimeStep){return null},GetReactionTorque:function(invTimeStep){return 0.0},GetBody1:function() +{return this.m_body1;},GetBody2:function() +{return this.m_body2;},GetNext:function(){return this.m_next;},GetUserData:function(){return this.m_userData;},initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;},PrepareVelocitySolver:function(){},SolveVelocityConstraints:function(step){},PreparePositionSolver:function(){},SolvePositionConstraints:function(){return false},m_type:0,m_prev:null,m_next:null,m_node1:new b2JointNode(),m_node2:new b2JointNode(),m_body1:null,m_body2:null,m_islandFlag:null,m_collideConnected:null,m_userData:null};b2Joint.Create=function(def,allocator){var joint=null;switch(def.type) +{case b2Joint.e_distanceJoint:{joint=new b2DistanceJoint(def);} +break;case b2Joint.e_mouseJoint:{joint=new b2MouseJoint(def);} +break;case b2Joint.e_prismaticJoint:{joint=new b2PrismaticJoint(def);} +break;case b2Joint.e_revoluteJoint:{joint=new b2RevoluteJoint(def);} +break;case b2Joint.e_pulleyJoint:{joint=new b2PulleyJoint(def);} +break;case b2Joint.e_gearJoint:{joint=new b2GearJoint(def);} +break;default:break;} +return joint;};b2Joint.Destroy=function(joint,allocator){};b2Joint.e_unknownJoint=0;b2Joint.e_revoluteJoint=1;b2Joint.e_prismaticJoint=2;b2Joint.e_distanceJoint=3;b2Joint.e_pulleyJoint=4;b2Joint.e_mouseJoint=5;b2Joint.e_gearJoint=6;b2Joint.e_inactiveLimit=0;b2Joint.e_atLowerLimit=1;b2Joint.e_atUpperLimit=2;b2Joint.e_equalLimits=3; +var b2JointDef=Class.create();b2JointDef.prototype={initialize:function() +{this.type=b2Joint.e_unknownJoint;this.userData=null;this.body1=null;this.body2=null;this.collideConnected=false;},type:0,userData:null,body1:null,body2:null,collideConnected:null} + +var b2DistanceJoint=Class.create();Object.extend(b2DistanceJoint.prototype,b2Joint.prototype);Object.extend(b2DistanceJoint.prototype,{initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;this.m_localAnchor1=new b2Vec2();this.m_localAnchor2=new b2Vec2();this.m_u=new b2Vec2();var tMat;var tX;var tY;tMat=this.m_body1.m_R;tX=def.anchorPoint1.x-this.m_body1.m_position.x;tY=def.anchorPoint1.y-this.m_body1.m_position.y;this.m_localAnchor1.x=tX*tMat.col1.x+tY*tMat.col1.y;this.m_localAnchor1.y=tX*tMat.col2.x+tY*tMat.col2.y;tMat=this.m_body2.m_R;tX=def.anchorPoint2.x-this.m_body2.m_position.x;tY=def.anchorPoint2.y-this.m_body2.m_position.y;this.m_localAnchor2.x=tX*tMat.col1.x+tY*tMat.col1.y;this.m_localAnchor2.y=tX*tMat.col2.x+tY*tMat.col2.y;tX=def.anchorPoint2.x-def.anchorPoint1.x;tY=def.anchorPoint2.y-def.anchorPoint1.y;this.m_length=Math.sqrt(tX*tX+tY*tY);this.m_impulse=0.0;},PrepareVelocitySolver:function(){var tMat;tMat=this.m_body1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=this.m_body2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;this.m_u.x=this.m_body2.m_position.x+r2X-this.m_body1.m_position.x-r1X;this.m_u.y=this.m_body2.m_position.y+r2Y-this.m_body1.m_position.y-r1Y;var length=Math.sqrt(this.m_u.x*this.m_u.x+this.m_u.y*this.m_u.y);if(length>b2Settings.b2_linearSlop) +{this.m_u.Multiply(1.0/length);} +else +{this.m_u.SetZero();} +var cr1u=(r1X*this.m_u.y-r1Y*this.m_u.x);var cr2u=(r2X*this.m_u.y-r2Y*this.m_u.x);this.m_mass=this.m_body1.m_invMass+this.m_body1.m_invI*cr1u*cr1u+this.m_body2.m_invMass+this.m_body2.m_invI*cr2u*cr2u;this.m_mass=1.0/this.m_mass;if(b2World.s_enableWarmStarting) +{var PX=this.m_impulse*this.m_u.x;var PY=this.m_impulse*this.m_u.y;this.m_body1.m_linearVelocity.x-=this.m_body1.m_invMass*PX;this.m_body1.m_linearVelocity.y-=this.m_body1.m_invMass*PY;this.m_body1.m_angularVelocity-=this.m_body1.m_invI*(r1X*PY-r1Y*PX);this.m_body2.m_linearVelocity.x+=this.m_body2.m_invMass*PX;this.m_body2.m_linearVelocity.y+=this.m_body2.m_invMass*PY;this.m_body2.m_angularVelocity+=this.m_body2.m_invI*(r2X*PY-r2Y*PX);} +else +{this.m_impulse=0.0;}},SolveVelocityConstraints:function(step){var tMat;tMat=this.m_body1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=this.m_body2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var v1X=this.m_body1.m_linearVelocity.x+(-this.m_body1.m_angularVelocity*r1Y);var v1Y=this.m_body1.m_linearVelocity.y+(this.m_body1.m_angularVelocity*r1X);var v2X=this.m_body2.m_linearVelocity.x+(-this.m_body2.m_angularVelocity*r2Y);var v2Y=this.m_body2.m_linearVelocity.y+(this.m_body2.m_angularVelocity*r2X);var Cdot=(this.m_u.x*(v2X-v1X)+this.m_u.y*(v2Y-v1Y));var impulse=-this.m_mass*Cdot;this.m_impulse+=impulse;var PX=impulse*this.m_u.x;var PY=impulse*this.m_u.y;this.m_body1.m_linearVelocity.x-=this.m_body1.m_invMass*PX;this.m_body1.m_linearVelocity.y-=this.m_body1.m_invMass*PY;this.m_body1.m_angularVelocity-=this.m_body1.m_invI*(r1X*PY-r1Y*PX);this.m_body2.m_linearVelocity.x+=this.m_body2.m_invMass*PX;this.m_body2.m_linearVelocity.y+=this.m_body2.m_invMass*PY;this.m_body2.m_angularVelocity+=this.m_body2.m_invI*(r2X*PY-r2Y*PX);},SolvePositionConstraints:function(){var tMat;tMat=this.m_body1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=this.m_body2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var dX=this.m_body2.m_position.x+r2X-this.m_body1.m_position.x-r1X;var dY=this.m_body2.m_position.y+r2Y-this.m_body1.m_position.y-r1Y;var length=Math.sqrt(dX*dX+dY*dY);dX/=length;dY/=length;var C=length-this.m_length;C=b2Math.b2Clamp(C,-b2Settings.b2_maxLinearCorrection,b2Settings.b2_maxLinearCorrection);var impulse=-this.m_mass*C;this.m_u.Set(dX,dY);var PX=impulse*this.m_u.x;var PY=impulse*this.m_u.y;this.m_body1.m_position.x-=this.m_body1.m_invMass*PX;this.m_body1.m_position.y-=this.m_body1.m_invMass*PY;this.m_body1.m_rotation-=this.m_body1.m_invI*(r1X*PY-r1Y*PX);this.m_body2.m_position.x+=this.m_body2.m_invMass*PX;this.m_body2.m_position.y+=this.m_body2.m_invMass*PY;this.m_body2.m_rotation+=this.m_body2.m_invI*(r2X*PY-r2Y*PX);this.m_body1.m_R.Set(this.m_body1.m_rotation);this.m_body2.m_R.Set(this.m_body2.m_rotation);return b2Math.b2Abs(C)<b2Settings.b2_linearSlop;},GetAnchor1:function(){return b2Math.AddVV(this.m_body1.m_position,b2Math.b2MulMV(this.m_body1.m_R,this.m_localAnchor1));},GetAnchor2:function(){return b2Math.AddVV(this.m_body2.m_position,b2Math.b2MulMV(this.m_body2.m_R,this.m_localAnchor2));},GetReactionForce:function(invTimeStep) +{var F=new b2Vec2();F.SetV(this.m_u);F.Multiply(this.m_impulse*invTimeStep);return F;},GetReactionTorque:function(invTimeStep) +{return 0.0;},m_localAnchor1:new b2Vec2(),m_localAnchor2:new b2Vec2(),m_u:new b2Vec2(),m_impulse:null,m_mass:null,m_length:null}); +var b2DistanceJointDef=Class.create();Object.extend(b2DistanceJointDef.prototype,b2JointDef.prototype);Object.extend(b2DistanceJointDef.prototype,{initialize:function() +{this.type=b2Joint.e_unknownJoint;this.userData=null;this.body1=null;this.body2=null;this.collideConnected=false;this.anchorPoint1=new b2Vec2();this.anchorPoint2=new b2Vec2();this.type=b2Joint.e_distanceJoint;},anchorPoint1:new b2Vec2(),anchorPoint2:new b2Vec2()}); +var b2Jacobian=Class.create();b2Jacobian.prototype={linear1:new b2Vec2(),angular1:null,linear2:new b2Vec2(),angular2:null,SetZero:function(){this.linear1.SetZero();this.angular1=0.0;this.linear2.SetZero();this.angular2=0.0;},Set:function(x1,a1,x2,a2){this.linear1.SetV(x1);this.angular1=a1;this.linear2.SetV(x2);this.angular2=a2;},Compute:function(x1,a1,x2,a2){return(this.linear1.x*x1.x+this.linear1.y*x1.y)+this.angular1*a1+(this.linear2.x*x2.x+this.linear2.y*x2.y)+this.angular2*a2;},initialize:function(){this.linear1=new b2Vec2();this.linear2=new b2Vec2();}}; +var b2GearJoint=Class.create();Object.extend(b2GearJoint.prototype,b2Joint.prototype);Object.extend(b2GearJoint.prototype,{GetAnchor1:function(){var tMat=this.m_body1.m_R;return new b2Vec2(this.m_body1.m_position.x+(tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y),this.m_body1.m_position.y+(tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y));},GetAnchor2:function(){var tMat=this.m_body2.m_R;return new b2Vec2(this.m_body2.m_position.x+(tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y),this.m_body2.m_position.y+(tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y));},GetReactionForce:function(invTimeStep){return new b2Vec2();},GetReactionTorque:function(invTimeStep){return 0.0;},GetRatio:function(){return this.m_ratio;},initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;this.m_groundAnchor1=new b2Vec2();this.m_groundAnchor2=new b2Vec2();this.m_localAnchor1=new b2Vec2();this.m_localAnchor2=new b2Vec2();this.m_J=new b2Jacobian();this.m_revolute1=null;this.m_prismatic1=null;this.m_revolute2=null;this.m_prismatic2=null;var coordinate1;var coordinate2;this.m_ground1=def.joint1.m_body1;this.m_body1=def.joint1.m_body2;if(def.joint1.m_type==b2Joint.e_revoluteJoint) +{this.m_revolute1=def.joint1;this.m_groundAnchor1.SetV(this.m_revolute1.m_localAnchor1);this.m_localAnchor1.SetV(this.m_revolute1.m_localAnchor2);coordinate1=this.m_revolute1.GetJointAngle();} +else +{this.m_prismatic1=def.joint1;this.m_groundAnchor1.SetV(this.m_prismatic1.m_localAnchor1);this.m_localAnchor1.SetV(this.m_prismatic1.m_localAnchor2);coordinate1=this.m_prismatic1.GetJointTranslation();} +this.m_ground2=def.joint2.m_body1;this.m_body2=def.joint2.m_body2;if(def.joint2.m_type==b2Joint.e_revoluteJoint) +{this.m_revolute2=def.joint2;this.m_groundAnchor2.SetV(this.m_revolute2.m_localAnchor1);this.m_localAnchor2.SetV(this.m_revolute2.m_localAnchor2);coordinate2=this.m_revolute2.GetJointAngle();} +else +{this.m_prismatic2=def.joint2;this.m_groundAnchor2.SetV(this.m_prismatic2.m_localAnchor1);this.m_localAnchor2.SetV(this.m_prismatic2.m_localAnchor2);coordinate2=this.m_prismatic2.GetJointTranslation();} +this.m_ratio=def.ratio;this.m_constant=coordinate1+this.m_ratio*coordinate2;this.m_impulse=0.0;},PrepareVelocitySolver:function(){var g1=this.m_ground1;var g2=this.m_ground2;var b1=this.m_body1;var b2=this.m_body2;var ugX;var ugY;var rX;var rY;var tMat;var tVec;var crug;var K=0.0;this.m_J.SetZero();if(this.m_revolute1) +{this.m_J.angular1=-1.0;K+=b1.m_invI;} +else +{tMat=g1.m_R;tVec=this.m_prismatic1.m_localXAxis1;ugX=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y;ugY=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y;tMat=b1.m_R;rX=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;rY=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;crug=rX*ugY-rY*ugX;this.m_J.linear1.Set(-ugX,-ugY);this.m_J.angular1=-crug;K+=b1.m_invMass+b1.m_invI*crug*crug;} +if(this.m_revolute2) +{this.m_J.angular2=-this.m_ratio;K+=this.m_ratio*this.m_ratio*b2.m_invI;} +else +{tMat=g2.m_R;tVec=this.m_prismatic2.m_localXAxis1;ugX=tMat.col1.x*tVec.x+tMat.col2.x*tVec.y;ugY=tMat.col1.y*tVec.x+tMat.col2.y*tVec.y;tMat=b2.m_R;rX=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;rY=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;crug=rX*ugY-rY*ugX;this.m_J.linear2.Set(-this.m_ratio*ugX,-this.m_ratio*ugY);this.m_J.angular2=-this.m_ratio*crug;K+=this.m_ratio*this.m_ratio*(b2.m_invMass+b2.m_invI*crug*crug);} +this.m_mass=1.0/K;b1.m_linearVelocity.x+=b1.m_invMass*this.m_impulse*this.m_J.linear1.x;b1.m_linearVelocity.y+=b1.m_invMass*this.m_impulse*this.m_J.linear1.y;b1.m_angularVelocity+=b1.m_invI*this.m_impulse*this.m_J.angular1;b2.m_linearVelocity.x+=b2.m_invMass*this.m_impulse*this.m_J.linear2.x;b2.m_linearVelocity.y+=b2.m_invMass*this.m_impulse*this.m_J.linear2.y;b2.m_angularVelocity+=b2.m_invI*this.m_impulse*this.m_J.angular2;},SolveVelocityConstraints:function(step){var b1=this.m_body1;var b2=this.m_body2;var Cdot=this.m_J.Compute(b1.m_linearVelocity,b1.m_angularVelocity,b2.m_linearVelocity,b2.m_angularVelocity);var impulse=-this.m_mass*Cdot;this.m_impulse+=impulse;b1.m_linearVelocity.x+=b1.m_invMass*impulse*this.m_J.linear1.x;b1.m_linearVelocity.y+=b1.m_invMass*impulse*this.m_J.linear1.y;b1.m_angularVelocity+=b1.m_invI*impulse*this.m_J.angular1;b2.m_linearVelocity.x+=b2.m_invMass*impulse*this.m_J.linear2.x;b2.m_linearVelocity.y+=b2.m_invMass*impulse*this.m_J.linear2.y;b2.m_angularVelocity+=b2.m_invI*impulse*this.m_J.angular2;},SolvePositionConstraints:function(){var linearError=0.0;var b1=this.m_body1;var b2=this.m_body2;var coordinate1;var coordinate2;if(this.m_revolute1) +{coordinate1=this.m_revolute1.GetJointAngle();} +else +{coordinate1=this.m_prismatic1.GetJointTranslation();} +if(this.m_revolute2) +{coordinate2=this.m_revolute2.GetJointAngle();} +else +{coordinate2=this.m_prismatic2.GetJointTranslation();} +var C=this.m_constant-(coordinate1+this.m_ratio*coordinate2);var impulse=-this.m_mass*C;b1.m_position.x+=b1.m_invMass*impulse*this.m_J.linear1.x;b1.m_position.y+=b1.m_invMass*impulse*this.m_J.linear1.y;b1.m_rotation+=b1.m_invI*impulse*this.m_J.angular1;b2.m_position.x+=b2.m_invMass*impulse*this.m_J.linear2.x;b2.m_position.y+=b2.m_invMass*impulse*this.m_J.linear2.y;b2.m_rotation+=b2.m_invI*impulse*this.m_J.angular2;b1.m_R.Set(b1.m_rotation);b2.m_R.Set(b2.m_rotation);return linearError<b2Settings.b2_linearSlop;},m_ground1:null,m_ground2:null,m_revolute1:null,m_prismatic1:null,m_revolute2:null,m_prismatic2:null,m_groundAnchor1:new b2Vec2(),m_groundAnchor2:new b2Vec2(),m_localAnchor1:new b2Vec2(),m_localAnchor2:new b2Vec2(),m_J:new b2Jacobian(),m_constant:null,m_ratio:null,m_mass:null,m_impulse:null}); +var b2GearJointDef=Class.create();Object.extend(b2GearJointDef.prototype,b2JointDef.prototype);Object.extend(b2GearJointDef.prototype,{initialize:function() +{this.type=b2Joint.e_gearJoint;this.joint1=null;this.joint2=null;this.ratio=1.0;},joint1:null,joint2:null,ratio:null}); +var b2MouseJoint=Class.create();Object.extend(b2MouseJoint.prototype,b2Joint.prototype);Object.extend(b2MouseJoint.prototype,{GetAnchor1:function(){return this.m_target;},GetAnchor2:function(){var tVec=b2Math.b2MulMV(this.m_body2.m_R,this.m_localAnchor);tVec.Add(this.m_body2.m_position);return tVec;},GetReactionForce:function(invTimeStep) +{var F=new b2Vec2();F.SetV(this.m_impulse);F.Multiply(invTimeStep);return F;},GetReactionTorque:function(invTimeStep) +{return 0.0;},SetTarget:function(target){this.m_body2.WakeUp();this.m_target=target;},initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;this.K=new b2Mat22();this.K1=new b2Mat22();this.K2=new b2Mat22();this.m_localAnchor=new b2Vec2();this.m_target=new b2Vec2();this.m_impulse=new b2Vec2();this.m_ptpMass=new b2Mat22();this.m_C=new b2Vec2();this.m_target.SetV(def.target);var tX=this.m_target.x-this.m_body2.m_position.x;var tY=this.m_target.y-this.m_body2.m_position.y;this.m_localAnchor.x=(tX*this.m_body2.m_R.col1.x+tY*this.m_body2.m_R.col1.y);this.m_localAnchor.y=(tX*this.m_body2.m_R.col2.x+tY*this.m_body2.m_R.col2.y);this.m_maxForce=def.maxForce;this.m_impulse.SetZero();var mass=this.m_body2.m_mass;var omega=2.0*b2Settings.b2_pi*def.frequencyHz;var d=2.0*mass*def.dampingRatio*omega;var k=mass*omega*omega;this.m_gamma=1.0/(d+def.timeStep*k);this.m_beta=def.timeStep*k/(d+def.timeStep*k);},K:new b2Mat22(),K1:new b2Mat22(),K2:new b2Mat22(),PrepareVelocitySolver:function(){var b=this.m_body2;var tMat;tMat=b.m_R;var rX=tMat.col1.x*this.m_localAnchor.x+tMat.col2.x*this.m_localAnchor.y;var rY=tMat.col1.y*this.m_localAnchor.x+tMat.col2.y*this.m_localAnchor.y;var invMass=b.m_invMass;var invI=b.m_invI;this.K1.col1.x=invMass;this.K1.col2.x=0.0;this.K1.col1.y=0.0;this.K1.col2.y=invMass;this.K2.col1.x=invI*rY*rY;this.K2.col2.x=-invI*rX*rY;this.K2.col1.y=-invI*rX*rY;this.K2.col2.y=invI*rX*rX;this.K.SetM(this.K1);this.K.AddM(this.K2);this.K.col1.x+=this.m_gamma;this.K.col2.y+=this.m_gamma;this.K.Invert(this.m_ptpMass);this.m_C.x=b.m_position.x+rX-this.m_target.x;this.m_C.y=b.m_position.y+rY-this.m_target.y;b.m_angularVelocity*=0.98;var PX=this.m_impulse.x;var PY=this.m_impulse.y;b.m_linearVelocity.x+=invMass*PX;b.m_linearVelocity.y+=invMass*PY;b.m_angularVelocity+=invI*(rX*PY-rY*PX);},SolveVelocityConstraints:function(step){var body=this.m_body2;var tMat;tMat=body.m_R;var rX=tMat.col1.x*this.m_localAnchor.x+tMat.col2.x*this.m_localAnchor.y;var rY=tMat.col1.y*this.m_localAnchor.x+tMat.col2.y*this.m_localAnchor.y;var CdotX=body.m_linearVelocity.x+(-body.m_angularVelocity*rY);var CdotY=body.m_linearVelocity.y+(body.m_angularVelocity*rX);tMat=this.m_ptpMass;var tX=CdotX+(this.m_beta*step.inv_dt)*this.m_C.x+this.m_gamma*this.m_impulse.x;var tY=CdotY+(this.m_beta*step.inv_dt)*this.m_C.y+this.m_gamma*this.m_impulse.y;var impulseX=-(tMat.col1.x*tX+tMat.col2.x*tY);var impulseY=-(tMat.col1.y*tX+tMat.col2.y*tY);var oldImpulseX=this.m_impulse.x;var oldImpulseY=this.m_impulse.y;this.m_impulse.x+=impulseX;this.m_impulse.y+=impulseY;var length=this.m_impulse.Length();if(length>step.dt*this.m_maxForce) +{this.m_impulse.Multiply(step.dt*this.m_maxForce/length);} +impulseX=this.m_impulse.x-oldImpulseX;impulseY=this.m_impulse.y-oldImpulseY;body.m_linearVelocity.x+=body.m_invMass*impulseX;body.m_linearVelocity.y+=body.m_invMass*impulseY;body.m_angularVelocity+=body.m_invI*(rX*impulseY-rY*impulseX);},SolvePositionConstraints:function(){return true;},m_localAnchor:new b2Vec2(),m_target:new b2Vec2(),m_impulse:new b2Vec2(),m_ptpMass:new b2Mat22(),m_C:new b2Vec2(),m_maxForce:null,m_beta:null,m_gamma:null}); +var b2MouseJointDef=Class.create();Object.extend(b2MouseJointDef.prototype,b2JointDef.prototype);Object.extend(b2MouseJointDef.prototype,{initialize:function() +{this.type=b2Joint.e_unknownJoint;this.userData=null;this.body1=null;this.body2=null;this.collideConnected=false;this.target=new b2Vec2();this.type=b2Joint.e_mouseJoint;this.maxForce=0.0;this.frequencyHz=5.0;this.dampingRatio=0.7;this.timeStep=1.0/60.0;},target:new b2Vec2(),maxForce:null,frequencyHz:null,dampingRatio:null,timeStep:null}); +var b2PrismaticJoint=Class.create();Object.extend(b2PrismaticJoint.prototype,b2Joint.prototype);Object.extend(b2PrismaticJoint.prototype,{GetAnchor1:function(){var b1=this.m_body1;var tVec=new b2Vec2();tVec.SetV(this.m_localAnchor1);tVec.MulM(b1.m_R);tVec.Add(b1.m_position);return tVec;},GetAnchor2:function(){var b2=this.m_body2;var tVec=new b2Vec2();tVec.SetV(this.m_localAnchor2);tVec.MulM(b2.m_R);tVec.Add(b2.m_position);return tVec;},GetJointTranslation:function(){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var p1X=b1.m_position.x+r1X;var p1Y=b1.m_position.y+r1Y;var p2X=b2.m_position.x+r2X;var p2Y=b2.m_position.y+r2Y;var dX=p2X-p1X;var dY=p2Y-p1Y;tMat=b1.m_R;var ax1X=tMat.col1.x*this.m_localXAxis1.x+tMat.col2.x*this.m_localXAxis1.y;var ax1Y=tMat.col1.y*this.m_localXAxis1.x+tMat.col2.y*this.m_localXAxis1.y;var translation=ax1X*dX+ax1Y*dY;return translation;},GetJointSpeed:function(){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var p1X=b1.m_position.x+r1X;var p1Y=b1.m_position.y+r1Y;var p2X=b2.m_position.x+r2X;var p2Y=b2.m_position.y+r2Y;var dX=p2X-p1X;var dY=p2Y-p1Y;tMat=b1.m_R;var ax1X=tMat.col1.x*this.m_localXAxis1.x+tMat.col2.x*this.m_localXAxis1.y;var ax1Y=tMat.col1.y*this.m_localXAxis1.x+tMat.col2.y*this.m_localXAxis1.y;var v1=b1.m_linearVelocity;var v2=b2.m_linearVelocity;var w1=b1.m_angularVelocity;var w2=b2.m_angularVelocity;var speed=(dX*(-w1*ax1Y)+dY*(w1*ax1X))+(ax1X*(((v2.x+(-w2*r2Y))-v1.x)-(-w1*r1Y))+ax1Y*(((v2.y+(w2*r2X))-v1.y)-(w1*r1X)));return speed;},GetMotorForce:function(invTimeStep){return invTimeStep*this.m_motorImpulse;},SetMotorSpeed:function(speed) +{this.m_motorSpeed=speed;},SetMotorForce:function(force) +{this.m_maxMotorForce=force;},GetReactionForce:function(invTimeStep) +{var tImp=invTimeStep*this.m_limitImpulse;var tMat;tMat=this.m_body1.m_R;var ax1X=tImp*(tMat.col1.x*this.m_localXAxis1.x+tMat.col2.x*this.m_localXAxis1.y);var ax1Y=tImp*(tMat.col1.y*this.m_localXAxis1.x+tMat.col2.y*this.m_localXAxis1.y);var ay1X=tImp*(tMat.col1.x*this.m_localYAxis1.x+tMat.col2.x*this.m_localYAxis1.y);var ay1Y=tImp*(tMat.col1.y*this.m_localYAxis1.x+tMat.col2.y*this.m_localYAxis1.y);return new b2Vec2(ax1X+ay1X,ax1Y+ay1Y);},GetReactionTorque:function(invTimeStep) +{return invTimeStep*this.m_angularImpulse;},initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;this.m_localAnchor1=new b2Vec2();this.m_localAnchor2=new b2Vec2();this.m_localXAxis1=new b2Vec2();this.m_localYAxis1=new b2Vec2();this.m_linearJacobian=new b2Jacobian();this.m_motorJacobian=new b2Jacobian();var tMat;var tX;var tY;tMat=this.m_body1.m_R;tX=(def.anchorPoint.x-this.m_body1.m_position.x);tY=(def.anchorPoint.y-this.m_body1.m_position.y);this.m_localAnchor1.Set((tX*tMat.col1.x+tY*tMat.col1.y),(tX*tMat.col2.x+tY*tMat.col2.y));tMat=this.m_body2.m_R;tX=(def.anchorPoint.x-this.m_body2.m_position.x);tY=(def.anchorPoint.y-this.m_body2.m_position.y);this.m_localAnchor2.Set((tX*tMat.col1.x+tY*tMat.col1.y),(tX*tMat.col2.x+tY*tMat.col2.y));tMat=this.m_body1.m_R;tX=def.axis.x;tY=def.axis.y;this.m_localXAxis1.Set((tX*tMat.col1.x+tY*tMat.col1.y),(tX*tMat.col2.x+tY*tMat.col2.y));this.m_localYAxis1.x=-this.m_localXAxis1.y;this.m_localYAxis1.y=this.m_localXAxis1.x;this.m_initialAngle=this.m_body2.m_rotation-this.m_body1.m_rotation;this.m_linearJacobian.SetZero();this.m_linearMass=0.0;this.m_linearImpulse=0.0;this.m_angularMass=0.0;this.m_angularImpulse=0.0;this.m_motorJacobian.SetZero();this.m_motorMass=0.0;this.m_motorImpulse=0.0;this.m_limitImpulse=0.0;this.m_limitPositionImpulse=0.0;this.m_lowerTranslation=def.lowerTranslation;this.m_upperTranslation=def.upperTranslation;this.m_maxMotorForce=def.motorForce;this.m_motorSpeed=def.motorSpeed;this.m_enableLimit=def.enableLimit;this.m_enableMotor=def.enableMotor;},PrepareVelocitySolver:function(){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var invMass1=b1.m_invMass;var invMass2=b2.m_invMass;var invI1=b1.m_invI;var invI2=b2.m_invI;tMat=b1.m_R;var ay1X=tMat.col1.x*this.m_localYAxis1.x+tMat.col2.x*this.m_localYAxis1.y;var ay1Y=tMat.col1.y*this.m_localYAxis1.x+tMat.col2.y*this.m_localYAxis1.y;var eX=b2.m_position.x+r2X-b1.m_position.x;var eY=b2.m_position.y+r2Y-b1.m_position.y;this.m_linearJacobian.linear1.x=-ay1X;this.m_linearJacobian.linear1.y=-ay1Y;this.m_linearJacobian.linear2.x=ay1X;this.m_linearJacobian.linear2.y=ay1Y;this.m_linearJacobian.angular1=-(eX*ay1Y-eY*ay1X);this.m_linearJacobian.angular2=r2X*ay1Y-r2Y*ay1X;this.m_linearMass=invMass1+invI1*this.m_linearJacobian.angular1*this.m_linearJacobian.angular1+ +invMass2+invI2*this.m_linearJacobian.angular2*this.m_linearJacobian.angular2;this.m_linearMass=1.0/this.m_linearMass;this.m_angularMass=1.0/(invI1+invI2);if(this.m_enableLimit||this.m_enableMotor) +{tMat=b1.m_R;var ax1X=tMat.col1.x*this.m_localXAxis1.x+tMat.col2.x*this.m_localXAxis1.y;var ax1Y=tMat.col1.y*this.m_localXAxis1.x+tMat.col2.y*this.m_localXAxis1.y;this.m_motorJacobian.linear1.x=-ax1X;this.m_motorJacobian.linear1.y=-ax1Y;this.m_motorJacobian.linear2.x=ax1X;this.m_motorJacobian.linear2.y=ax1Y;this.m_motorJacobian.angular1=-(eX*ax1Y-eY*ax1X);this.m_motorJacobian.angular2=r2X*ax1Y-r2Y*ax1X;this.m_motorMass=invMass1+invI1*this.m_motorJacobian.angular1*this.m_motorJacobian.angular1+ +invMass2+invI2*this.m_motorJacobian.angular2*this.m_motorJacobian.angular2;this.m_motorMass=1.0/this.m_motorMass;if(this.m_enableLimit) +{var dX=eX-r1X;var dY=eY-r1Y;var jointTranslation=ax1X*dX+ax1Y*dY;if(b2Math.b2Abs(this.m_upperTranslation-this.m_lowerTranslation)<2.0*b2Settings.b2_linearSlop) +{this.m_limitState=b2Joint.e_equalLimits;} +else if(jointTranslation<=this.m_lowerTranslation) +{if(this.m_limitState!=b2Joint.e_atLowerLimit) +{this.m_limitImpulse=0.0;} +this.m_limitState=b2Joint.e_atLowerLimit;} +else if(jointTranslation>=this.m_upperTranslation) +{if(this.m_limitState!=b2Joint.e_atUpperLimit) +{this.m_limitImpulse=0.0;} +this.m_limitState=b2Joint.e_atUpperLimit;} +else +{this.m_limitState=b2Joint.e_inactiveLimit;this.m_limitImpulse=0.0;}}} +if(this.m_enableMotor==false) +{this.m_motorImpulse=0.0;} +if(this.m_enableLimit==false) +{this.m_limitImpulse=0.0;} +if(b2World.s_enableWarmStarting) +{var P1X=this.m_linearImpulse*this.m_linearJacobian.linear1.x+(this.m_motorImpulse+this.m_limitImpulse)*this.m_motorJacobian.linear1.x;var P1Y=this.m_linearImpulse*this.m_linearJacobian.linear1.y+(this.m_motorImpulse+this.m_limitImpulse)*this.m_motorJacobian.linear1.y;var P2X=this.m_linearImpulse*this.m_linearJacobian.linear2.x+(this.m_motorImpulse+this.m_limitImpulse)*this.m_motorJacobian.linear2.x;var P2Y=this.m_linearImpulse*this.m_linearJacobian.linear2.y+(this.m_motorImpulse+this.m_limitImpulse)*this.m_motorJacobian.linear2.y;var L1=this.m_linearImpulse*this.m_linearJacobian.angular1-this.m_angularImpulse+(this.m_motorImpulse+this.m_limitImpulse)*this.m_motorJacobian.angular1;var L2=this.m_linearImpulse*this.m_linearJacobian.angular2+this.m_angularImpulse+(this.m_motorImpulse+this.m_limitImpulse)*this.m_motorJacobian.angular2;b1.m_linearVelocity.x+=invMass1*P1X;b1.m_linearVelocity.y+=invMass1*P1Y;b1.m_angularVelocity+=invI1*L1;b2.m_linearVelocity.x+=invMass2*P2X;b2.m_linearVelocity.y+=invMass2*P2Y;b2.m_angularVelocity+=invI2*L2;} +else +{this.m_linearImpulse=0.0;this.m_angularImpulse=0.0;this.m_limitImpulse=0.0;this.m_motorImpulse=0.0;} +this.m_limitPositionImpulse=0.0;},SolveVelocityConstraints:function(step){var b1=this.m_body1;var b2=this.m_body2;var invMass1=b1.m_invMass;var invMass2=b2.m_invMass;var invI1=b1.m_invI;var invI2=b2.m_invI;var oldLimitImpulse;var linearCdot=this.m_linearJacobian.Compute(b1.m_linearVelocity,b1.m_angularVelocity,b2.m_linearVelocity,b2.m_angularVelocity);var linearImpulse=-this.m_linearMass*linearCdot;this.m_linearImpulse+=linearImpulse;b1.m_linearVelocity.x+=(invMass1*linearImpulse)*this.m_linearJacobian.linear1.x;b1.m_linearVelocity.y+=(invMass1*linearImpulse)*this.m_linearJacobian.linear1.y;b1.m_angularVelocity+=invI1*linearImpulse*this.m_linearJacobian.angular1;b2.m_linearVelocity.x+=(invMass2*linearImpulse)*this.m_linearJacobian.linear2.x;b2.m_linearVelocity.y+=(invMass2*linearImpulse)*this.m_linearJacobian.linear2.y;b2.m_angularVelocity+=invI2*linearImpulse*this.m_linearJacobian.angular2;var angularCdot=b2.m_angularVelocity-b1.m_angularVelocity;var angularImpulse=-this.m_angularMass*angularCdot;this.m_angularImpulse+=angularImpulse;b1.m_angularVelocity-=invI1*angularImpulse;b2.m_angularVelocity+=invI2*angularImpulse;if(this.m_enableMotor&&this.m_limitState!=b2Joint.e_equalLimits) +{var motorCdot=this.m_motorJacobian.Compute(b1.m_linearVelocity,b1.m_angularVelocity,b2.m_linearVelocity,b2.m_angularVelocity)-this.m_motorSpeed;var motorImpulse=-this.m_motorMass*motorCdot;var oldMotorImpulse=this.m_motorImpulse;this.m_motorImpulse=b2Math.b2Clamp(this.m_motorImpulse+motorImpulse,-step.dt*this.m_maxMotorForce,step.dt*this.m_maxMotorForce);motorImpulse=this.m_motorImpulse-oldMotorImpulse;b1.m_linearVelocity.x+=(invMass1*motorImpulse)*this.m_motorJacobian.linear1.x;b1.m_linearVelocity.y+=(invMass1*motorImpulse)*this.m_motorJacobian.linear1.y;b1.m_angularVelocity+=invI1*motorImpulse*this.m_motorJacobian.angular1;b2.m_linearVelocity.x+=(invMass2*motorImpulse)*this.m_motorJacobian.linear2.x;b2.m_linearVelocity.y+=(invMass2*motorImpulse)*this.m_motorJacobian.linear2.y;b2.m_angularVelocity+=invI2*motorImpulse*this.m_motorJacobian.angular2;} +if(this.m_enableLimit&&this.m_limitState!=b2Joint.e_inactiveLimit) +{var limitCdot=this.m_motorJacobian.Compute(b1.m_linearVelocity,b1.m_angularVelocity,b2.m_linearVelocity,b2.m_angularVelocity);var limitImpulse=-this.m_motorMass*limitCdot;if(this.m_limitState==b2Joint.e_equalLimits) +{this.m_limitImpulse+=limitImpulse;} +else if(this.m_limitState==b2Joint.e_atLowerLimit) +{oldLimitImpulse=this.m_limitImpulse;this.m_limitImpulse=b2Math.b2Max(this.m_limitImpulse+limitImpulse,0.0);limitImpulse=this.m_limitImpulse-oldLimitImpulse;} +else if(this.m_limitState==b2Joint.e_atUpperLimit) +{oldLimitImpulse=this.m_limitImpulse;this.m_limitImpulse=b2Math.b2Min(this.m_limitImpulse+limitImpulse,0.0);limitImpulse=this.m_limitImpulse-oldLimitImpulse;} +b1.m_linearVelocity.x+=(invMass1*limitImpulse)*this.m_motorJacobian.linear1.x;b1.m_linearVelocity.y+=(invMass1*limitImpulse)*this.m_motorJacobian.linear1.y;b1.m_angularVelocity+=invI1*limitImpulse*this.m_motorJacobian.angular1;b2.m_linearVelocity.x+=(invMass2*limitImpulse)*this.m_motorJacobian.linear2.x;b2.m_linearVelocity.y+=(invMass2*limitImpulse)*this.m_motorJacobian.linear2.y;b2.m_angularVelocity+=invI2*limitImpulse*this.m_motorJacobian.angular2;}},SolvePositionConstraints:function(){var limitC;var oldLimitImpulse;var b1=this.m_body1;var b2=this.m_body2;var invMass1=b1.m_invMass;var invMass2=b2.m_invMass;var invI1=b1.m_invI;var invI2=b2.m_invI;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var p1X=b1.m_position.x+r1X;var p1Y=b1.m_position.y+r1Y;var p2X=b2.m_position.x+r2X;var p2Y=b2.m_position.y+r2Y;var dX=p2X-p1X;var dY=p2Y-p1Y;tMat=b1.m_R;var ay1X=tMat.col1.x*this.m_localYAxis1.x+tMat.col2.x*this.m_localYAxis1.y;var ay1Y=tMat.col1.y*this.m_localYAxis1.x+tMat.col2.y*this.m_localYAxis1.y;var linearC=ay1X*dX+ay1Y*dY;linearC=b2Math.b2Clamp(linearC,-b2Settings.b2_maxLinearCorrection,b2Settings.b2_maxLinearCorrection);var linearImpulse=-this.m_linearMass*linearC;b1.m_position.x+=(invMass1*linearImpulse)*this.m_linearJacobian.linear1.x;b1.m_position.y+=(invMass1*linearImpulse)*this.m_linearJacobian.linear1.y;b1.m_rotation+=invI1*linearImpulse*this.m_linearJacobian.angular1;b2.m_position.x+=(invMass2*linearImpulse)*this.m_linearJacobian.linear2.x;b2.m_position.y+=(invMass2*linearImpulse)*this.m_linearJacobian.linear2.y;b2.m_rotation+=invI2*linearImpulse*this.m_linearJacobian.angular2;var positionError=b2Math.b2Abs(linearC);var angularC=b2.m_rotation-b1.m_rotation-this.m_initialAngle;angularC=b2Math.b2Clamp(angularC,-b2Settings.b2_maxAngularCorrection,b2Settings.b2_maxAngularCorrection);var angularImpulse=-this.m_angularMass*angularC;b1.m_rotation-=b1.m_invI*angularImpulse;b1.m_R.Set(b1.m_rotation);b2.m_rotation+=b2.m_invI*angularImpulse;b2.m_R.Set(b2.m_rotation);var angularError=b2Math.b2Abs(angularC);if(this.m_enableLimit&&this.m_limitState!=b2Joint.e_inactiveLimit) +{tMat=b1.m_R;r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;p1X=b1.m_position.x+r1X;p1Y=b1.m_position.y+r1Y;p2X=b2.m_position.x+r2X;p2Y=b2.m_position.y+r2Y;dX=p2X-p1X;dY=p2Y-p1Y;tMat=b1.m_R;var ax1X=tMat.col1.x*this.m_localXAxis1.x+tMat.col2.x*this.m_localXAxis1.y;var ax1Y=tMat.col1.y*this.m_localXAxis1.x+tMat.col2.y*this.m_localXAxis1.y;var translation=(ax1X*dX+ax1Y*dY);var limitImpulse=0.0;if(this.m_limitState==b2Joint.e_equalLimits) +{limitC=b2Math.b2Clamp(translation,-b2Settings.b2_maxLinearCorrection,b2Settings.b2_maxLinearCorrection);limitImpulse=-this.m_motorMass*limitC;positionError=b2Math.b2Max(positionError,b2Math.b2Abs(angularC));} +else if(this.m_limitState==b2Joint.e_atLowerLimit) +{limitC=translation-this.m_lowerTranslation;positionError=b2Math.b2Max(positionError,-limitC);limitC=b2Math.b2Clamp(limitC+b2Settings.b2_linearSlop,-b2Settings.b2_maxLinearCorrection,0.0);limitImpulse=-this.m_motorMass*limitC;oldLimitImpulse=this.m_limitPositionImpulse;this.m_limitPositionImpulse=b2Math.b2Max(this.m_limitPositionImpulse+limitImpulse,0.0);limitImpulse=this.m_limitPositionImpulse-oldLimitImpulse;} +else if(this.m_limitState==b2Joint.e_atUpperLimit) +{limitC=translation-this.m_upperTranslation;positionError=b2Math.b2Max(positionError,limitC);limitC=b2Math.b2Clamp(limitC-b2Settings.b2_linearSlop,0.0,b2Settings.b2_maxLinearCorrection);limitImpulse=-this.m_motorMass*limitC;oldLimitImpulse=this.m_limitPositionImpulse;this.m_limitPositionImpulse=b2Math.b2Min(this.m_limitPositionImpulse+limitImpulse,0.0);limitImpulse=this.m_limitPositionImpulse-oldLimitImpulse;} +b1.m_position.x+=(invMass1*limitImpulse)*this.m_motorJacobian.linear1.x;b1.m_position.y+=(invMass1*limitImpulse)*this.m_motorJacobian.linear1.y;b1.m_rotation+=invI1*limitImpulse*this.m_motorJacobian.angular1;b1.m_R.Set(b1.m_rotation);b2.m_position.x+=(invMass2*limitImpulse)*this.m_motorJacobian.linear2.x;b2.m_position.y+=(invMass2*limitImpulse)*this.m_motorJacobian.linear2.y;b2.m_rotation+=invI2*limitImpulse*this.m_motorJacobian.angular2;b2.m_R.Set(b2.m_rotation);} +return positionError<=b2Settings.b2_linearSlop&&angularError<=b2Settings.b2_angularSlop;},m_localAnchor1:new b2Vec2(),m_localAnchor2:new b2Vec2(),m_localXAxis1:new b2Vec2(),m_localYAxis1:new b2Vec2(),m_initialAngle:null,m_linearJacobian:new b2Jacobian(),m_linearMass:null,m_linearImpulse:null,m_angularMass:null,m_angularImpulse:null,m_motorJacobian:new b2Jacobian(),m_motorMass:null,m_motorImpulse:null,m_limitImpulse:null,m_limitPositionImpulse:null,m_lowerTranslation:null,m_upperTranslation:null,m_maxMotorForce:null,m_motorSpeed:null,m_enableLimit:null,m_enableMotor:null,m_limitState:0}); +var b2PrismaticJointDef=Class.create();Object.extend(b2PrismaticJointDef.prototype,b2JointDef.prototype);Object.extend(b2PrismaticJointDef.prototype,{initialize:function() +{this.type=b2Joint.e_unknownJoint;this.userData=null;this.body1=null;this.body2=null;this.collideConnected=false;this.type=b2Joint.e_prismaticJoint;this.anchorPoint=new b2Vec2(0.0,0.0);this.axis=new b2Vec2(0.0,0.0);this.lowerTranslation=0.0;this.upperTranslation=0.0;this.motorForce=0.0;this.motorSpeed=0.0;this.enableLimit=false;this.enableMotor=false;},anchorPoint:null,axis:null,lowerTranslation:null,upperTranslation:null,motorForce:null,motorSpeed:null,enableLimit:null,enableMotor:null}); +var b2PulleyJoint=Class.create();Object.extend(b2PulleyJoint.prototype,b2Joint.prototype);Object.extend(b2PulleyJoint.prototype,{GetAnchor1:function(){var tMat=this.m_body1.m_R;return new b2Vec2(this.m_body1.m_position.x+(tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y),this.m_body1.m_position.y+(tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y));},GetAnchor2:function(){var tMat=this.m_body2.m_R;return new b2Vec2(this.m_body2.m_position.x+(tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y),this.m_body2.m_position.y+(tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y));},GetGroundPoint1:function(){return new b2Vec2(this.m_ground.m_position.x+this.m_groundAnchor1.x,this.m_ground.m_position.y+this.m_groundAnchor1.y);},GetGroundPoint2:function(){return new b2Vec2(this.m_ground.m_position.x+this.m_groundAnchor2.x,this.m_ground.m_position.y+this.m_groundAnchor2.y);},GetReactionForce:function(invTimeStep){return new b2Vec2();},GetReactionTorque:function(invTimeStep){return 0.0;},GetLength1:function(){var tMat;tMat=this.m_body1.m_R;var pX=this.m_body1.m_position.x+(tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y);var pY=this.m_body1.m_position.y+(tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y);var dX=pX-(this.m_ground.m_position.x+this.m_groundAnchor1.x);var dY=pY-(this.m_ground.m_position.y+this.m_groundAnchor1.y);return Math.sqrt(dX*dX+dY*dY);},GetLength2:function(){var tMat;tMat=this.m_body2.m_R;var pX=this.m_body2.m_position.x+(tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y);var pY=this.m_body2.m_position.y+(tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y);var dX=pX-(this.m_ground.m_position.x+this.m_groundAnchor2.x);var dY=pY-(this.m_ground.m_position.y+this.m_groundAnchor2.y);return Math.sqrt(dX*dX+dY*dY);},GetRatio:function(){return this.m_ratio;},initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;this.m_groundAnchor1=new b2Vec2();this.m_groundAnchor2=new b2Vec2();this.m_localAnchor1=new b2Vec2();this.m_localAnchor2=new b2Vec2();this.m_u1=new b2Vec2();this.m_u2=new b2Vec2();var tMat;var tX;var tY;this.m_ground=this.m_body1.m_world.m_groundBody;this.m_groundAnchor1.x=def.groundPoint1.x-this.m_ground.m_position.x;this.m_groundAnchor1.y=def.groundPoint1.y-this.m_ground.m_position.y;this.m_groundAnchor2.x=def.groundPoint2.x-this.m_ground.m_position.x;this.m_groundAnchor2.y=def.groundPoint2.y-this.m_ground.m_position.y;tMat=this.m_body1.m_R;tX=def.anchorPoint1.x-this.m_body1.m_position.x;tY=def.anchorPoint1.y-this.m_body1.m_position.y;this.m_localAnchor1.x=tX*tMat.col1.x+tY*tMat.col1.y;this.m_localAnchor1.y=tX*tMat.col2.x+tY*tMat.col2.y;tMat=this.m_body2.m_R;tX=def.anchorPoint2.x-this.m_body2.m_position.x;tY=def.anchorPoint2.y-this.m_body2.m_position.y;this.m_localAnchor2.x=tX*tMat.col1.x+tY*tMat.col1.y;this.m_localAnchor2.y=tX*tMat.col2.x+tY*tMat.col2.y;this.m_ratio=def.ratio;tX=def.groundPoint1.x-def.anchorPoint1.x;tY=def.groundPoint1.y-def.anchorPoint1.y;var d1Len=Math.sqrt(tX*tX+tY*tY);tX=def.groundPoint2.x-def.anchorPoint2.x;tY=def.groundPoint2.y-def.anchorPoint2.y;var d2Len=Math.sqrt(tX*tX+tY*tY);var length1=b2Math.b2Max(0.5*b2PulleyJoint.b2_minPulleyLength,d1Len);var length2=b2Math.b2Max(0.5*b2PulleyJoint.b2_minPulleyLength,d2Len);this.m_constant=length1+this.m_ratio*length2;this.m_maxLength1=b2Math.b2Clamp(def.maxLength1,length1,this.m_constant-this.m_ratio*b2PulleyJoint.b2_minPulleyLength);this.m_maxLength2=b2Math.b2Clamp(def.maxLength2,length2,(this.m_constant-b2PulleyJoint.b2_minPulleyLength)/this.m_ratio);this.m_pulleyImpulse=0.0;this.m_limitImpulse1=0.0;this.m_limitImpulse2=0.0;},PrepareVelocitySolver:function(){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var p1X=b1.m_position.x+r1X;var p1Y=b1.m_position.y+r1Y;var p2X=b2.m_position.x+r2X;var p2Y=b2.m_position.y+r2Y;var s1X=this.m_ground.m_position.x+this.m_groundAnchor1.x;var s1Y=this.m_ground.m_position.y+this.m_groundAnchor1.y;var s2X=this.m_ground.m_position.x+this.m_groundAnchor2.x;var s2Y=this.m_ground.m_position.y+this.m_groundAnchor2.y;this.m_u1.Set(p1X-s1X,p1Y-s1Y);this.m_u2.Set(p2X-s2X,p2Y-s2Y);var length1=this.m_u1.Length();var length2=this.m_u2.Length();if(length1>b2Settings.b2_linearSlop) +{this.m_u1.Multiply(1.0/length1);} +else +{this.m_u1.SetZero();} +if(length2>b2Settings.b2_linearSlop) +{this.m_u2.Multiply(1.0/length2);} +else +{this.m_u2.SetZero();} +if(length1<this.m_maxLength1) +{this.m_limitState1=b2Joint.e_inactiveLimit;this.m_limitImpulse1=0.0;} +else +{this.m_limitState1=b2Joint.e_atUpperLimit;this.m_limitPositionImpulse1=0.0;} +if(length2<this.m_maxLength2) +{this.m_limitState2=b2Joint.e_inactiveLimit;this.m_limitImpulse2=0.0;} +else +{this.m_limitState2=b2Joint.e_atUpperLimit;this.m_limitPositionImpulse2=0.0;} +var cr1u1=r1X*this.m_u1.y-r1Y*this.m_u1.x;var cr2u2=r2X*this.m_u2.y-r2Y*this.m_u2.x;this.m_limitMass1=b1.m_invMass+b1.m_invI*cr1u1*cr1u1;this.m_limitMass2=b2.m_invMass+b2.m_invI*cr2u2*cr2u2;this.m_pulleyMass=this.m_limitMass1+this.m_ratio*this.m_ratio*this.m_limitMass2;this.m_limitMass1=1.0/this.m_limitMass1;this.m_limitMass2=1.0/this.m_limitMass2;this.m_pulleyMass=1.0/this.m_pulleyMass;var P1X=(-this.m_pulleyImpulse-this.m_limitImpulse1)*this.m_u1.x;var P1Y=(-this.m_pulleyImpulse-this.m_limitImpulse1)*this.m_u1.y;var P2X=(-this.m_ratio*this.m_pulleyImpulse-this.m_limitImpulse2)*this.m_u2.x;var P2Y=(-this.m_ratio*this.m_pulleyImpulse-this.m_limitImpulse2)*this.m_u2.y;b1.m_linearVelocity.x+=b1.m_invMass*P1X;b1.m_linearVelocity.y+=b1.m_invMass*P1Y;b1.m_angularVelocity+=b1.m_invI*(r1X*P1Y-r1Y*P1X);b2.m_linearVelocity.x+=b2.m_invMass*P2X;b2.m_linearVelocity.y+=b2.m_invMass*P2Y;b2.m_angularVelocity+=b2.m_invI*(r2X*P2Y-r2Y*P2X);},SolveVelocityConstraints:function(step){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var v1X;var v1Y;var v2X;var v2Y;var P1X;var P1Y;var P2X;var P2Y;var Cdot;var impulse;var oldLimitImpulse;v1X=b1.m_linearVelocity.x+(-b1.m_angularVelocity*r1Y);v1Y=b1.m_linearVelocity.y+(b1.m_angularVelocity*r1X);v2X=b2.m_linearVelocity.x+(-b2.m_angularVelocity*r2Y);v2Y=b2.m_linearVelocity.y+(b2.m_angularVelocity*r2X);Cdot=-(this.m_u1.x*v1X+this.m_u1.y*v1Y)-this.m_ratio*(this.m_u2.x*v2X+this.m_u2.y*v2Y);impulse=-this.m_pulleyMass*Cdot;this.m_pulleyImpulse+=impulse;P1X=-impulse*this.m_u1.x;P1Y=-impulse*this.m_u1.y;P2X=-this.m_ratio*impulse*this.m_u2.x;P2Y=-this.m_ratio*impulse*this.m_u2.y;b1.m_linearVelocity.x+=b1.m_invMass*P1X;b1.m_linearVelocity.y+=b1.m_invMass*P1Y;b1.m_angularVelocity+=b1.m_invI*(r1X*P1Y-r1Y*P1X);b2.m_linearVelocity.x+=b2.m_invMass*P2X;b2.m_linearVelocity.y+=b2.m_invMass*P2Y;b2.m_angularVelocity+=b2.m_invI*(r2X*P2Y-r2Y*P2X);if(this.m_limitState1==b2Joint.e_atUpperLimit) +{v1X=b1.m_linearVelocity.x+(-b1.m_angularVelocity*r1Y);v1Y=b1.m_linearVelocity.y+(b1.m_angularVelocity*r1X);Cdot=-(this.m_u1.x*v1X+this.m_u1.y*v1Y);impulse=-this.m_limitMass1*Cdot;oldLimitImpulse=this.m_limitImpulse1;this.m_limitImpulse1=b2Math.b2Max(0.0,this.m_limitImpulse1+impulse);impulse=this.m_limitImpulse1-oldLimitImpulse;P1X=-impulse*this.m_u1.x;P1Y=-impulse*this.m_u1.y;b1.m_linearVelocity.x+=b1.m_invMass*P1X;b1.m_linearVelocity.y+=b1.m_invMass*P1Y;b1.m_angularVelocity+=b1.m_invI*(r1X*P1Y-r1Y*P1X);} +if(this.m_limitState2==b2Joint.e_atUpperLimit) +{v2X=b2.m_linearVelocity.x+(-b2.m_angularVelocity*r2Y);v2Y=b2.m_linearVelocity.y+(b2.m_angularVelocity*r2X);Cdot=-(this.m_u2.x*v2X+this.m_u2.y*v2Y);impulse=-this.m_limitMass2*Cdot;oldLimitImpulse=this.m_limitImpulse2;this.m_limitImpulse2=b2Math.b2Max(0.0,this.m_limitImpulse2+impulse);impulse=this.m_limitImpulse2-oldLimitImpulse;P2X=-impulse*this.m_u2.x;P2Y=-impulse*this.m_u2.y;b2.m_linearVelocity.x+=b2.m_invMass*P2X;b2.m_linearVelocity.y+=b2.m_invMass*P2Y;b2.m_angularVelocity+=b2.m_invI*(r2X*P2Y-r2Y*P2X);}},SolvePositionConstraints:function(){var b1=this.m_body1;var b2=this.m_body2;var tMat;var s1X=this.m_ground.m_position.x+this.m_groundAnchor1.x;var s1Y=this.m_ground.m_position.y+this.m_groundAnchor1.y;var s2X=this.m_ground.m_position.x+this.m_groundAnchor2.x;var s2Y=this.m_ground.m_position.y+this.m_groundAnchor2.y;var r1X;var r1Y;var r2X;var r2Y;var p1X;var p1Y;var p2X;var p2Y;var length1;var length2;var C;var impulse;var oldLimitPositionImpulse;var linearError=0.0;{tMat=b1.m_R;r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;p1X=b1.m_position.x+r1X;p1Y=b1.m_position.y+r1Y;p2X=b2.m_position.x+r2X;p2Y=b2.m_position.y+r2Y;this.m_u1.Set(p1X-s1X,p1Y-s1Y);this.m_u2.Set(p2X-s2X,p2Y-s2Y);length1=this.m_u1.Length();length2=this.m_u2.Length();if(length1>b2Settings.b2_linearSlop) +{this.m_u1.Multiply(1.0/length1);} +else +{this.m_u1.SetZero();} +if(length2>b2Settings.b2_linearSlop) +{this.m_u2.Multiply(1.0/length2);} +else +{this.m_u2.SetZero();} +C=this.m_constant-length1-this.m_ratio*length2;linearError=b2Math.b2Max(linearError,Math.abs(C));C=b2Math.b2Clamp(C,-b2Settings.b2_maxLinearCorrection,b2Settings.b2_maxLinearCorrection);impulse=-this.m_pulleyMass*C;p1X=-impulse*this.m_u1.x;p1Y=-impulse*this.m_u1.y;p2X=-this.m_ratio*impulse*this.m_u2.x;p2Y=-this.m_ratio*impulse*this.m_u2.y;b1.m_position.x+=b1.m_invMass*p1X;b1.m_position.y+=b1.m_invMass*p1Y;b1.m_rotation+=b1.m_invI*(r1X*p1Y-r1Y*p1X);b2.m_position.x+=b2.m_invMass*p2X;b2.m_position.y+=b2.m_invMass*p2Y;b2.m_rotation+=b2.m_invI*(r2X*p2Y-r2Y*p2X);b1.m_R.Set(b1.m_rotation);b2.m_R.Set(b2.m_rotation);} +if(this.m_limitState1==b2Joint.e_atUpperLimit) +{tMat=b1.m_R;r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;p1X=b1.m_position.x+r1X;p1Y=b1.m_position.y+r1Y;this.m_u1.Set(p1X-s1X,p1Y-s1Y);length1=this.m_u1.Length();if(length1>b2Settings.b2_linearSlop) +{this.m_u1.x*=1.0/length1;this.m_u1.y*=1.0/length1;} +else +{this.m_u1.SetZero();} +C=this.m_maxLength1-length1;linearError=b2Math.b2Max(linearError,-C);C=b2Math.b2Clamp(C+b2Settings.b2_linearSlop,-b2Settings.b2_maxLinearCorrection,0.0);impulse=-this.m_limitMass1*C;oldLimitPositionImpulse=this.m_limitPositionImpulse1;this.m_limitPositionImpulse1=b2Math.b2Max(0.0,this.m_limitPositionImpulse1+impulse);impulse=this.m_limitPositionImpulse1-oldLimitPositionImpulse;p1X=-impulse*this.m_u1.x;p1Y=-impulse*this.m_u1.y;b1.m_position.x+=b1.m_invMass*p1X;b1.m_position.y+=b1.m_invMass*p1Y;b1.m_rotation+=b1.m_invI*(r1X*p1Y-r1Y*p1X);b1.m_R.Set(b1.m_rotation);} +if(this.m_limitState2==b2Joint.e_atUpperLimit) +{tMat=b2.m_R;r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;p2X=b2.m_position.x+r2X;p2Y=b2.m_position.y+r2Y;this.m_u2.Set(p2X-s2X,p2Y-s2Y);length2=this.m_u2.Length();if(length2>b2Settings.b2_linearSlop) +{this.m_u2.x*=1.0/length2;this.m_u2.y*=1.0/length2;} +else +{this.m_u2.SetZero();} +C=this.m_maxLength2-length2;linearError=b2Math.b2Max(linearError,-C);C=b2Math.b2Clamp(C+b2Settings.b2_linearSlop,-b2Settings.b2_maxLinearCorrection,0.0);impulse=-this.m_limitMass2*C;oldLimitPositionImpulse=this.m_limitPositionImpulse2;this.m_limitPositionImpulse2=b2Math.b2Max(0.0,this.m_limitPositionImpulse2+impulse);impulse=this.m_limitPositionImpulse2-oldLimitPositionImpulse;p2X=-impulse*this.m_u2.x;p2Y=-impulse*this.m_u2.y;b2.m_position.x+=b2.m_invMass*p2X;b2.m_position.y+=b2.m_invMass*p2Y;b2.m_rotation+=b2.m_invI*(r2X*p2Y-r2Y*p2X);b2.m_R.Set(b2.m_rotation);} +return linearError<b2Settings.b2_linearSlop;},m_ground:null,m_groundAnchor1:new b2Vec2(),m_groundAnchor2:new b2Vec2(),m_localAnchor1:new b2Vec2(),m_localAnchor2:new b2Vec2(),m_u1:new b2Vec2(),m_u2:new b2Vec2(),m_constant:null,m_ratio:null,m_maxLength1:null,m_maxLength2:null,m_pulleyMass:null,m_limitMass1:null,m_limitMass2:null,m_pulleyImpulse:null,m_limitImpulse1:null,m_limitImpulse2:null,m_limitPositionImpulse1:null,m_limitPositionImpulse2:null,m_limitState1:0,m_limitState2:0});b2PulleyJoint.b2_minPulleyLength=b2Settings.b2_lengthUnitsPerMeter; +var b2PulleyJointDef=Class.create();Object.extend(b2PulleyJointDef.prototype,b2JointDef.prototype);Object.extend(b2PulleyJointDef.prototype,{initialize:function() +{this.type=b2Joint.e_unknownJoint;this.userData=null;this.body1=null;this.body2=null;this.collideConnected=false;this.groundPoint1=new b2Vec2();this.groundPoint2=new b2Vec2();this.anchorPoint1=new b2Vec2();this.anchorPoint2=new b2Vec2();this.type=b2Joint.e_pulleyJoint;this.groundPoint1.Set(-1.0,1.0);this.groundPoint2.Set(1.0,1.0);this.anchorPoint1.Set(-1.0,0.0);this.anchorPoint2.Set(1.0,0.0);this.maxLength1=0.5*b2PulleyJoint.b2_minPulleyLength;this.maxLength2=0.5*b2PulleyJoint.b2_minPulleyLength;this.ratio=1.0;this.collideConnected=true;},groundPoint1:new b2Vec2(),groundPoint2:new b2Vec2(),anchorPoint1:new b2Vec2(),anchorPoint2:new b2Vec2(),maxLength1:null,maxLength2:null,ratio:null}); +var b2RevoluteJoint=Class.create();Object.extend(b2RevoluteJoint.prototype,b2Joint.prototype);Object.extend(b2RevoluteJoint.prototype,{GetAnchor1:function(){var tMat=this.m_body1.m_R;return new b2Vec2(this.m_body1.m_position.x+(tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y),this.m_body1.m_position.y+(tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y));},GetAnchor2:function(){var tMat=this.m_body2.m_R;return new b2Vec2(this.m_body2.m_position.x+(tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y),this.m_body2.m_position.y+(tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y));},GetJointAngle:function(){return this.m_body2.m_rotation-this.m_body1.m_rotation;},GetJointSpeed:function(){return this.m_body2.m_angularVelocity-this.m_body1.m_angularVelocity;},GetMotorTorque:function(invTimeStep){return invTimeStep*this.m_motorImpulse;},SetMotorSpeed:function(speed) +{this.m_motorSpeed=speed;},SetMotorTorque:function(torque) +{this.m_maxMotorTorque=torque;},GetReactionForce:function(invTimeStep) +{var tVec=this.m_ptpImpulse.Copy();tVec.Multiply(invTimeStep);return tVec;},GetReactionTorque:function(invTimeStep) +{return invTimeStep*this.m_limitImpulse;},initialize:function(def){this.m_node1=new b2JointNode();this.m_node2=new b2JointNode();this.m_type=def.type;this.m_prev=null;this.m_next=null;this.m_body1=def.body1;this.m_body2=def.body2;this.m_collideConnected=def.collideConnected;this.m_islandFlag=false;this.m_userData=def.userData;this.K=new b2Mat22();this.K1=new b2Mat22();this.K2=new b2Mat22();this.K3=new b2Mat22();this.m_localAnchor1=new b2Vec2();this.m_localAnchor2=new b2Vec2();this.m_ptpImpulse=new b2Vec2();this.m_ptpMass=new b2Mat22();var tMat;var tX;var tY;tMat=this.m_body1.m_R;tX=def.anchorPoint.x-this.m_body1.m_position.x;tY=def.anchorPoint.y-this.m_body1.m_position.y;this.m_localAnchor1.x=tX*tMat.col1.x+tY*tMat.col1.y;this.m_localAnchor1.y=tX*tMat.col2.x+tY*tMat.col2.y;tMat=this.m_body2.m_R;tX=def.anchorPoint.x-this.m_body2.m_position.x;tY=def.anchorPoint.y-this.m_body2.m_position.y;this.m_localAnchor2.x=tX*tMat.col1.x+tY*tMat.col1.y;this.m_localAnchor2.y=tX*tMat.col2.x+tY*tMat.col2.y;this.m_intialAngle=this.m_body2.m_rotation-this.m_body1.m_rotation;this.m_ptpImpulse.Set(0.0,0.0);this.m_motorImpulse=0.0;this.m_limitImpulse=0.0;this.m_limitPositionImpulse=0.0;this.m_lowerAngle=def.lowerAngle;this.m_upperAngle=def.upperAngle;this.m_maxMotorTorque=def.motorTorque;this.m_motorSpeed=def.motorSpeed;this.m_enableLimit=def.enableLimit;this.m_enableMotor=def.enableMotor;},K:new b2Mat22(),K1:new b2Mat22(),K2:new b2Mat22(),K3:new b2Mat22(),PrepareVelocitySolver:function(){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var invMass1=b1.m_invMass;var invMass2=b2.m_invMass;var invI1=b1.m_invI;var invI2=b2.m_invI;this.K1.col1.x=invMass1+invMass2;this.K1.col2.x=0.0;this.K1.col1.y=0.0;this.K1.col2.y=invMass1+invMass2;this.K2.col1.x=invI1*r1Y*r1Y;this.K2.col2.x=-invI1*r1X*r1Y;this.K2.col1.y=-invI1*r1X*r1Y;this.K2.col2.y=invI1*r1X*r1X;this.K3.col1.x=invI2*r2Y*r2Y;this.K3.col2.x=-invI2*r2X*r2Y;this.K3.col1.y=-invI2*r2X*r2Y;this.K3.col2.y=invI2*r2X*r2X;this.K.SetM(this.K1);this.K.AddM(this.K2);this.K.AddM(this.K3);this.K.Invert(this.m_ptpMass);this.m_motorMass=1.0/(invI1+invI2);if(this.m_enableMotor==false) +{this.m_motorImpulse=0.0;} +if(this.m_enableLimit) +{var jointAngle=b2.m_rotation-b1.m_rotation-this.m_intialAngle;if(b2Math.b2Abs(this.m_upperAngle-this.m_lowerAngle)<2.0*b2Settings.b2_angularSlop) +{this.m_limitState=b2Joint.e_equalLimits;} +else if(jointAngle<=this.m_lowerAngle) +{if(this.m_limitState!=b2Joint.e_atLowerLimit) +{this.m_limitImpulse=0.0;} +this.m_limitState=b2Joint.e_atLowerLimit;} +else if(jointAngle>=this.m_upperAngle) +{if(this.m_limitState!=b2Joint.e_atUpperLimit) +{this.m_limitImpulse=0.0;} +this.m_limitState=b2Joint.e_atUpperLimit;} +else +{this.m_limitState=b2Joint.e_inactiveLimit;this.m_limitImpulse=0.0;}} +else +{this.m_limitImpulse=0.0;} +if(b2World.s_enableWarmStarting) +{b1.m_linearVelocity.x-=invMass1*this.m_ptpImpulse.x;b1.m_linearVelocity.y-=invMass1*this.m_ptpImpulse.y;b1.m_angularVelocity-=invI1*((r1X*this.m_ptpImpulse.y-r1Y*this.m_ptpImpulse.x)+this.m_motorImpulse+this.m_limitImpulse);b2.m_linearVelocity.x+=invMass2*this.m_ptpImpulse.x;b2.m_linearVelocity.y+=invMass2*this.m_ptpImpulse.y;b2.m_angularVelocity+=invI2*((r2X*this.m_ptpImpulse.y-r2Y*this.m_ptpImpulse.x)+this.m_motorImpulse+this.m_limitImpulse);} +else{this.m_ptpImpulse.SetZero();this.m_motorImpulse=0.0;this.m_limitImpulse=0.0;} +this.m_limitPositionImpulse=0.0;},SolveVelocityConstraints:function(step){var b1=this.m_body1;var b2=this.m_body2;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var oldLimitImpulse;var ptpCdotX=b2.m_linearVelocity.x+(-b2.m_angularVelocity*r2Y)-b1.m_linearVelocity.x-(-b1.m_angularVelocity*r1Y);var ptpCdotY=b2.m_linearVelocity.y+(b2.m_angularVelocity*r2X)-b1.m_linearVelocity.y-(b1.m_angularVelocity*r1X);var ptpImpulseX=-(this.m_ptpMass.col1.x*ptpCdotX+this.m_ptpMass.col2.x*ptpCdotY);var ptpImpulseY=-(this.m_ptpMass.col1.y*ptpCdotX+this.m_ptpMass.col2.y*ptpCdotY);this.m_ptpImpulse.x+=ptpImpulseX;this.m_ptpImpulse.y+=ptpImpulseY;b1.m_linearVelocity.x-=b1.m_invMass*ptpImpulseX;b1.m_linearVelocity.y-=b1.m_invMass*ptpImpulseY;b1.m_angularVelocity-=b1.m_invI*(r1X*ptpImpulseY-r1Y*ptpImpulseX);b2.m_linearVelocity.x+=b2.m_invMass*ptpImpulseX;b2.m_linearVelocity.y+=b2.m_invMass*ptpImpulseY;b2.m_angularVelocity+=b2.m_invI*(r2X*ptpImpulseY-r2Y*ptpImpulseX);if(this.m_enableMotor&&this.m_limitState!=b2Joint.e_equalLimits) +{var motorCdot=b2.m_angularVelocity-b1.m_angularVelocity-this.m_motorSpeed;var motorImpulse=-this.m_motorMass*motorCdot;var oldMotorImpulse=this.m_motorImpulse;this.m_motorImpulse=b2Math.b2Clamp(this.m_motorImpulse+motorImpulse,-step.dt*this.m_maxMotorTorque,step.dt*this.m_maxMotorTorque);motorImpulse=this.m_motorImpulse-oldMotorImpulse;b1.m_angularVelocity-=b1.m_invI*motorImpulse;b2.m_angularVelocity+=b2.m_invI*motorImpulse;} +if(this.m_enableLimit&&this.m_limitState!=b2Joint.e_inactiveLimit) +{var limitCdot=b2.m_angularVelocity-b1.m_angularVelocity;var limitImpulse=-this.m_motorMass*limitCdot;if(this.m_limitState==b2Joint.e_equalLimits) +{this.m_limitImpulse+=limitImpulse;} +else if(this.m_limitState==b2Joint.e_atLowerLimit) +{oldLimitImpulse=this.m_limitImpulse;this.m_limitImpulse=b2Math.b2Max(this.m_limitImpulse+limitImpulse,0.0);limitImpulse=this.m_limitImpulse-oldLimitImpulse;} +else if(this.m_limitState==b2Joint.e_atUpperLimit) +{oldLimitImpulse=this.m_limitImpulse;this.m_limitImpulse=b2Math.b2Min(this.m_limitImpulse+limitImpulse,0.0);limitImpulse=this.m_limitImpulse-oldLimitImpulse;} +b1.m_angularVelocity-=b1.m_invI*limitImpulse;b2.m_angularVelocity+=b2.m_invI*limitImpulse;}},SolvePositionConstraints:function(){var oldLimitImpulse;var limitC;var b1=this.m_body1;var b2=this.m_body2;var positionError=0.0;var tMat;tMat=b1.m_R;var r1X=tMat.col1.x*this.m_localAnchor1.x+tMat.col2.x*this.m_localAnchor1.y;var r1Y=tMat.col1.y*this.m_localAnchor1.x+tMat.col2.y*this.m_localAnchor1.y;tMat=b2.m_R;var r2X=tMat.col1.x*this.m_localAnchor2.x+tMat.col2.x*this.m_localAnchor2.y;var r2Y=tMat.col1.y*this.m_localAnchor2.x+tMat.col2.y*this.m_localAnchor2.y;var p1X=b1.m_position.x+r1X;var p1Y=b1.m_position.y+r1Y;var p2X=b2.m_position.x+r2X;var p2Y=b2.m_position.y+r2Y;var ptpCX=p2X-p1X;var ptpCY=p2Y-p1Y;positionError=Math.sqrt(ptpCX*ptpCX+ptpCY*ptpCY);var invMass1=b1.m_invMass;var invMass2=b2.m_invMass;var invI1=b1.m_invI;var invI2=b2.m_invI;this.K1.col1.x=invMass1+invMass2;this.K1.col2.x=0.0;this.K1.col1.y=0.0;this.K1.col2.y=invMass1+invMass2;this.K2.col1.x=invI1*r1Y*r1Y;this.K2.col2.x=-invI1*r1X*r1Y;this.K2.col1.y=-invI1*r1X*r1Y;this.K2.col2.y=invI1*r1X*r1X;this.K3.col1.x=invI2*r2Y*r2Y;this.K3.col2.x=-invI2*r2X*r2Y;this.K3.col1.y=-invI2*r2X*r2Y;this.K3.col2.y=invI2*r2X*r2X;this.K.SetM(this.K1);this.K.AddM(this.K2);this.K.AddM(this.K3);this.K.Solve(b2RevoluteJoint.tImpulse,-ptpCX,-ptpCY);var impulseX=b2RevoluteJoint.tImpulse.x;var impulseY=b2RevoluteJoint.tImpulse.y;b1.m_position.x-=b1.m_invMass*impulseX;b1.m_position.y-=b1.m_invMass*impulseY;b1.m_rotation-=b1.m_invI*(r1X*impulseY-r1Y*impulseX);b1.m_R.Set(b1.m_rotation);b2.m_position.x+=b2.m_invMass*impulseX;b2.m_position.y+=b2.m_invMass*impulseY;b2.m_rotation+=b2.m_invI*(r2X*impulseY-r2Y*impulseX);b2.m_R.Set(b2.m_rotation);var angularError=0.0;if(this.m_enableLimit&&this.m_limitState!=b2Joint.e_inactiveLimit) +{var angle=b2.m_rotation-b1.m_rotation-this.m_intialAngle;var limitImpulse=0.0;if(this.m_limitState==b2Joint.e_equalLimits) +{limitC=b2Math.b2Clamp(angle,-b2Settings.b2_maxAngularCorrection,b2Settings.b2_maxAngularCorrection);limitImpulse=-this.m_motorMass*limitC;angularError=b2Math.b2Abs(limitC);} +else if(this.m_limitState==b2Joint.e_atLowerLimit) +{limitC=angle-this.m_lowerAngle;angularError=b2Math.b2Max(0.0,-limitC);limitC=b2Math.b2Clamp(limitC+b2Settings.b2_angularSlop,-b2Settings.b2_maxAngularCorrection,0.0);limitImpulse=-this.m_motorMass*limitC;oldLimitImpulse=this.m_limitPositionImpulse;this.m_limitPositionImpulse=b2Math.b2Max(this.m_limitPositionImpulse+limitImpulse,0.0);limitImpulse=this.m_limitPositionImpulse-oldLimitImpulse;} +else if(this.m_limitState==b2Joint.e_atUpperLimit) +{limitC=angle-this.m_upperAngle;angularError=b2Math.b2Max(0.0,limitC);limitC=b2Math.b2Clamp(limitC-b2Settings.b2_angularSlop,0.0,b2Settings.b2_maxAngularCorrection);limitImpulse=-this.m_motorMass*limitC;oldLimitImpulse=this.m_limitPositionImpulse;this.m_limitPositionImpulse=b2Math.b2Min(this.m_limitPositionImpulse+limitImpulse,0.0);limitImpulse=this.m_limitPositionImpulse-oldLimitImpulse;} +b1.m_rotation-=b1.m_invI*limitImpulse;b1.m_R.Set(b1.m_rotation);b2.m_rotation+=b2.m_invI*limitImpulse;b2.m_R.Set(b2.m_rotation);} +return positionError<=b2Settings.b2_linearSlop&&angularError<=b2Settings.b2_angularSlop;},m_localAnchor1:new b2Vec2(),m_localAnchor2:new b2Vec2(),m_ptpImpulse:new b2Vec2(),m_motorImpulse:null,m_limitImpulse:null,m_limitPositionImpulse:null,m_ptpMass:new b2Mat22(),m_motorMass:null,m_intialAngle:null,m_lowerAngle:null,m_upperAngle:null,m_maxMotorTorque:null,m_motorSpeed:null,m_enableLimit:null,m_enableMotor:null,m_limitState:0});b2RevoluteJoint.tImpulse=new b2Vec2(); +var b2RevoluteJointDef=Class.create();Object.extend(b2RevoluteJointDef.prototype,b2JointDef.prototype);Object.extend(b2RevoluteJointDef.prototype,{initialize:function() +{this.type=b2Joint.e_unknownJoint;this.userData=null;this.body1=null;this.body2=null;this.collideConnected=false;this.type=b2Joint.e_revoluteJoint;this.anchorPoint=new b2Vec2(0.0,0.0);this.lowerAngle=0.0;this.upperAngle=0.0;this.motorTorque=0.0;this.motorSpeed=0.0;this.enableLimit=false;this.enableMotor=false;},anchorPoint:null,lowerAngle:null,upperAngle:null,motorTorque:null,motorSpeed:null,enableLimit:null,enableMotor:null}); diff --git a/o3d/samples/manipulators/translate1.html b/o3d/samples/manipulators/translate1.html index dd3eadb..7debeae 100644 --- a/o3d/samples/manipulators/translate1.html +++ b/o3d/samples/manipulators/translate1.html @@ -1,273 +1,273 @@ -<!-- -Copyright 2009, Google Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"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 THE COPYRIGHT -OWNER OR CONTRIBUTORS 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. ---> - -<!-- -Translate1 manipulator - -This sample shows how to use the o3djs.manipulators.Translate1 class -to interactively drag objects around the scene. ---> -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" - "http://www.w3.org/TR/html4/loose.dtd"> -<html> -<head> -<meta http-equiv="content-type" content="text/html; charset=UTF-8"> -<title> -Translate1 Manipulator -</title> -<script type="text/javascript" src="../o3djs/base.js"></script> -<script type="text/javascript" id="o3dscript"> -o3djs.require('o3djs.util'); -o3djs.require('o3djs.math'); -o3djs.require('o3djs.quaternions'); -o3djs.require('o3djs.rendergraph'); -o3djs.require('o3djs.primitives'); -o3djs.require('o3djs.manipulators'); -o3djs.require('o3djs.effect'); - -// global variables -var g_o3dElement; -var g_client; -var g_o3d; -var g_math; -var g_pack; -var g_viewInfo; - -var g_lightPosition = [5, 5, 7]; -var g_eyePosition = [1, 5, 23]; - -var g_primitives = []; -var g_manager; - -/** - * Creates the client area. - */ -function initClient() { - window.g_finished = false; // for selenium testing. - - // Runs the sample in V8. Comment out this line to run it in the browser - // JavaScript engine, for example if you want to debug it. - // TODO(kbr): we need to investigate why turning this on is - // significantly slower than leaving it disabled. - // o3djs.util.setMainEngine(o3djs.util.Engine.V8); - - o3djs.util.makeClients(main); -} - -/** - * Initializes global variables, positions camera, draws shapes. - * @param {Array} clientElements Array of o3d object elements. - */ -function main(clientElements) { - var o3dElement = clientElements[0]; - - // Init global variables. - initGlobals(clientElements); - - // Set up the view and projection transformations. - initContext(); - - // Add the shapes to the transform heirarchy. - createShapes(); - - // Add the manipulators to the transform hierarchy. - createManipulators(); - - // Start picking; it won't do anything until the scene finishes loading. - o3djs.event.addEventListener(o3dElement, 'mousedown', onMouseDown); - o3djs.event.addEventListener(o3dElement, 'mousemove', onMouseMove); - o3djs.event.addEventListener(o3dElement, 'mouseup', onMouseUp); - - window.g_finished = true; // for selenium testing. -} - -function onMouseDown(e) { - g_manager.mousedown(e.x, e.y, - g_viewInfo.drawContext.view, - g_viewInfo.drawContext.projection, - g_client.width, - g_client.height); -} - -function onMouseMove(e) { - g_manager.mousemove(e.x, e.y, - g_viewInfo.drawContext.view, - g_viewInfo.drawContext.projection, - g_client.width, - g_client.height); - g_manager.updateInactiveManipulators(); -} - -function onMouseUp(e) { - g_manager.mouseup(); - g_manager.updateInactiveManipulators(); -} - -/** - * Initializes global variables and libraries. - */ -function initGlobals(clientElements) { - g_o3dElement = clientElements[0]; - window.g_client = g_client = g_o3dElement.client; - g_o3d = g_o3dElement.o3d; - g_math = o3djs.math; - - // Create a pack to manage the objects created. - g_pack = g_client.createPack(); - - // Create the render graph for a view. - g_viewInfo = o3djs.rendergraph.createBasicView( - g_pack, - g_client.root, - g_client.renderGraphRoot); -} - -/** - * Sets up reasonable view and projection matrices. - */ -function initContext() { - // Set up a perspective transformation for the projection. - g_viewInfo.drawContext.projection = g_math.matrix4.perspective( - g_math.degToRad(30), // 30 degree frustum. - g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio. - 1, // Near plane. - 5000); // Far plane. - - // Set up our view transformation to look towards the world origin where the - // primitives are located. - g_viewInfo.drawContext.view = g_math.matrix4.lookAt( - g_eyePosition, // eye - [0, 2, 0], // target - [0, 1, 0]); // up -} - -/** - * Creates shapes using the primitives utility library, and adds them to the - * transform graph at the root node. - */ -function createShapes() { - // Create a little tree-like hierarchy of cubes - createCubeTree(2, 1.5, [0, 0, 0], g_client.root); -} - -/** - * Creates a small tree of cubes to demonstrate interaction with a - * hierarchy of shapes. - */ -function createCubeTree(depth, edgeLength, translation, parent) { - var cur = createCube(edgeLength, translation, parent); - if (depth > 0) { - createCubeTree(depth - 1, - edgeLength / 1.5, - o3djs.math.addVector(translation, - [-1.2 * edgeLength, - 1.0 * edgeLength, - 0]), - cur); - createCubeTree(depth - 1, - edgeLength / 1.5, - o3djs.math.addVector(translation, - [1.2 * edgeLength, - 1.0 * edgeLength, - 0]), - cur); - } - return cur; -} - -/** - * Creates a cube shape using the primitives utility library, with an - * optional translation and parent. Returns the newly-created - * transform for the cube. - */ -function createCube(edgeLength, opt_translation, opt_parent) { - var cube = o3djs.primitives.createCube( - g_pack, - // A green phong-shaded material. - o3djs.material.createBasicMaterial(g_pack, - g_viewInfo, - [0, 1, 0, 1]), - edgeLength); - var transform = g_pack.createObject('Transform'); - transform.addShape(cube); - if (opt_translation) { - transform.translate(opt_translation); - } - if (opt_parent) { - transform.parent = opt_parent; - } else { - transform.parent = g_client.root; - } - g_primitives.push(transform); - return transform; -} - -/** - * Creates manipulators attached to the objects we've just created. - */ -function createManipulators() { - g_manager = o3djs.manipulators.createManager(g_pack, - g_viewInfo.performanceDrawList, - g_client.root, - null, - 0); - var jj = 2; - for (var ii = 0; ii < g_primitives.length; ii++) { - var manip = g_manager.createTranslate1(); - manip.attachTo(g_primitives[ii]); - manip.setOffsetTranslation([0, -1.5, 0]); - // Demonstrate that we can drag along arbitrary directions. - if ((++jj % 4) == 0) { - manip.setOffsetRotation(o3djs.quaternions.rotationY(Math.PI / 4)); - } - } -} - -/** - * Removes any callbacks so they don't get called after the page has unloaded. - */ -function unload() { - if (g_client) { - g_client.cleanup(); - } -} -</script> -</head> -<body onload="initClient()" onunload="unload()"> -<h1>Translate1 Manipulator Sample</h1> -This example shows how to move objects around the scene using the -Translate1 manipulator. -<br/> -<!-- Start of O3D plugin --> -<div id="o3d" style="width: 600px; height: 600px;"></div> -<!-- End of O3D plugin --> -</body> -</html> +<!-- +Copyright 2009, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 THE COPYRIGHT +OWNER OR CONTRIBUTORS 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. +--> + +<!-- +Translate1 manipulator + +This sample shows how to use the o3djs.manipulators.Translate1 class +to interactively drag objects around the scene. +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> +Translate1 Manipulator +</title> +<script type="text/javascript" src="../o3djs/base.js"></script> +<script type="text/javascript" id="o3dscript"> +o3djs.require('o3djs.util'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.quaternions'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.primitives'); +o3djs.require('o3djs.manipulators'); +o3djs.require('o3djs.effect'); + +// global variables +var g_o3dElement; +var g_client; +var g_o3d; +var g_math; +var g_pack; +var g_viewInfo; + +var g_lightPosition = [5, 5, 7]; +var g_eyePosition = [1, 5, 23]; + +var g_primitives = []; +var g_manager; + +/** + * Creates the client area. + */ +function initClient() { + window.g_finished = false; // for selenium testing. + + // Runs the sample in V8. Comment out this line to run it in the browser + // JavaScript engine, for example if you want to debug it. + // TODO(kbr): we need to investigate why turning this on is + // significantly slower than leaving it disabled. + // o3djs.util.setMainEngine(o3djs.util.Engine.V8); + + o3djs.util.makeClients(main); +} + +/** + * Initializes global variables, positions camera, draws shapes. + * @param {Array} clientElements Array of o3d object elements. + */ +function main(clientElements) { + var o3dElement = clientElements[0]; + + // Init global variables. + initGlobals(clientElements); + + // Set up the view and projection transformations. + initContext(); + + // Add the shapes to the transform heirarchy. + createShapes(); + + // Add the manipulators to the transform hierarchy. + createManipulators(); + + // Start picking; it won't do anything until the scene finishes loading. + o3djs.event.addEventListener(o3dElement, 'mousedown', onMouseDown); + o3djs.event.addEventListener(o3dElement, 'mousemove', onMouseMove); + o3djs.event.addEventListener(o3dElement, 'mouseup', onMouseUp); + + window.g_finished = true; // for selenium testing. +} + +function onMouseDown(e) { + g_manager.mousedown(e.x, e.y, + g_viewInfo.drawContext.view, + g_viewInfo.drawContext.projection, + g_client.width, + g_client.height); +} + +function onMouseMove(e) { + g_manager.mousemove(e.x, e.y, + g_viewInfo.drawContext.view, + g_viewInfo.drawContext.projection, + g_client.width, + g_client.height); + g_manager.updateInactiveManipulators(); +} + +function onMouseUp(e) { + g_manager.mouseup(); + g_manager.updateInactiveManipulators(); +} + +/** + * Initializes global variables and libraries. + */ +function initGlobals(clientElements) { + g_o3dElement = clientElements[0]; + window.g_client = g_client = g_o3dElement.client; + g_o3d = g_o3dElement.o3d; + g_math = o3djs.math; + + // Create a pack to manage the objects created. + g_pack = g_client.createPack(); + + // Create the render graph for a view. + g_viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); +} + +/** + * Sets up reasonable view and projection matrices. + */ +function initContext() { + // Set up a perspective transformation for the projection. + g_viewInfo.drawContext.projection = g_math.matrix4.perspective( + g_math.degToRad(30), // 30 degree frustum. + g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio. + 1, // Near plane. + 5000); // Far plane. + + // Set up our view transformation to look towards the world origin where the + // primitives are located. + g_viewInfo.drawContext.view = g_math.matrix4.lookAt( + g_eyePosition, // eye + [0, 2, 0], // target + [0, 1, 0]); // up +} + +/** + * Creates shapes using the primitives utility library, and adds them to the + * transform graph at the root node. + */ +function createShapes() { + // Create a little tree-like hierarchy of cubes + createCubeTree(2, 1.5, [0, 0, 0], g_client.root); +} + +/** + * Creates a small tree of cubes to demonstrate interaction with a + * hierarchy of shapes. + */ +function createCubeTree(depth, edgeLength, translation, parent) { + var cur = createCube(edgeLength, translation, parent); + if (depth > 0) { + createCubeTree(depth - 1, + edgeLength / 1.5, + o3djs.math.addVector(translation, + [-1.2 * edgeLength, + 1.0 * edgeLength, + 0]), + cur); + createCubeTree(depth - 1, + edgeLength / 1.5, + o3djs.math.addVector(translation, + [1.2 * edgeLength, + 1.0 * edgeLength, + 0]), + cur); + } + return cur; +} + +/** + * Creates a cube shape using the primitives utility library, with an + * optional translation and parent. Returns the newly-created + * transform for the cube. + */ +function createCube(edgeLength, opt_translation, opt_parent) { + var cube = o3djs.primitives.createCube( + g_pack, + // A green phong-shaded material. + o3djs.material.createBasicMaterial(g_pack, + g_viewInfo, + [0, 1, 0, 1]), + edgeLength); + var transform = g_pack.createObject('Transform'); + transform.addShape(cube); + if (opt_translation) { + transform.translate(opt_translation); + } + if (opt_parent) { + transform.parent = opt_parent; + } else { + transform.parent = g_client.root; + } + g_primitives.push(transform); + return transform; +} + +/** + * Creates manipulators attached to the objects we've just created. + */ +function createManipulators() { + g_manager = o3djs.manipulators.createManager(g_pack, + g_viewInfo.performanceDrawList, + g_client.root, + null, + 0); + var jj = 2; + for (var ii = 0; ii < g_primitives.length; ii++) { + var manip = g_manager.createTranslate1(); + manip.attachTo(g_primitives[ii]); + manip.setOffsetTranslation([0, -1.5, 0]); + // Demonstrate that we can drag along arbitrary directions. + if ((++jj % 4) == 0) { + manip.setOffsetRotation(o3djs.quaternions.rotationY(Math.PI / 4)); + } + } +} + +/** + * Removes any callbacks so they don't get called after the page has unloaded. + */ +function unload() { + if (g_client) { + g_client.cleanup(); + } +} +</script> +</head> +<body onload="initClient()" onunload="unload()"> +<h1>Translate1 Manipulator Sample</h1> +This example shows how to move objects around the scene using the +Translate1 manipulator. +<br/> +<!-- Start of O3D plugin --> +<div id="o3d" style="width: 600px; height: 600px;"></div> +<!-- End of O3D plugin --> +</body> +</html> diff --git a/o3d/samples/o3djs/manipulators.js b/o3d/samples/o3djs/manipulators.js index e1ddce6..cade671 100644 --- a/o3d/samples/o3djs/manipulators.js +++ b/o3d/samples/o3djs/manipulators.js @@ -1,962 +1,962 @@ -/* - * Copyright 2009, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "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 THE COPYRIGHT - * OWNER OR CONTRIBUTORS 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. - */ - - -/** - * @fileoverview This file contains classes that implement several - * forms of 2D and 3D manipulation. - */ - -o3djs.provide('o3djs.manipulators'); - -o3djs.require('o3djs.material'); - -o3djs.require('o3djs.math'); - -o3djs.require('o3djs.picking'); - -o3djs.require('o3djs.primitives'); - -o3djs.require('o3djs.quaternions'); - -/** - * A module implementing several forms of 2D and 3D manipulation. - * @namespace - */ -o3djs.manipulators = o3djs.manipulators || {}; - -/** - * Creates a new manipulator manager, which maintains multiple - * manipulators in the same scene. The manager is implicitly - * associated with a particular O3D client via the Pack which is - * passed in, although multiple managers can be created for a given - * client. The manipulators are positioned in world coordinates and - * are placed in the scene graph underneath the parent transform which - * is passed in. - * @param {!o3d.Pack} pack Pack in which manipulators' geometry and - * materials will be created. - * @param {!o3d.DrawList} drawList The draw list against which - * internal materials are created. - * @param {!o3d.Transform} parentTransform The parent transform under - * which the manipulators' geometry should be parented. - * @param {!o3d.RenderNode} parentRenderNode The parent render node - * under which the manipulators' draw elements should be placed. - * @param {number} renderNodePriority The priority that the - * manipulators' geometry should use for rendering. - * @return {!o3djs.manipulators.Manager} The created manipulator - * manager. - */ -o3djs.manipulators.createManager = function(pack, - drawList, - parentTransform, - parentRenderNode, - renderNodePriority) { - return new o3djs.manipulators.Manager(pack, - drawList, - parentTransform, - parentRenderNode, - renderNodePriority); -} - -// -// Some linear algebra classes. -// TODO(kbr): find a better home for these. -// - -/** - * Creates a new Line object, which implements projection and - * closest-point operations. - * @constructor - * @private - * @param {o3djs.math.Vector3} opt_direction The direction of the - * line. Does not need to be normalized but must not be the zero - * vector. Defaults to [1, 0, 0] if not specified. - * @param {o3djs.math.Vector3} opt_point A point through which the - * line goes. Defaults to [0, 0, 0] if not specified. - */ -o3djs.manipulators.Line_ = function(opt_direction, - opt_point) { - if (opt_direction) { - this.direction_ = o3djs.math.copyVector(opt_direction); - } else { - this.direction_ = [1, 0, 0]; - } - - if (opt_point) { - this.point_ = o3djs.math.copyVector(opt_point); - } else { - this.point_ = [0, 0, 0]; - } - - // Helper for computing projections along the line - this.alongVec_ = [0, 0, 0]; - this.recalc_(); -} - -/** - * Sets the direction of this line. - * @private - * @param {!o3djs.math.Vector3} direction The new direction of the - * line. Does not need to be normalized but must not be the zero - * vector. - */ -o3djs.manipulators.Line_.prototype.setDirection = function(direction) { - this.direction_ = o3djs.math.copyVector(direction); - this.recalc_(); -} - -/** - * Gets the direction of this line. - * @private - * @return {!o3djs.math.Vector3} The direction of the line. - */ -o3djs.manipulators.Line_.prototype.getDirection = function() { - return this.direction_; -} - -/** - * Sets one point through which this line travels. - * @private - * @param {!o3djs.math.Vector3} point A point which through the line - * will travel. - */ -o3djs.manipulators.Line_.prototype.setPoint = function(point) { - this.point_ = o3djs.math.copyVector(point); - this.recalc_(); -} - -/** - * Gets one point through which this line travels. - * @private - * @return {!o3djs.math.Vector3} A point which through the line - * travels. - */ -o3djs.manipulators.Line_.prototype.getPoint = function() { - return this.point_; -} - -/** - * Projects a point onto the line. - * @private - * @param {!o3djs.math.Vector3} point Point to be projected. - * @return {!o3djs.math.Vector3} Point on the line closest to the - * passed point. - */ -o3djs.manipulators.Line_.prototype.projectPoint = function(point) { - var dotp = o3djs.math.dot(this.direction_, point); - return o3djs.math.addVector(this.alongVec_, - o3djs.math.mulScalarVector(dotp, - this.direction_)); -} - -o3djs.manipulators.EPSILON = 0.00001; -o3djs.manipulators.X_AXIS = [1, 0, 0]; - - -/** - * Returns the closest point on this line to the given ray, which is - * specified by start and end points. If the ray is parallel to the - * line, returns null. - * @private - * @param {!o3djs.math.Vector3} startPoint Start point of ray. - * @param {!o3djs.math.Vector3} endPoint End point of ray. - * @return {o3djs.math.Vector3} The closest point on the line to the - * ray, or null if the ray is parallel to the line. - */ -o3djs.manipulators.Line_.prototype.closestPointToRay = function(startPoint, - endPoint) { - // Consider a two-sided line and a one-sided ray, both in in 3D - // space, and assume they are not parallel. Their parametric - // formulation is: - // - // p1 = point + t * dir - // p2 = raystart + u * raydir - // - // Here t and u are scalar parameter values, and the other values - // are three-dimensional vectors. p1 and p2 are arbitrary points on - // the line and ray, respectively. - // - // At the points cp1 and cp2 on these two lines where the line and - // the ray are closest together, the line segment between cp1 and - // cp2 is perpendicular to both of the lines. - // - // We can therefore write the following equations: - // - // dot( dir, (cp2 - cp1)) = 0 - // dot(raydir, (cp2 - cp1)) = 0 - // - // Define t' and u' as the parameter values for cp1 and cp2, - // respectively. Expanding, these equations become - // - // dot( dir, ((raystart + u' * raydir) - (point + t' * dir))) = 0 - // dot(raydir, ((raystart + u' * raydir) - (point + t' * dir))) = 0 - // - // With some reshuffling, these can be expressed in vector/matrix - // form: - // - // [ dot( dir, raystart) - dot( dir, point) ] - // [ dot(raydir, raystart) - dot(raydir, point) ] + (continued) - // - // [ -dot( dir, dir) dot( dir, raydir) ] [ t' ] [0] - // [ -dot(raydir, dir) dot(raydir, raydir) ] * [ u' ] = [0] - // - // u' is the parameter for the world space ray being cast into the - // screen. We can deduce whether the starting point of the ray is - // actually the closest point to the infinite 3D line by whether the - // value of u' is less than zero. - var rayDirection = o3djs.math.subVector(endPoint, startPoint); - var ddrd = o3djs.math.dot(this.direction_, rayDirection); - var A = [[-o3djs.math.lengthSquared(this.direction_), ddrd], - [ddrd, -o3djs.math.lengthSquared(rayDirection)]]; - var det = o3djs.math.det2(A); - if (Math.abs(det) < o3djs.manipulators.EPSILON) { - return null; - } - var Ainv = o3djs.math.inverse2(A); - var b = [o3djs.math.dot(this.point_, this.direction_) - - o3djs.math.dot(startPoint, this.direction_), - o3djs.math.dot(startPoint, rayDirection) - - o3djs.math.dot(this.point_, rayDirection)]; - var x = o3djs.math.mulMatrixVector(Ainv, b); - if (x[1] < 0) { - // Means that start point is closest point to this line - return startPoint; - } else { - return o3djs.math.addVector(this.point_, - o3djs.math.mulScalarVector( - x[0], - this.direction_)); - } -} - -/** - * Performs internal recalculations when the parameters of the line change. - * @private - */ -o3djs.manipulators.Line_.prototype.recalc_ = function() { - var denom = o3djs.math.lengthSquared(this.direction_); - if (denom == 0.0) { - throw 'Line_.recalc: ERROR: direction was the zero vector (not allowed)'; - } - this.alongVec_ = - o3djs.math.subVector(this.point_, - o3djs.math.mulScalarVector( - o3djs.math.dot(this.point_, - this.direction_), - this.direction_)); -} - -o3djs.manipulators.DEFAULT_COLOR = [0.8, 0.8, 0.8, 1.0]; -o3djs.manipulators.HIGHLIGHTED_COLOR = [0.9, 0.9, 0.0, 1.0]; - -/** - * Constructs a new manipulator manager. Do not call this directly; - * use o3djs.manipulators.createManager instead. - * @constructor - * @param {!o3d.Pack} pack Pack in which manipulators' geometry and - * materials will be created. - * @param {!o3d.DrawList} drawList The draw list against which - * internal materials are created. - * @param {!o3d.Transform} parentTransform The parent transform under - * which the manipulators' geometry should be parented. - * @param {!o3d.RenderNode} parentRenderNode The parent render node - * under which the manipulators' draw elements should be placed. - * @param {number} renderNodePriority The priority that the - * manipulators' geometry should use for rendering. - */ -o3djs.manipulators.Manager = function(pack, - drawList, - parentTransform, - parentRenderNode, - renderNodePriority) { - this.pack = pack; - this.drawList = drawList; - this.parentTransform = parentTransform; - this.parentRenderNode = parentRenderNode; - this.renderNodePriority = renderNodePriority; - - this.lightPosition = [10, 10, 10]; - - // Create the default and highlighted materials. - this.defaultMaterial = - this.createPhongMaterial_(o3djs.manipulators.DEFAULT_COLOR); - this.highlightedMaterial = - this.createPhongMaterial_(o3djs.manipulators.HIGHLIGHTED_COLOR); - - // This is a map from the manip's parent Transform clientId to the manip. - this.manipsByClientId = []; - - // Presumably we need a TransformInfo for the parentTransform. - this.transformInfo = - o3djs.picking.createTransformInfo(this.parentTransform, null); - - /** - * The currently-highlighted manipulator. - * @type {o3djs.manipulators.Manip} - */ - this.highlightedManip = null; - - /** - * The manipulator currently being dragged. - * @private - * @type {o3djs.manipulators.Manip} - */ - this.draggedManip_ = null; -} - -/** - * Creates a phong material based on the given single color. - * @private - * @param {!o3djs.math.Vector4} baseColor A vector with 4 entries, the - * R,G,B, and A components of a color. - * @return {!o3d.Material} A phong material whose overall pigment is baseColor. - */ -o3djs.manipulators.Manager.prototype.createPhongMaterial_ = - function(baseColor) { - // Create a new, empty Material object. - var material = this.pack.createObject('Material'); - - o3djs.effect.attachStandardShader( - this.pack, material, this.lightPosition, 'phong'); - - material.drawList = this.drawList; - - // Assign parameters to the phong material. - material.getParam('emissive').value = [0, 0, 0, 1]; - material.getParam('ambient').value = - o3djs.math.mulScalarVector(0.1, baseColor); - material.getParam('diffuse').value = baseColor; - material.getParam('specular').value = [.2, .2, .2, 1]; - material.getParam('shininess').value = 20; - - return material; -} - -/** - * Creates a new Translate1 manipulator. A Translate1 moves along the - * X axis in its local coordinate system. - * @return {!o3djs.manipulators.Translate1} A new Translate1 manipulator. - */ -o3djs.manipulators.Manager.prototype.createTranslate1 = function() { - var manip = new o3djs.manipulators.Translate1(this); - this.add_(manip); - return manip; -} - -/** - * Adds a manipulator to this manager's set. - * @private - * @param {!o3djs.manipulators.Manip} manip The manipulator to add. - */ -o3djs.manipulators.Manager.prototype.add_ = function(manip) { - // Generate draw elements for the manipulator's transform - manip.getTransform().createDrawElements(this.pack, null); - // Add the manipulator's transform to the parent transform - manip.getBaseTransform_().parent = this.parentTransform; - // Add the manipulator into our managed list - this.manipsByClientId[manip.getTransform().clientId] = manip; -} - -/** - * Event handler for multiple kinds of mouse events. - * @private - * @param {number} x The x coordinate of the mouse event. - * @param {number} y The y coordinate of the mouse event. - * @param {!o3djs.math.Matrix4} view The current view matrix. - * @param {!o3djs.math.Matrix4} projection The current projection matrix. - * @param {number} width The width of the viewport. - * @param {number} height The height of the viewport. - * @param {!function(!o3djs.manipulators.Manager, - * o3djs.picking.PickInfo, o3djs.manipulators.Manip): void} func - * Callback function. Always receives the manager as argument; if - * a manipulator was picked, receives non-null PickInfo and Manip - * arguments, otherwise receives null for both of these arguments. - */ -o3djs.manipulators.Manager.prototype.handleMouse_ = function(x, - y, - view, - projection, - width, - height, - func) { - this.transformInfo.update(); - - // Create the world ray - var worldRay = - o3djs.picking.clientPositionToWorldRayEx(x, y, - view, projection, - width, height); - - // Pick against all of the manipulators' geometry - var pickResult = this.transformInfo.pick(worldRay); - if (pickResult != null) { - // Find which manipulator we picked. - // NOTE this assumes some things about the transform graph - // structure of the manipulators. - var manip = - this.manipsByClientId[pickResult.shapeInfo.parent.transform.clientId]; - func(this, pickResult, manip); - } else { - func(this, null, null); - } -} - -/** - * Callback handling the mouse-down event on a manipulator. - * @private - * @param {!o3djs.manipulators.Manager} manager The manipulator - * manager owning the given manipulator. - * @param {o3djs.picking.PickInfo} pickResult The picking information - * associated with the mouse-down event. - * @param {o3djs.manipulators.Manip} manip The manipulator to be - * selected. - */ -o3djs.manipulators.mouseDownCallback_ = function(manager, - pickResult, - manip) { - if (manip != null) { - manager.draggedManip_ = manip; - manip.makeActive(pickResult); - } -} - -/** - * Callback handling the mouse-over event on a manipulator. - * @private - * @param {!o3djs.manipulators.Manager} manager The manipulator - * manager owning the given manipulator. - * @param {o3djs.picking.PickInfo} pickResult The picking information - * associated with the mouse-over event. - * @param {o3djs.manipulators.Manip} manip The manipulator to be - * highlighted. - */ -o3djs.manipulators.hoverCallback_ = function(manager, - pickResult, - manip) { - if (manager.highlightedManip != null && - manager.highlightedManip != manip) { - // Un-highlight the previously highlighted manipulator - manager.highlightedManip.clearHighlight(); - manager.highlightedManip = null; - } - - if (manip != null) { - manip.highlight(pickResult); - manager.highlightedManip = manip; - } -} - -/** - * Method which should be called by end user code upon receiving a - * mouse-down event. - * @param {number} x The x coordinate of the mouse event. - * @param {number} y The y coordinate of the mouse event. - * @param {!o3djs.math.Matrix4} view The current view matrix. - * @param {!o3djs.math.Matrix4} projection The current projection matrix. - * @param {number} width The width of the viewport. - * @param {number} height The height of the viewport. - */ -o3djs.manipulators.Manager.prototype.mousedown = function(x, - y, - view, - projection, - width, - height) { - this.handleMouse_(x, y, view, projection, width, height, - o3djs.manipulators.mouseDownCallback_); -} - -/** - * Method which should be called by end user code upon receiving a - * mouse motion event. - * @param {number} x The x coordinate of the mouse event. - * @param {number} y The y coordinate of the mouse event. - * @param {!o3djs.math.Matrix4} view The current view matrix. - * @param {!o3djs.math.Matrix4} projection The current projection matrix. - * @param {number} width The width of the viewport. - * @param {number} height The height of the viewport. - */ -o3djs.manipulators.Manager.prototype.mousemove = function(x, - y, - view, - projection, - width, - height) { - if (this.draggedManip_ != null) { - var worldRay = - o3djs.picking.clientPositionToWorldRayEx(x, y, - view, projection, - width, height); - this.draggedManip_.drag(worldRay.near, worldRay.far); - } else { - this.handleMouse_(x, y, view, projection, width, height, - o3djs.manipulators.hoverCallback_); - } -} - -/** - * Method which should be called by end user code upon receiving a - * mouse-up event. - */ -o3djs.manipulators.Manager.prototype.mouseup = function() { - if (this.draggedManip_ != null) { - this.draggedManip_.makeInactive(); - this.draggedManip_ = null; - } -} - -/** - * Method which should be called by end user code, typically in - * response to mouse move events, to update the transforms of - * manipulators which might have been moved either because of - * manipulators further up the hierarchy, or programmatic changes to - * transforms. - */ -o3djs.manipulators.Manager.prototype.updateInactiveManipulators = function() { - for (var ii in this.manipsByClientId) { - var manip = this.manipsByClientId[ii]; - if (!manip.isActive()) { - manip.updateBaseTransformFromAttachedTransform_(); - } - } -} - -/** - * Base class for all manipulators. - * @constructor - * @param {!o3djs.manipulators.Manager} manager The manager of this - * manipulator. - */ -o3djs.manipulators.Manip = function(manager) { - this.manager_ = manager; - var pack = manager.pack; - // This transform holds the local transformation of the manipulator, - // which is either applied to the transform to which it is attached, - // or (see below) consumed by the user in the manipulator's - // callbacks. After each interaction, if there is an attached - // transform, this local transform is added in to it and reset to - // the identity. - // TODO(kbr): add support for user callbacks on manipulators. - this.localTransform_ = pack.createObject('Transform'); - - // This transform provides an offset, if desired, between the - // manipulator's geometry and the transform (and, implicitly, the - // shape) to which it is attached. This allows the manipulator to be - // easily placed below an object, for example. - this.offsetTransform_ = pack.createObject('Transform'); - - // This transform is the one which is actually parented to the - // manager's parentTransform. It is used to place the manipulator in - // world space, regardless of the world space location of the - // parentTransform supplied to the manager. If this manipulator is - // attached to a given transform, then upon completion of a - // particular drag interaction, this transform is adjusted to take - // into account the attached transform's new value. - this.baseTransform_ = pack.createObject('Transform'); - - // Hook up these transforms - this.localTransform_.parent = this.offsetTransform_; - this.offsetTransform_.parent = this.baseTransform_; - - // This is the transform in the scene graph to which this - // manipulator is conceptually "attached", and whose local transform - // we are modifying. - this.attachedTransform_ = null; - - this.active_ = false; -} - -/** - * Adds shapes to the internal transform of this manipulator. - * @private - * @param {!Array.<!o3d.Shape>} shapes Array of shapes to add. - */ -o3djs.manipulators.Manip.prototype.addShapes_ = function(shapes) { - for (var ii = 0; ii < shapes.length; ii++) { - this.localTransform_.addShape(shapes[ii]); - } -} - -/** - * Returns the "base" transform of this manipulator, which places the - * origin of the manipulator at the local origin of the attached - * transform. - * @private - * @return {!o3d.Transform} The base transform of this manipulator. - */ -o3djs.manipulators.Manip.prototype.getBaseTransform_ = function() { - return this.baseTransform_; -} - -/** - * Returns the "offset" transform of this manipulator, which allows - * the manipulator's geometry to be moved or rotated with respect to - * the local origin of the attached transform. - * @return {!o3d.Transform} The offset transform of this manipulator. - */ -o3djs.manipulators.Manip.prototype.getOffsetTransform = function() { - return this.offsetTransform_; -} - -/** - * Returns the local transform of this manipulator, which contains the - * changes that have been made in response to the current drag - * operation. Upon completion of the drag, this transform's effects - * are composed in to the attached transform, and this transform is - * reset to the identity. - * @return {!o3d.Transform} The local transform of this manipulator. - */ -o3djs.manipulators.Manip.prototype.getTransform = function() { - return this.localTransform_; -} - -/** - * Sets the translation component of the offset transform. This is - * useful for moving the manipulator's geometry with respect to the - * local origin of the attached transform. - * @param {!o3djs.math.Vector3} translation The offset translation for - * this manipulator. - */ -o3djs.manipulators.Manip.prototype.setOffsetTranslation = - function(translation) { - this.getOffsetTransform().localMatrix = - o3djs.math.matrix4.setTranslation(this.getOffsetTransform().localMatrix, - translation); - -} - -/** - * Sets the rotation component of the offset transform. This is useful - * for orienting the manipulator's geometry with respect to the local - * origin of the attached transform. - * @param {!o3djs.quaternions.Quaternion} quaternion The offset - * rotation for this manipulator. - */ -o3djs.manipulators.Manip.prototype.setOffsetRotation = function(quaternion) { - var rot = o3djs.quaternions.quaternionToRotation(quaternion); - this.getOffsetTransform().localMatrix = - o3djs.math.matrix4.setUpper3x3(this.getOffsetTransform().localMatrix, - rot); - -} - -/** - * Explicitly sets the local translation of this manipulator. - * (TODO(kbr): it is not clear that this capability should be in the - * API.) - * @param {!o3djs.math.Vector3} translation The local translation for - * this manipulator. - */ -o3djs.manipulators.Manip.prototype.setTranslation = function(translation) { - this.getTransform().localMatrix = - o3djs.math.matrix4.setTranslation(this.getTransform().localMatrix, - translation); - -} - -/** - * Explicitly sets the local rotation of this manipulator. (TODO(kbr): - * it is not clear that this capability should be in the API.) - * @param {!o3djs.quaternions.Quaternion} quaternion The local - * rotation for this manipulator. - */ -o3djs.manipulators.Manip.prototype.setRotation = function(quaternion) { - var rot = o3djs.quaternions.quaternionToRotation(quaternion); - this.getTransform().localMatrix = - o3djs.math.matrix4.setUpper3x3(this.getTransform().localMatrix, - rot); - -} - -/** - * Attaches this manipulator to the given transform. Interactions with - * the manipulator will cause this transform's local matrix to be - * modified appropriately. - * @param {!o3d.Transform} transform The transform to which this - * manipulator should be attached. - */ -o3djs.manipulators.Manip.prototype.attachTo = function(transform) { - this.attachedTransform_ = transform; - // Update our base transform to place the manipulator at exactly the - // location of the attached transform. - this.updateBaseTransformFromAttachedTransform_(); -} - -/** - * Highlights this manipulator according to the given pick result. - * @param {o3djs.picking.PickInfo} pickResult The pick result which - * caused this manipulator to become highlighted. - */ -o3djs.manipulators.Manip.prototype.highlight = function(pickResult) { -} - -/** - * Clears any highlight for this manipulator. - */ -o3djs.manipulators.Manip.prototype.clearHighlight = function() { -} - -/** - * Activates this manipulator according to the given pick result. In - * complex manipulators, picking different portions of the manipulator - * may result in different forms of interaction. - * @param {o3djs.picking.PickInfo} pickResult The pick result which - * caused this manipulator to become active. - */ -o3djs.manipulators.Manip.prototype.makeActive = function(pickResult) { - this.active_ = true; -} - -/** - * Deactivates this manipulator. - */ -o3djs.manipulators.Manip.prototype.makeInactive = function() { - this.active_ = false; -} - -/** - * Drags this manipulator according to the world-space ray specified - * by startPoint and endPoint. makeActive must already have been - * called with the initial pick result causing this manipulator to - * become active. This method exists only to be overridden. - * @param {!o3djs.math.Vector3} startPoint Start point of the - * world-space ray through the current mouse position. - * @param {!o3djs.math.Vector3} endPoint End point of the world-space - * ray through the current mouse position. - */ -o3djs.manipulators.Manip.prototype.drag = function(startPoint, - endPoint) { -} - -/** - * Indicates whether this manipulator is active. - * @return {boolean} Whether this manipulator is active. - */ -o3djs.manipulators.Manip.prototype.isActive = function() { - return this.active_; -} - -/** - * Updates the base transform of this manipulator from the state of - * its attached transform, resetting the local transform of this - * manipulator to the identity. - * @private - */ -o3djs.manipulators.Manip.prototype.updateBaseTransformFromAttachedTransform_ = - function() { - if (this.attachedTransform_ != null) { - var attWorld = this.attachedTransform_.worldMatrix; - var parWorld = this.manager_.parentTransform.worldMatrix; - var parWorldInv = o3djs.math.matrix4.inverse(parWorld); - this.baseTransform_.localMatrix = - o3djs.math.matrix4.mul(attWorld, parWorldInv); - // Reset the manipulator's local matrix to the identity. - this.localTransform_.localMatrix = o3djs.math.matrix4.identity(); - } -} - -/** - * Updates this manipulator's attached transform based on the values - * in the local transform. - * @private - */ -o3djs.manipulators.Manip.prototype.updateAttachedTransformFromLocalTransform_ = - function() { - if (this.attachedTransform_ != null) { - // Compute the composition of the base and local transforms. - // The offset transform is skipped except for transforming the - // local matrix's translation by the rotation component of the - // offset transform. - var base = this.baseTransform_.worldMatrix; - var local = this.localTransform_.localMatrix; - var xlate = o3djs.math.matrix4.getTranslation(local); - var transformedXlate = - o3djs.math.matrix4.transformDirection( - this.offsetTransform_.localMatrix, - o3djs.math.matrix4.getTranslation(local)); - o3djs.math.matrix4.setTranslation(local, transformedXlate); - var totalMat = o3djs.math.matrix4.mul(base, local); - - // Set this into the attached transform, taking into account its - // parent's transform, if any. - // Note that we can not query the parent's transform directly, so - // we compute it using a little trick. - var attWorld = this.attachedTransform_.worldMatrix; - var attLocal = this.attachedTransform_.localMatrix; - var attParentMat = - o3djs.math.matrix4.mul(attWorld, - o3djs.math.matrix4.inverse(attLocal)); - // Now we can take the inverse of this matrix - var attParentMatInv = o3djs.math.matrix4.inverse(attParentMat); - totalMat = o3djs.math.matrix4.mul(attParentMatInv, totalMat); - this.attachedTransform_.localMatrix = totalMat; - } -} - -/** - * Sets the material of the given shape's draw elements. - * @private - */ -o3djs.manipulators.Manip.prototype.setMaterial_ = function(shape, material) { - var elements = shape.elements; - for (var ii = 0; ii < elements.length; ii++) { - var drawElements = elements[ii].drawElements; - for (var jj = 0; jj < drawElements.length; jj++) { - drawElements[jj].material = material; - } - } -} - -/** - * Sets the materials of the given shapes' draw elements. - * @private - */ -o3djs.manipulators.Manip.prototype.setMaterials_ = function(shapes, material) { - for (var ii = 0; ii < shapes.length; ii++) { - this.setMaterial_(shapes[ii], material); - } -} - -/** - * A manipulator allowing an object to be dragged along a line. - * @constructor - * @extends {o3djs.manipulators.Manip} - * @param {!o3djs.manipulators.Manager} manager The manager for the - * new Translate1 manipulator. - */ -o3djs.manipulators.Translate1 = function(manager) { - o3djs.manipulators.Manip.call(this, manager); - - var pack = manager.pack; - var material = manager.defaultMaterial; - - var shape = manager.translate1Shape_; - if (!shape) { - // Create the geometry for the manipulator, which looks like a - // two-way arrow going from (-1, 0, 0) to (1, 0, 0). - var matrix4 = o3djs.math.matrix4; - var zRot = matrix4.rotationZ(Math.PI / 2); - - var verts = o3djs.primitives.createTruncatedConeVertices( - 0.15, // Bottom radius. - 0.0, // Top radius. - 0.3, // Height. - 4, // Number of radial subdivisions. - 1, // Number of vertical subdivisions. - matrix4.mul(matrix4.translation([0, 0.85, 0]), zRot)); - - verts.append(o3djs.primitives.createCylinderVertices( - 0.06, // Radius. - 1.4, // Height. - 4, // Number of radial subdivisions. - 1, // Number of vertical subdivisions. - zRot)); - - verts.append(o3djs.primitives.createTruncatedConeVertices( - 0.0, // Bottom radius. - 0.15, // Top radius. - 0.3, // Height. - 4, // Number of radial subdivisions. - 1, // Number of vertical subdivisions. - matrix4.mul(matrix4.translation([0, -0.85, 0]), zRot))); - - shape = verts.createShape(pack, material); - manager.translate1Shape_ = shape; - } - - this.addShapes_([ shape ]); - - this.transformInfo = o3djs.picking.createTransformInfo(this.getTransform(), - manager.transformInfo); - - // Add a parameter to our transform to be able to change the - // material's color for highlighting. - this.colorParam = this.getTransform().createParam('diffuse', 'ParamFloat4'); - this.clearHighlight(); - - /** Dragging state */ - this.dragLine = new o3djs.manipulators.Line_(); -} - -o3djs.base.inherit(o3djs.manipulators.Translate1, o3djs.manipulators.Manip); - -o3djs.manipulators.Translate1.prototype.highlight = function(pickResult) { - // We can use instanced geometry for the entire Translate1 since its - // entire color changes during highlighting. - // TODO(kbr): support custom user geometry and associated callbacks. - this.colorParam.value = o3djs.manipulators.HIGHLIGHTED_COLOR; -} - -o3djs.manipulators.Translate1.prototype.clearHighlight = function() { - this.colorParam.value = o3djs.manipulators.DEFAULT_COLOR; -} - -o3djs.manipulators.Translate1.prototype.makeActive = function(pickResult) { - o3djs.manipulators.Manip.prototype.makeActive.call(this, pickResult); - this.highlight(pickResult); - var worldMatrix = this.getTransform().worldMatrix; - this.dragLine.setDirection( - o3djs.math.matrix4.transformDirection(worldMatrix, - o3djs.manipulators.X_AXIS)); - this.dragLine.setPoint(pickResult.worldIntersectionPosition); -} - -o3djs.manipulators.Translate1.prototype.makeInactive = function() { - o3djs.manipulators.Manip.prototype.makeInactive.call(this); - this.clearHighlight(); - this.updateAttachedTransformFromLocalTransform_(); - this.updateBaseTransformFromAttachedTransform_(); -} - -o3djs.manipulators.Translate1.prototype.drag = function(startPoint, - endPoint) { - // Algorithm: Find closest point of ray to dragLine. Subtract this - // point from the line's point to find difference vector; transform - // from world to local coordinates to find new local offset of - // manipulator. - var closestPoint = this.dragLine.closestPointToRay(startPoint, endPoint); - if (closestPoint == null) { - // Drag axis is parallel to ray. Punt. - return; - } - // Need to do a world-to-local transformation on the difference vector. - // Note that we also incorporate the translation portion of the matrix. - var diffVector = - o3djs.math.subVector(closestPoint, this.dragLine.getPoint()); - var worldToLocal = - o3djs.math.matrix4.inverse(this.getTransform().worldMatrix); - this.getTransform().localMatrix = - o3djs.math.matrix4.setTranslation( - this.getTransform().localMatrix, - o3djs.math.matrix4.transformDirection(worldToLocal, - diffVector)); - this.updateAttachedTransformFromLocalTransform_(); -} +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + + +/** + * @fileoverview This file contains classes that implement several + * forms of 2D and 3D manipulation. + */ + +o3djs.provide('o3djs.manipulators'); + +o3djs.require('o3djs.material'); + +o3djs.require('o3djs.math'); + +o3djs.require('o3djs.picking'); + +o3djs.require('o3djs.primitives'); + +o3djs.require('o3djs.quaternions'); + +/** + * A module implementing several forms of 2D and 3D manipulation. + * @namespace + */ +o3djs.manipulators = o3djs.manipulators || {}; + +/** + * Creates a new manipulator manager, which maintains multiple + * manipulators in the same scene. The manager is implicitly + * associated with a particular O3D client via the Pack which is + * passed in, although multiple managers can be created for a given + * client. The manipulators are positioned in world coordinates and + * are placed in the scene graph underneath the parent transform which + * is passed in. + * @param {!o3d.Pack} pack Pack in which manipulators' geometry and + * materials will be created. + * @param {!o3d.DrawList} drawList The draw list against which + * internal materials are created. + * @param {!o3d.Transform} parentTransform The parent transform under + * which the manipulators' geometry should be parented. + * @param {!o3d.RenderNode} parentRenderNode The parent render node + * under which the manipulators' draw elements should be placed. + * @param {number} renderNodePriority The priority that the + * manipulators' geometry should use for rendering. + * @return {!o3djs.manipulators.Manager} The created manipulator + * manager. + */ +o3djs.manipulators.createManager = function(pack, + drawList, + parentTransform, + parentRenderNode, + renderNodePriority) { + return new o3djs.manipulators.Manager(pack, + drawList, + parentTransform, + parentRenderNode, + renderNodePriority); +} + +// +// Some linear algebra classes. +// TODO(kbr): find a better home for these. +// + +/** + * Creates a new Line object, which implements projection and + * closest-point operations. + * @constructor + * @private + * @param {o3djs.math.Vector3} opt_direction The direction of the + * line. Does not need to be normalized but must not be the zero + * vector. Defaults to [1, 0, 0] if not specified. + * @param {o3djs.math.Vector3} opt_point A point through which the + * line goes. Defaults to [0, 0, 0] if not specified. + */ +o3djs.manipulators.Line_ = function(opt_direction, + opt_point) { + if (opt_direction) { + this.direction_ = o3djs.math.copyVector(opt_direction); + } else { + this.direction_ = [1, 0, 0]; + } + + if (opt_point) { + this.point_ = o3djs.math.copyVector(opt_point); + } else { + this.point_ = [0, 0, 0]; + } + + // Helper for computing projections along the line + this.alongVec_ = [0, 0, 0]; + this.recalc_(); +} + +/** + * Sets the direction of this line. + * @private + * @param {!o3djs.math.Vector3} direction The new direction of the + * line. Does not need to be normalized but must not be the zero + * vector. + */ +o3djs.manipulators.Line_.prototype.setDirection = function(direction) { + this.direction_ = o3djs.math.copyVector(direction); + this.recalc_(); +} + +/** + * Gets the direction of this line. + * @private + * @return {!o3djs.math.Vector3} The direction of the line. + */ +o3djs.manipulators.Line_.prototype.getDirection = function() { + return this.direction_; +} + +/** + * Sets one point through which this line travels. + * @private + * @param {!o3djs.math.Vector3} point A point which through the line + * will travel. + */ +o3djs.manipulators.Line_.prototype.setPoint = function(point) { + this.point_ = o3djs.math.copyVector(point); + this.recalc_(); +} + +/** + * Gets one point through which this line travels. + * @private + * @return {!o3djs.math.Vector3} A point which through the line + * travels. + */ +o3djs.manipulators.Line_.prototype.getPoint = function() { + return this.point_; +} + +/** + * Projects a point onto the line. + * @private + * @param {!o3djs.math.Vector3} point Point to be projected. + * @return {!o3djs.math.Vector3} Point on the line closest to the + * passed point. + */ +o3djs.manipulators.Line_.prototype.projectPoint = function(point) { + var dotp = o3djs.math.dot(this.direction_, point); + return o3djs.math.addVector(this.alongVec_, + o3djs.math.mulScalarVector(dotp, + this.direction_)); +} + +o3djs.manipulators.EPSILON = 0.00001; +o3djs.manipulators.X_AXIS = [1, 0, 0]; + + +/** + * Returns the closest point on this line to the given ray, which is + * specified by start and end points. If the ray is parallel to the + * line, returns null. + * @private + * @param {!o3djs.math.Vector3} startPoint Start point of ray. + * @param {!o3djs.math.Vector3} endPoint End point of ray. + * @return {o3djs.math.Vector3} The closest point on the line to the + * ray, or null if the ray is parallel to the line. + */ +o3djs.manipulators.Line_.prototype.closestPointToRay = function(startPoint, + endPoint) { + // Consider a two-sided line and a one-sided ray, both in in 3D + // space, and assume they are not parallel. Their parametric + // formulation is: + // + // p1 = point + t * dir + // p2 = raystart + u * raydir + // + // Here t and u are scalar parameter values, and the other values + // are three-dimensional vectors. p1 and p2 are arbitrary points on + // the line and ray, respectively. + // + // At the points cp1 and cp2 on these two lines where the line and + // the ray are closest together, the line segment between cp1 and + // cp2 is perpendicular to both of the lines. + // + // We can therefore write the following equations: + // + // dot( dir, (cp2 - cp1)) = 0 + // dot(raydir, (cp2 - cp1)) = 0 + // + // Define t' and u' as the parameter values for cp1 and cp2, + // respectively. Expanding, these equations become + // + // dot( dir, ((raystart + u' * raydir) - (point + t' * dir))) = 0 + // dot(raydir, ((raystart + u' * raydir) - (point + t' * dir))) = 0 + // + // With some reshuffling, these can be expressed in vector/matrix + // form: + // + // [ dot( dir, raystart) - dot( dir, point) ] + // [ dot(raydir, raystart) - dot(raydir, point) ] + (continued) + // + // [ -dot( dir, dir) dot( dir, raydir) ] [ t' ] [0] + // [ -dot(raydir, dir) dot(raydir, raydir) ] * [ u' ] = [0] + // + // u' is the parameter for the world space ray being cast into the + // screen. We can deduce whether the starting point of the ray is + // actually the closest point to the infinite 3D line by whether the + // value of u' is less than zero. + var rayDirection = o3djs.math.subVector(endPoint, startPoint); + var ddrd = o3djs.math.dot(this.direction_, rayDirection); + var A = [[-o3djs.math.lengthSquared(this.direction_), ddrd], + [ddrd, -o3djs.math.lengthSquared(rayDirection)]]; + var det = o3djs.math.det2(A); + if (Math.abs(det) < o3djs.manipulators.EPSILON) { + return null; + } + var Ainv = o3djs.math.inverse2(A); + var b = [o3djs.math.dot(this.point_, this.direction_) - + o3djs.math.dot(startPoint, this.direction_), + o3djs.math.dot(startPoint, rayDirection) - + o3djs.math.dot(this.point_, rayDirection)]; + var x = o3djs.math.mulMatrixVector(Ainv, b); + if (x[1] < 0) { + // Means that start point is closest point to this line + return startPoint; + } else { + return o3djs.math.addVector(this.point_, + o3djs.math.mulScalarVector( + x[0], + this.direction_)); + } +} + +/** + * Performs internal recalculations when the parameters of the line change. + * @private + */ +o3djs.manipulators.Line_.prototype.recalc_ = function() { + var denom = o3djs.math.lengthSquared(this.direction_); + if (denom == 0.0) { + throw 'Line_.recalc: ERROR: direction was the zero vector (not allowed)'; + } + this.alongVec_ = + o3djs.math.subVector(this.point_, + o3djs.math.mulScalarVector( + o3djs.math.dot(this.point_, + this.direction_), + this.direction_)); +} + +o3djs.manipulators.DEFAULT_COLOR = [0.8, 0.8, 0.8, 1.0]; +o3djs.manipulators.HIGHLIGHTED_COLOR = [0.9, 0.9, 0.0, 1.0]; + +/** + * Constructs a new manipulator manager. Do not call this directly; + * use o3djs.manipulators.createManager instead. + * @constructor + * @param {!o3d.Pack} pack Pack in which manipulators' geometry and + * materials will be created. + * @param {!o3d.DrawList} drawList The draw list against which + * internal materials are created. + * @param {!o3d.Transform} parentTransform The parent transform under + * which the manipulators' geometry should be parented. + * @param {!o3d.RenderNode} parentRenderNode The parent render node + * under which the manipulators' draw elements should be placed. + * @param {number} renderNodePriority The priority that the + * manipulators' geometry should use for rendering. + */ +o3djs.manipulators.Manager = function(pack, + drawList, + parentTransform, + parentRenderNode, + renderNodePriority) { + this.pack = pack; + this.drawList = drawList; + this.parentTransform = parentTransform; + this.parentRenderNode = parentRenderNode; + this.renderNodePriority = renderNodePriority; + + this.lightPosition = [10, 10, 10]; + + // Create the default and highlighted materials. + this.defaultMaterial = + this.createPhongMaterial_(o3djs.manipulators.DEFAULT_COLOR); + this.highlightedMaterial = + this.createPhongMaterial_(o3djs.manipulators.HIGHLIGHTED_COLOR); + + // This is a map from the manip's parent Transform clientId to the manip. + this.manipsByClientId = []; + + // Presumably we need a TransformInfo for the parentTransform. + this.transformInfo = + o3djs.picking.createTransformInfo(this.parentTransform, null); + + /** + * The currently-highlighted manipulator. + * @type {o3djs.manipulators.Manip} + */ + this.highlightedManip = null; + + /** + * The manipulator currently being dragged. + * @private + * @type {o3djs.manipulators.Manip} + */ + this.draggedManip_ = null; +} + +/** + * Creates a phong material based on the given single color. + * @private + * @param {!o3djs.math.Vector4} baseColor A vector with 4 entries, the + * R,G,B, and A components of a color. + * @return {!o3d.Material} A phong material whose overall pigment is baseColor. + */ +o3djs.manipulators.Manager.prototype.createPhongMaterial_ = + function(baseColor) { + // Create a new, empty Material object. + var material = this.pack.createObject('Material'); + + o3djs.effect.attachStandardShader( + this.pack, material, this.lightPosition, 'phong'); + + material.drawList = this.drawList; + + // Assign parameters to the phong material. + material.getParam('emissive').value = [0, 0, 0, 1]; + material.getParam('ambient').value = + o3djs.math.mulScalarVector(0.1, baseColor); + material.getParam('diffuse').value = baseColor; + material.getParam('specular').value = [.2, .2, .2, 1]; + material.getParam('shininess').value = 20; + + return material; +} + +/** + * Creates a new Translate1 manipulator. A Translate1 moves along the + * X axis in its local coordinate system. + * @return {!o3djs.manipulators.Translate1} A new Translate1 manipulator. + */ +o3djs.manipulators.Manager.prototype.createTranslate1 = function() { + var manip = new o3djs.manipulators.Translate1(this); + this.add_(manip); + return manip; +} + +/** + * Adds a manipulator to this manager's set. + * @private + * @param {!o3djs.manipulators.Manip} manip The manipulator to add. + */ +o3djs.manipulators.Manager.prototype.add_ = function(manip) { + // Generate draw elements for the manipulator's transform + manip.getTransform().createDrawElements(this.pack, null); + // Add the manipulator's transform to the parent transform + manip.getBaseTransform_().parent = this.parentTransform; + // Add the manipulator into our managed list + this.manipsByClientId[manip.getTransform().clientId] = manip; +} + +/** + * Event handler for multiple kinds of mouse events. + * @private + * @param {number} x The x coordinate of the mouse event. + * @param {number} y The y coordinate of the mouse event. + * @param {!o3djs.math.Matrix4} view The current view matrix. + * @param {!o3djs.math.Matrix4} projection The current projection matrix. + * @param {number} width The width of the viewport. + * @param {number} height The height of the viewport. + * @param {!function(!o3djs.manipulators.Manager, + * o3djs.picking.PickInfo, o3djs.manipulators.Manip): void} func + * Callback function. Always receives the manager as argument; if + * a manipulator was picked, receives non-null PickInfo and Manip + * arguments, otherwise receives null for both of these arguments. + */ +o3djs.manipulators.Manager.prototype.handleMouse_ = function(x, + y, + view, + projection, + width, + height, + func) { + this.transformInfo.update(); + + // Create the world ray + var worldRay = + o3djs.picking.clientPositionToWorldRayEx(x, y, + view, projection, + width, height); + + // Pick against all of the manipulators' geometry + var pickResult = this.transformInfo.pick(worldRay); + if (pickResult != null) { + // Find which manipulator we picked. + // NOTE this assumes some things about the transform graph + // structure of the manipulators. + var manip = + this.manipsByClientId[pickResult.shapeInfo.parent.transform.clientId]; + func(this, pickResult, manip); + } else { + func(this, null, null); + } +} + +/** + * Callback handling the mouse-down event on a manipulator. + * @private + * @param {!o3djs.manipulators.Manager} manager The manipulator + * manager owning the given manipulator. + * @param {o3djs.picking.PickInfo} pickResult The picking information + * associated with the mouse-down event. + * @param {o3djs.manipulators.Manip} manip The manipulator to be + * selected. + */ +o3djs.manipulators.mouseDownCallback_ = function(manager, + pickResult, + manip) { + if (manip != null) { + manager.draggedManip_ = manip; + manip.makeActive(pickResult); + } +} + +/** + * Callback handling the mouse-over event on a manipulator. + * @private + * @param {!o3djs.manipulators.Manager} manager The manipulator + * manager owning the given manipulator. + * @param {o3djs.picking.PickInfo} pickResult The picking information + * associated with the mouse-over event. + * @param {o3djs.manipulators.Manip} manip The manipulator to be + * highlighted. + */ +o3djs.manipulators.hoverCallback_ = function(manager, + pickResult, + manip) { + if (manager.highlightedManip != null && + manager.highlightedManip != manip) { + // Un-highlight the previously highlighted manipulator + manager.highlightedManip.clearHighlight(); + manager.highlightedManip = null; + } + + if (manip != null) { + manip.highlight(pickResult); + manager.highlightedManip = manip; + } +} + +/** + * Method which should be called by end user code upon receiving a + * mouse-down event. + * @param {number} x The x coordinate of the mouse event. + * @param {number} y The y coordinate of the mouse event. + * @param {!o3djs.math.Matrix4} view The current view matrix. + * @param {!o3djs.math.Matrix4} projection The current projection matrix. + * @param {number} width The width of the viewport. + * @param {number} height The height of the viewport. + */ +o3djs.manipulators.Manager.prototype.mousedown = function(x, + y, + view, + projection, + width, + height) { + this.handleMouse_(x, y, view, projection, width, height, + o3djs.manipulators.mouseDownCallback_); +} + +/** + * Method which should be called by end user code upon receiving a + * mouse motion event. + * @param {number} x The x coordinate of the mouse event. + * @param {number} y The y coordinate of the mouse event. + * @param {!o3djs.math.Matrix4} view The current view matrix. + * @param {!o3djs.math.Matrix4} projection The current projection matrix. + * @param {number} width The width of the viewport. + * @param {number} height The height of the viewport. + */ +o3djs.manipulators.Manager.prototype.mousemove = function(x, + y, + view, + projection, + width, + height) { + if (this.draggedManip_ != null) { + var worldRay = + o3djs.picking.clientPositionToWorldRayEx(x, y, + view, projection, + width, height); + this.draggedManip_.drag(worldRay.near, worldRay.far); + } else { + this.handleMouse_(x, y, view, projection, width, height, + o3djs.manipulators.hoverCallback_); + } +} + +/** + * Method which should be called by end user code upon receiving a + * mouse-up event. + */ +o3djs.manipulators.Manager.prototype.mouseup = function() { + if (this.draggedManip_ != null) { + this.draggedManip_.makeInactive(); + this.draggedManip_ = null; + } +} + +/** + * Method which should be called by end user code, typically in + * response to mouse move events, to update the transforms of + * manipulators which might have been moved either because of + * manipulators further up the hierarchy, or programmatic changes to + * transforms. + */ +o3djs.manipulators.Manager.prototype.updateInactiveManipulators = function() { + for (var ii in this.manipsByClientId) { + var manip = this.manipsByClientId[ii]; + if (!manip.isActive()) { + manip.updateBaseTransformFromAttachedTransform_(); + } + } +} + +/** + * Base class for all manipulators. + * @constructor + * @param {!o3djs.manipulators.Manager} manager The manager of this + * manipulator. + */ +o3djs.manipulators.Manip = function(manager) { + this.manager_ = manager; + var pack = manager.pack; + // This transform holds the local transformation of the manipulator, + // which is either applied to the transform to which it is attached, + // or (see below) consumed by the user in the manipulator's + // callbacks. After each interaction, if there is an attached + // transform, this local transform is added in to it and reset to + // the identity. + // TODO(kbr): add support for user callbacks on manipulators. + this.localTransform_ = pack.createObject('Transform'); + + // This transform provides an offset, if desired, between the + // manipulator's geometry and the transform (and, implicitly, the + // shape) to which it is attached. This allows the manipulator to be + // easily placed below an object, for example. + this.offsetTransform_ = pack.createObject('Transform'); + + // This transform is the one which is actually parented to the + // manager's parentTransform. It is used to place the manipulator in + // world space, regardless of the world space location of the + // parentTransform supplied to the manager. If this manipulator is + // attached to a given transform, then upon completion of a + // particular drag interaction, this transform is adjusted to take + // into account the attached transform's new value. + this.baseTransform_ = pack.createObject('Transform'); + + // Hook up these transforms + this.localTransform_.parent = this.offsetTransform_; + this.offsetTransform_.parent = this.baseTransform_; + + // This is the transform in the scene graph to which this + // manipulator is conceptually "attached", and whose local transform + // we are modifying. + this.attachedTransform_ = null; + + this.active_ = false; +} + +/** + * Adds shapes to the internal transform of this manipulator. + * @private + * @param {!Array.<!o3d.Shape>} shapes Array of shapes to add. + */ +o3djs.manipulators.Manip.prototype.addShapes_ = function(shapes) { + for (var ii = 0; ii < shapes.length; ii++) { + this.localTransform_.addShape(shapes[ii]); + } +} + +/** + * Returns the "base" transform of this manipulator, which places the + * origin of the manipulator at the local origin of the attached + * transform. + * @private + * @return {!o3d.Transform} The base transform of this manipulator. + */ +o3djs.manipulators.Manip.prototype.getBaseTransform_ = function() { + return this.baseTransform_; +} + +/** + * Returns the "offset" transform of this manipulator, which allows + * the manipulator's geometry to be moved or rotated with respect to + * the local origin of the attached transform. + * @return {!o3d.Transform} The offset transform of this manipulator. + */ +o3djs.manipulators.Manip.prototype.getOffsetTransform = function() { + return this.offsetTransform_; +} + +/** + * Returns the local transform of this manipulator, which contains the + * changes that have been made in response to the current drag + * operation. Upon completion of the drag, this transform's effects + * are composed in to the attached transform, and this transform is + * reset to the identity. + * @return {!o3d.Transform} The local transform of this manipulator. + */ +o3djs.manipulators.Manip.prototype.getTransform = function() { + return this.localTransform_; +} + +/** + * Sets the translation component of the offset transform. This is + * useful for moving the manipulator's geometry with respect to the + * local origin of the attached transform. + * @param {!o3djs.math.Vector3} translation The offset translation for + * this manipulator. + */ +o3djs.manipulators.Manip.prototype.setOffsetTranslation = + function(translation) { + this.getOffsetTransform().localMatrix = + o3djs.math.matrix4.setTranslation(this.getOffsetTransform().localMatrix, + translation); + +} + +/** + * Sets the rotation component of the offset transform. This is useful + * for orienting the manipulator's geometry with respect to the local + * origin of the attached transform. + * @param {!o3djs.quaternions.Quaternion} quaternion The offset + * rotation for this manipulator. + */ +o3djs.manipulators.Manip.prototype.setOffsetRotation = function(quaternion) { + var rot = o3djs.quaternions.quaternionToRotation(quaternion); + this.getOffsetTransform().localMatrix = + o3djs.math.matrix4.setUpper3x3(this.getOffsetTransform().localMatrix, + rot); + +} + +/** + * Explicitly sets the local translation of this manipulator. + * (TODO(kbr): it is not clear that this capability should be in the + * API.) + * @param {!o3djs.math.Vector3} translation The local translation for + * this manipulator. + */ +o3djs.manipulators.Manip.prototype.setTranslation = function(translation) { + this.getTransform().localMatrix = + o3djs.math.matrix4.setTranslation(this.getTransform().localMatrix, + translation); + +} + +/** + * Explicitly sets the local rotation of this manipulator. (TODO(kbr): + * it is not clear that this capability should be in the API.) + * @param {!o3djs.quaternions.Quaternion} quaternion The local + * rotation for this manipulator. + */ +o3djs.manipulators.Manip.prototype.setRotation = function(quaternion) { + var rot = o3djs.quaternions.quaternionToRotation(quaternion); + this.getTransform().localMatrix = + o3djs.math.matrix4.setUpper3x3(this.getTransform().localMatrix, + rot); + +} + +/** + * Attaches this manipulator to the given transform. Interactions with + * the manipulator will cause this transform's local matrix to be + * modified appropriately. + * @param {!o3d.Transform} transform The transform to which this + * manipulator should be attached. + */ +o3djs.manipulators.Manip.prototype.attachTo = function(transform) { + this.attachedTransform_ = transform; + // Update our base transform to place the manipulator at exactly the + // location of the attached transform. + this.updateBaseTransformFromAttachedTransform_(); +} + +/** + * Highlights this manipulator according to the given pick result. + * @param {o3djs.picking.PickInfo} pickResult The pick result which + * caused this manipulator to become highlighted. + */ +o3djs.manipulators.Manip.prototype.highlight = function(pickResult) { +} + +/** + * Clears any highlight for this manipulator. + */ +o3djs.manipulators.Manip.prototype.clearHighlight = function() { +} + +/** + * Activates this manipulator according to the given pick result. In + * complex manipulators, picking different portions of the manipulator + * may result in different forms of interaction. + * @param {o3djs.picking.PickInfo} pickResult The pick result which + * caused this manipulator to become active. + */ +o3djs.manipulators.Manip.prototype.makeActive = function(pickResult) { + this.active_ = true; +} + +/** + * Deactivates this manipulator. + */ +o3djs.manipulators.Manip.prototype.makeInactive = function() { + this.active_ = false; +} + +/** + * Drags this manipulator according to the world-space ray specified + * by startPoint and endPoint. makeActive must already have been + * called with the initial pick result causing this manipulator to + * become active. This method exists only to be overridden. + * @param {!o3djs.math.Vector3} startPoint Start point of the + * world-space ray through the current mouse position. + * @param {!o3djs.math.Vector3} endPoint End point of the world-space + * ray through the current mouse position. + */ +o3djs.manipulators.Manip.prototype.drag = function(startPoint, + endPoint) { +} + +/** + * Indicates whether this manipulator is active. + * @return {boolean} Whether this manipulator is active. + */ +o3djs.manipulators.Manip.prototype.isActive = function() { + return this.active_; +} + +/** + * Updates the base transform of this manipulator from the state of + * its attached transform, resetting the local transform of this + * manipulator to the identity. + * @private + */ +o3djs.manipulators.Manip.prototype.updateBaseTransformFromAttachedTransform_ = + function() { + if (this.attachedTransform_ != null) { + var attWorld = this.attachedTransform_.worldMatrix; + var parWorld = this.manager_.parentTransform.worldMatrix; + var parWorldInv = o3djs.math.matrix4.inverse(parWorld); + this.baseTransform_.localMatrix = + o3djs.math.matrix4.mul(attWorld, parWorldInv); + // Reset the manipulator's local matrix to the identity. + this.localTransform_.localMatrix = o3djs.math.matrix4.identity(); + } +} + +/** + * Updates this manipulator's attached transform based on the values + * in the local transform. + * @private + */ +o3djs.manipulators.Manip.prototype.updateAttachedTransformFromLocalTransform_ = + function() { + if (this.attachedTransform_ != null) { + // Compute the composition of the base and local transforms. + // The offset transform is skipped except for transforming the + // local matrix's translation by the rotation component of the + // offset transform. + var base = this.baseTransform_.worldMatrix; + var local = this.localTransform_.localMatrix; + var xlate = o3djs.math.matrix4.getTranslation(local); + var transformedXlate = + o3djs.math.matrix4.transformDirection( + this.offsetTransform_.localMatrix, + o3djs.math.matrix4.getTranslation(local)); + o3djs.math.matrix4.setTranslation(local, transformedXlate); + var totalMat = o3djs.math.matrix4.mul(base, local); + + // Set this into the attached transform, taking into account its + // parent's transform, if any. + // Note that we can not query the parent's transform directly, so + // we compute it using a little trick. + var attWorld = this.attachedTransform_.worldMatrix; + var attLocal = this.attachedTransform_.localMatrix; + var attParentMat = + o3djs.math.matrix4.mul(attWorld, + o3djs.math.matrix4.inverse(attLocal)); + // Now we can take the inverse of this matrix + var attParentMatInv = o3djs.math.matrix4.inverse(attParentMat); + totalMat = o3djs.math.matrix4.mul(attParentMatInv, totalMat); + this.attachedTransform_.localMatrix = totalMat; + } +} + +/** + * Sets the material of the given shape's draw elements. + * @private + */ +o3djs.manipulators.Manip.prototype.setMaterial_ = function(shape, material) { + var elements = shape.elements; + for (var ii = 0; ii < elements.length; ii++) { + var drawElements = elements[ii].drawElements; + for (var jj = 0; jj < drawElements.length; jj++) { + drawElements[jj].material = material; + } + } +} + +/** + * Sets the materials of the given shapes' draw elements. + * @private + */ +o3djs.manipulators.Manip.prototype.setMaterials_ = function(shapes, material) { + for (var ii = 0; ii < shapes.length; ii++) { + this.setMaterial_(shapes[ii], material); + } +} + +/** + * A manipulator allowing an object to be dragged along a line. + * @constructor + * @extends {o3djs.manipulators.Manip} + * @param {!o3djs.manipulators.Manager} manager The manager for the + * new Translate1 manipulator. + */ +o3djs.manipulators.Translate1 = function(manager) { + o3djs.manipulators.Manip.call(this, manager); + + var pack = manager.pack; + var material = manager.defaultMaterial; + + var shape = manager.translate1Shape_; + if (!shape) { + // Create the geometry for the manipulator, which looks like a + // two-way arrow going from (-1, 0, 0) to (1, 0, 0). + var matrix4 = o3djs.math.matrix4; + var zRot = matrix4.rotationZ(Math.PI / 2); + + var verts = o3djs.primitives.createTruncatedConeVertices( + 0.15, // Bottom radius. + 0.0, // Top radius. + 0.3, // Height. + 4, // Number of radial subdivisions. + 1, // Number of vertical subdivisions. + matrix4.mul(matrix4.translation([0, 0.85, 0]), zRot)); + + verts.append(o3djs.primitives.createCylinderVertices( + 0.06, // Radius. + 1.4, // Height. + 4, // Number of radial subdivisions. + 1, // Number of vertical subdivisions. + zRot)); + + verts.append(o3djs.primitives.createTruncatedConeVertices( + 0.0, // Bottom radius. + 0.15, // Top radius. + 0.3, // Height. + 4, // Number of radial subdivisions. + 1, // Number of vertical subdivisions. + matrix4.mul(matrix4.translation([0, -0.85, 0]), zRot))); + + shape = verts.createShape(pack, material); + manager.translate1Shape_ = shape; + } + + this.addShapes_([ shape ]); + + this.transformInfo = o3djs.picking.createTransformInfo(this.getTransform(), + manager.transformInfo); + + // Add a parameter to our transform to be able to change the + // material's color for highlighting. + this.colorParam = this.getTransform().createParam('diffuse', 'ParamFloat4'); + this.clearHighlight(); + + /** Dragging state */ + this.dragLine = new o3djs.manipulators.Line_(); +} + +o3djs.base.inherit(o3djs.manipulators.Translate1, o3djs.manipulators.Manip); + +o3djs.manipulators.Translate1.prototype.highlight = function(pickResult) { + // We can use instanced geometry for the entire Translate1 since its + // entire color changes during highlighting. + // TODO(kbr): support custom user geometry and associated callbacks. + this.colorParam.value = o3djs.manipulators.HIGHLIGHTED_COLOR; +} + +o3djs.manipulators.Translate1.prototype.clearHighlight = function() { + this.colorParam.value = o3djs.manipulators.DEFAULT_COLOR; +} + +o3djs.manipulators.Translate1.prototype.makeActive = function(pickResult) { + o3djs.manipulators.Manip.prototype.makeActive.call(this, pickResult); + this.highlight(pickResult); + var worldMatrix = this.getTransform().worldMatrix; + this.dragLine.setDirection( + o3djs.math.matrix4.transformDirection(worldMatrix, + o3djs.manipulators.X_AXIS)); + this.dragLine.setPoint(pickResult.worldIntersectionPosition); +} + +o3djs.manipulators.Translate1.prototype.makeInactive = function() { + o3djs.manipulators.Manip.prototype.makeInactive.call(this); + this.clearHighlight(); + this.updateAttachedTransformFromLocalTransform_(); + this.updateBaseTransformFromAttachedTransform_(); +} + +o3djs.manipulators.Translate1.prototype.drag = function(startPoint, + endPoint) { + // Algorithm: Find closest point of ray to dragLine. Subtract this + // point from the line's point to find difference vector; transform + // from world to local coordinates to find new local offset of + // manipulator. + var closestPoint = this.dragLine.closestPointToRay(startPoint, endPoint); + if (closestPoint == null) { + // Drag axis is parallel to ray. Punt. + return; + } + // Need to do a world-to-local transformation on the difference vector. + // Note that we also incorporate the translation portion of the matrix. + var diffVector = + o3djs.math.subVector(closestPoint, this.dragLine.getPoint()); + var worldToLocal = + o3djs.math.matrix4.inverse(this.getTransform().worldMatrix); + this.getTransform().localMatrix = + o3djs.math.matrix4.setTranslation( + this.getTransform().localMatrix, + o3djs.math.matrix4.transformDirection(worldToLocal, + diffVector)); + this.updateAttachedTransformFromLocalTransform_(); +} diff --git a/o3d/samples/o3djs/performance.js b/o3d/samples/o3djs/performance.js index 92865e5..c5faeaa 100644 --- a/o3d/samples/o3djs/performance.js +++ b/o3d/samples/o3djs/performance.js @@ -1,202 +1,202 @@ -/* - * Copyright 2009, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "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 THE COPYRIGHT - * OWNER OR CONTRIBUTORS 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. - */ - - -/** - * @fileoverview This file contains a utility that helps adjust rendering - * quality [or any other setting, really] based on rendering performance. - * - */ - -o3djs.provide('o3djs.performance'); - -/** - * A Module to help with adjusting performance. - * @namespace - */ -o3djs.performance = o3djs.performance || {}; - -/** - * Creates a utility that monitors performance [in terms of FPS] and helps to - * adjust the rendered scene accordingly. - * @param {number} targetFPSMin the minimum acceptable frame rate; if we're - * under this, try to decrease quality to improve performance. - * @param {number} targetFPSMax if we're over this, try to increase quality. - * @param {!function(): void} increaseQuality a function to increase - * quality because we're rendering at high-enough FPS to afford it. - * @param {!function(): void} decreaseQuality a function to decrease - * quality to try to raise our rendering speed. - * @param {!o3djs.performance.PerformanceMonitor.Options} opt_options Options. - * @return {!o3djs.performance.PerformanceMonitor} The created - * PerformanceMonitor. - */ -o3djs.performance.createPerformanceMonitor = function( - targetFPSMin, targetFPSMax, increaseQuality, decreaseQuality, opt_options) { - return new o3djs.performance.PerformanceMonitor(targetFPSMin, targetFPSMax, - increaseQuality, decreaseQuality, opt_options); -}; - -/** - * A class that monitors performance [in terms of FPS] and helps to adjust the - * rendered scene accordingly. - * @constructor - * @param {number} targetFPSMin the minimum acceptable frame rate; if we're - * under this, try to decrease quality to improve performance. - * @param {number} targetFPSMax if we're over this, try to increase quality. - * @param {function(): void} increaseQuality a function to increase - * quality/lower FPS. - * @param {function(): void} decreaseQuality a function to decrease - * quality/raise FPS. - * @param {!o3djs.performance.PerformanceMonitor.Options} opt_options Options. - */ -o3djs.performance.PerformanceMonitor = function( - targetFPSMin, targetFPSMax, increaseQuality, decreaseQuality, opt_options) { - opt_options = opt_options || {}; - - /** - * A function to increase quality/lower FPS. - * @type {function(): void} - */ - this.increaseQuality = increaseQuality; - - /** - * A function to decrease quality/raise FPS. - * @type {function(): void} - */ - this.decreaseQuality = decreaseQuality; - - /** - * The mean time taken per frame so far, in seconds. This is only valid once - * we've collected at least minSamples samples. - * @type {number} - */ - this.meanFrameTime = 0; - - /** - * The number of samples we've collected so far, when that number is less than - * or equal to this.damping. After that point, we no longer update - * this.sampleCount, so it will clip at this.damping. - * - * @type {number} - */ - this.sampleCount = 0; - - /** - * The minimum number of samples to collect before trying to adjust quality. - * - * @type {number} - */ - this.minSamples = opt_options.opt_minSamples || 60; - - /** - * A number that controls the rate at which the effects of any given sample - * fade away. Higher is slower, but also means that each individual sample - * counts for less at its most-influential. Damping defaults to 120; anywhere - * between 60 and 600 are probably reasonable values, depending on your needs, - * but the number must be no less than minSamples. - * - * @type {number} - */ - this.damping = opt_options.opt_damping || 120; - - /** - * The minimum number of samples to take in between adjustments, to cut down - * on overshoot. It defaults to 2 * minSamples. - * - * @type {number} - */ - this.delayCycles = opt_options.opt_delayCycles || 2 * this.minSamples; - - this.targetFrameTimeMax_ = 1 / targetFPSMin; - this.targetFrameTimeMin_ = 1 / targetFPSMax; - this.scaleInput_ = 1 / this.minSamples; - this.scaleMean_ = 1; - this.delayCyclesLeft_ = 0; - if (this.damping < this.minSamples) { - throw Error('Damping must be at least minSamples.'); - } -}; - -/** - * Options for the PerformanceMonitor. - * - * opt_minSamples is the minimum number of samples to take before making any - * performance adjustments. - * opt_damping is a number that controls the rate at which the effects of any - * given sample fade away. Higher is slower, but also means that each - * individual sample counts for less at its most-influential. Damping - * defaults to 120; anywhere between 60 and 600 are probably reasonable values, - * depending on your needs, but the number must be no less than minSamples. - * opt_delayCycles is the minimum number of samples to take in between - * adjustments, to cut down on overshoot. It defaults to 2 * opt_minSamples. - * - * @type {{ - * opt_minSamples: number, - * opt_damping: number, - * opt_delayCycles: number - * }} - */ -o3djs.performance.PerformanceMonitor.Options = goog.typedef; - -/** - * Call this once per frame with the elapsed time since the last call, and it - * will attempt to adjust your rendering quality as needed. - * - * @param {number} seconds the elapsed time since the last frame was rendered, - * in seconds. - */ -o3djs.performance.PerformanceMonitor.prototype.onRender = function(seconds) { - var test = true; - if (this.sampleCount < this.damping) { - if (this.sampleCount >= this.minSamples) { - this.scaleInput_ = 1 / (this.sampleCount + 1); - this.scaleMean_ = this.sampleCount * this.scaleInput_; - } else { - test = false; - } - this.sampleCount += 1; - } - this.meanFrameTime = this.meanFrameTime * this.scaleMean_ + - seconds * this.scaleInput_; - if (this.delayCyclesLeft_ > 0) { - this.delayCyclesLeft_ -= 1; - } else if (test) { - if (this.meanFrameTime < this.targetFrameTimeMin_) { - this.increaseQuality(); - this.delayCyclesLeft_ = this.delayCycles; - } else if (this.meanFrameTime > this.targetFrameTimeMax_) { - this.decreaseQuality(); - this.delayCyclesLeft_ = this.delayCycles; - } - } -}; - - +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + + +/** + * @fileoverview This file contains a utility that helps adjust rendering + * quality [or any other setting, really] based on rendering performance. + * + */ + +o3djs.provide('o3djs.performance'); + +/** + * A Module to help with adjusting performance. + * @namespace + */ +o3djs.performance = o3djs.performance || {}; + +/** + * Creates a utility that monitors performance [in terms of FPS] and helps to + * adjust the rendered scene accordingly. + * @param {number} targetFPSMin the minimum acceptable frame rate; if we're + * under this, try to decrease quality to improve performance. + * @param {number} targetFPSMax if we're over this, try to increase quality. + * @param {!function(): void} increaseQuality a function to increase + * quality because we're rendering at high-enough FPS to afford it. + * @param {!function(): void} decreaseQuality a function to decrease + * quality to try to raise our rendering speed. + * @param {!o3djs.performance.PerformanceMonitor.Options} opt_options Options. + * @return {!o3djs.performance.PerformanceMonitor} The created + * PerformanceMonitor. + */ +o3djs.performance.createPerformanceMonitor = function( + targetFPSMin, targetFPSMax, increaseQuality, decreaseQuality, opt_options) { + return new o3djs.performance.PerformanceMonitor(targetFPSMin, targetFPSMax, + increaseQuality, decreaseQuality, opt_options); +}; + +/** + * A class that monitors performance [in terms of FPS] and helps to adjust the + * rendered scene accordingly. + * @constructor + * @param {number} targetFPSMin the minimum acceptable frame rate; if we're + * under this, try to decrease quality to improve performance. + * @param {number} targetFPSMax if we're over this, try to increase quality. + * @param {function(): void} increaseQuality a function to increase + * quality/lower FPS. + * @param {function(): void} decreaseQuality a function to decrease + * quality/raise FPS. + * @param {!o3djs.performance.PerformanceMonitor.Options} opt_options Options. + */ +o3djs.performance.PerformanceMonitor = function( + targetFPSMin, targetFPSMax, increaseQuality, decreaseQuality, opt_options) { + opt_options = opt_options || {}; + + /** + * A function to increase quality/lower FPS. + * @type {function(): void} + */ + this.increaseQuality = increaseQuality; + + /** + * A function to decrease quality/raise FPS. + * @type {function(): void} + */ + this.decreaseQuality = decreaseQuality; + + /** + * The mean time taken per frame so far, in seconds. This is only valid once + * we've collected at least minSamples samples. + * @type {number} + */ + this.meanFrameTime = 0; + + /** + * The number of samples we've collected so far, when that number is less than + * or equal to this.damping. After that point, we no longer update + * this.sampleCount, so it will clip at this.damping. + * + * @type {number} + */ + this.sampleCount = 0; + + /** + * The minimum number of samples to collect before trying to adjust quality. + * + * @type {number} + */ + this.minSamples = opt_options.opt_minSamples || 60; + + /** + * A number that controls the rate at which the effects of any given sample + * fade away. Higher is slower, but also means that each individual sample + * counts for less at its most-influential. Damping defaults to 120; anywhere + * between 60 and 600 are probably reasonable values, depending on your needs, + * but the number must be no less than minSamples. + * + * @type {number} + */ + this.damping = opt_options.opt_damping || 120; + + /** + * The minimum number of samples to take in between adjustments, to cut down + * on overshoot. It defaults to 2 * minSamples. + * + * @type {number} + */ + this.delayCycles = opt_options.opt_delayCycles || 2 * this.minSamples; + + this.targetFrameTimeMax_ = 1 / targetFPSMin; + this.targetFrameTimeMin_ = 1 / targetFPSMax; + this.scaleInput_ = 1 / this.minSamples; + this.scaleMean_ = 1; + this.delayCyclesLeft_ = 0; + if (this.damping < this.minSamples) { + throw Error('Damping must be at least minSamples.'); + } +}; + +/** + * Options for the PerformanceMonitor. + * + * opt_minSamples is the minimum number of samples to take before making any + * performance adjustments. + * opt_damping is a number that controls the rate at which the effects of any + * given sample fade away. Higher is slower, but also means that each + * individual sample counts for less at its most-influential. Damping + * defaults to 120; anywhere between 60 and 600 are probably reasonable values, + * depending on your needs, but the number must be no less than minSamples. + * opt_delayCycles is the minimum number of samples to take in between + * adjustments, to cut down on overshoot. It defaults to 2 * opt_minSamples. + * + * @type {{ + * opt_minSamples: number, + * opt_damping: number, + * opt_delayCycles: number + * }} + */ +o3djs.performance.PerformanceMonitor.Options = goog.typedef; + +/** + * Call this once per frame with the elapsed time since the last call, and it + * will attempt to adjust your rendering quality as needed. + * + * @param {number} seconds the elapsed time since the last frame was rendered, + * in seconds. + */ +o3djs.performance.PerformanceMonitor.prototype.onRender = function(seconds) { + var test = true; + if (this.sampleCount < this.damping) { + if (this.sampleCount >= this.minSamples) { + this.scaleInput_ = 1 / (this.sampleCount + 1); + this.scaleMean_ = this.sampleCount * this.scaleInput_; + } else { + test = false; + } + this.sampleCount += 1; + } + this.meanFrameTime = this.meanFrameTime * this.scaleMean_ + + seconds * this.scaleInput_; + if (this.delayCyclesLeft_ > 0) { + this.delayCyclesLeft_ -= 1; + } else if (test) { + if (this.meanFrameTime < this.targetFrameTimeMin_) { + this.increaseQuality(); + this.delayCyclesLeft_ = this.delayCycles; + } else if (this.meanFrameTime > this.targetFrameTimeMax_) { + this.decreaseQuality(); + this.delayCyclesLeft_ = this.delayCycles; + } + } +}; + + diff --git a/o3d/samples/o3djs/texture.js b/o3d/samples/o3djs/texture.js index 33678b2..5041dde 100644 --- a/o3d/samples/o3djs/texture.js +++ b/o3d/samples/o3djs/texture.js @@ -1,239 +1,239 @@ -/* - * Copyright 2009, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "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 THE COPYRIGHT - * OWNER OR CONTRIBUTORS 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. - */ - - -/** - * @fileoverview This file contains functions helping to manipulate and manage - * textures. - */ - -o3djs.provide('o3djs.texture'); - -/** - * A Module for bitmaps. - * @namespace - */ -o3djs.texture = o3djs.texture || {}; - -/** - * The maximum dimension of a texture. - * @type {number} - */ -o3djs.texture.MAX_TEXTURE_DIMENSION = 2048; - -/** - * Computes the maximum number of levels of mips a given width and height could - * use. - * @param {number} width Width of texture. - * @param {number} height Height of texture. - * @return {number} The maximum number of levels for the given width and height. - */ -o3djs.texture.computeNumLevels = function(width, height) { - if (width == 0 || height == 0) { - return 0; - } - var max = Math.max(width, height); - var levels = 0; - while (max > 0) { - ++levels; - max = max >> 1; - } - return levels; -}; - -/** - * Creates a texture from a RawData object. - * @param {!o3d.Pack} pack The pack to create the texture in. - * @param {!o3d.RawData} rawData The raw data to create the texture from. - * @param {boolean} opt_generateMips Whether or not to generate mips. Note, mips - * can not be generated for DXT textures although they will be loaded if they - * exist in the RawData. - * @param {boolean} opt_flip Whether or not to flip the texture. Most DCC tools - * Like Maya, Max, etc expect the textures to be flipped. Note that only - * 2D (image) textures will be flipped. Cube textures will not be flipped. - * Default = true. - * @param {number} opt_maxWidth The maximum width of the texture. If the RawData - * is larger than this size it will be scaled down to this size. Note that - * DXT format textures can not be scaled. Default = 2048. - * @param {number} opt_maxHeight The maximum width of the texture. If the - * RawData is larger than this size it will be scaled down to this size. Note - * that DXT format textures can not be scaled. Default = 2048. - * @return {!o3d.Texture} The created texture. - */ -o3djs.texture.createTextureFromRawData = function( - pack, - rawData, - opt_generateMips, - opt_flip, - opt_maxWidth, - opt_maxHeight) { - // Make a bitmaps from the raw data. - var bitmaps = pack.createBitmapsFromRawData(rawData); - if (opt_flip || typeof opt_flip === 'undefined') { - for (var ii = 0; ii < bitmaps.length; ++ii) { - var bitmap = bitmaps[ii]; - if (bitmap.semantic == o3djs.base.o3d.Bitmap.IMAGE) { - bitmaps[ii].flipVertically(); - } - } - } - - // Create a texture from the bitmaps. - var texture = o3djs.texture.createTextureFromBitmaps( - pack, bitmaps, opt_generateMips); - - // Delete the bitmaps. - for (var ii = 0; ii < bitmaps.length; ++ii) { - pack.removeObject(bitmaps[ii]); - } - - return texture; -}; - -/** - * Returns whether or not a given texture format can be scaled. - * @param {!o3d.Texture.Format} format The format to check. - * @return {boolean} True if you can scale and make mips for the given format. - */ -o3djs.texture.canMakeMipsAndScale = function(format) { - switch (format) { - case o3djs.base.o3d.Texture.XRGB8: - case o3djs.base.o3d.Texture.ARGB8: - case o3djs.base.o3d.Texture.ABGR16F: - case o3djs.base.o3d.Texture.R32F: - case o3djs.base.o3d.Texture.ABGR32F: - return true; - case o3djs.base.o3d.Texture.DXT1: - case o3djs.base.o3d.Texture.DXT3: - case o3djs.base.o3d.Texture.DXT5: - return false; - } - return false; -}; - -/** - * Creates a Texture from an array of bitmaps. - * @param {!o3d.Pack} pack The pack to create the texture in. - * @param {!Array.<!o3d.Bitmap>} bitmaps An array of bitmaps to create the - * texture from. For a 2D texture this would be 1 bitmap. For a cubemap this - * would be 6 bitmaps. - * @param {boolean} opt_generateMips Whether or not to generate mips. Note, mips - * can not be generated for DXT textures although they will be loaded if they - * exist in the RawData. Default = true. - * @return {!o3d.Texture} The created texture. - */ -o3djs.texture.createTextureFromBitmaps = function( - pack, - bitmaps, - opt_generateMips) { - - if (bitmaps.length == 0) { - throw 'no bitmaps'; - } - - var srcWidth = bitmaps[0].width; - var srcHeight = bitmaps[0].height; - var format = bitmaps[0].format; - var mipMaps = bitmaps[0].numMipmaps; - var maxMips = o3djs.texture.computeNumLevels(srcWidth, srcHeight); - var targetMips = mipMaps; - var dstWidth = srcWidth; - var dstHeight = srcHeight; - if ((typeof opt_generateMips === 'undefined' || opt_generateMips) && - o3djs.texture.canMakeMipsAndScale(format) && - mipMaps == 1 && maxMips > 1) { - targetMips = maxMips; - } - - // Check that all the bitmaps are the same size and make mips - for (var ii = 0; ii < bitmaps.length; ++ii) { - var bitmap = bitmaps[ii]; - if (bitmap.width != srcWidth || - bitmap.height != srcHeight || - bitmap.format != format || - bitmap.numMipmaps != mipMaps) { - throw 'bitmaps must all be the same width, height, mips and format'; - } - if (targetMips != mipMaps) { - bitmap.generateMips(0, targetMips - 1); - } - } - - var levels = bitmap.numMipmaps > 1 ? bitmap.numMipmaps : - o3djs.texture.computeNumLevels(dstWidth, dstHeight); - var texture; - if (bitmaps.length == 6 && - bitmaps[0].semantic != o3djs.base.o3d.Bitmap.SLICE) { - if (srcWidth != srcHeight || - srcWidth != dstWidth || - srcHeight != dstHeight) { - throw 'Cubemaps must be square'; - } - texture = pack.createTextureCUBE(dstWidth, format, targetMips, false); - for (var ii = 0; ii < 6; ++ii) { - texture.setFromBitmap( - /** @type {o3d.TextureCUBE.CubeFace} */ (ii), - bitmaps[ii]); - } - } else if (bitmaps.length == 1) { - texture = pack.createTexture2D( - dstWidth, dstHeight, format, targetMips, false); - texture.setFromBitmap(bitmaps[0]); - } - - return /** @type{!o3d.Texture} */ (texture); -}; - -/** - * Creates a TextureCUBE from 6 bitmaps. The bitmaps do not have to be the same - * size thought they do have to be the same format. - * - * @param {!o3d.Pack} pack The pack to create the texture in. - * @param {number} edgeLength The size of the cubemap. - * @param {!Array.<!o3d.Bitmap>} bitmaps An array of 6 bitmaps in the order - * FACE_POSITIVE_X, FACE_NEGATIVE_X, FACE_POSITIVE_Y, FACE_NEGATIVE_Y, - * FACE_POSITIVE_Z, FACE_NEGATIVE_Z. - * @return {!o3d.Texture} The created texture. - */ -o3djs.texture.createCubeTextureFrom6Bitmaps = function( - pack, edgeLength, bitmaps) { - var numMips = o3djs.texture.computeNumLevels(edgeLength, edgeLength); - var texture = pack.createTextureCUBE( - edgeLength, bitmaps[0].format, numMips, false); - for (var ii = 0; ii < 6; ++ii) { - var bitmap = bitmaps[ii]; - texture.drawImage(bitmap, 0, 0, 0, bitmap.width, bitmap.height, - ii, 0, 0, edgeLength, edgeLength); - } - texture.generateMips(0, numMips - 1); - return texture; -}; - +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 THE COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + + +/** + * @fileoverview This file contains functions helping to manipulate and manage + * textures. + */ + +o3djs.provide('o3djs.texture'); + +/** + * A Module for bitmaps. + * @namespace + */ +o3djs.texture = o3djs.texture || {}; + +/** + * The maximum dimension of a texture. + * @type {number} + */ +o3djs.texture.MAX_TEXTURE_DIMENSION = 2048; + +/** + * Computes the maximum number of levels of mips a given width and height could + * use. + * @param {number} width Width of texture. + * @param {number} height Height of texture. + * @return {number} The maximum number of levels for the given width and height. + */ +o3djs.texture.computeNumLevels = function(width, height) { + if (width == 0 || height == 0) { + return 0; + } + var max = Math.max(width, height); + var levels = 0; + while (max > 0) { + ++levels; + max = max >> 1; + } + return levels; +}; + +/** + * Creates a texture from a RawData object. + * @param {!o3d.Pack} pack The pack to create the texture in. + * @param {!o3d.RawData} rawData The raw data to create the texture from. + * @param {boolean} opt_generateMips Whether or not to generate mips. Note, mips + * can not be generated for DXT textures although they will be loaded if they + * exist in the RawData. + * @param {boolean} opt_flip Whether or not to flip the texture. Most DCC tools + * Like Maya, Max, etc expect the textures to be flipped. Note that only + * 2D (image) textures will be flipped. Cube textures will not be flipped. + * Default = true. + * @param {number} opt_maxWidth The maximum width of the texture. If the RawData + * is larger than this size it will be scaled down to this size. Note that + * DXT format textures can not be scaled. Default = 2048. + * @param {number} opt_maxHeight The maximum width of the texture. If the + * RawData is larger than this size it will be scaled down to this size. Note + * that DXT format textures can not be scaled. Default = 2048. + * @return {!o3d.Texture} The created texture. + */ +o3djs.texture.createTextureFromRawData = function( + pack, + rawData, + opt_generateMips, + opt_flip, + opt_maxWidth, + opt_maxHeight) { + // Make a bitmaps from the raw data. + var bitmaps = pack.createBitmapsFromRawData(rawData); + if (opt_flip || typeof opt_flip === 'undefined') { + for (var ii = 0; ii < bitmaps.length; ++ii) { + var bitmap = bitmaps[ii]; + if (bitmap.semantic == o3djs.base.o3d.Bitmap.IMAGE) { + bitmaps[ii].flipVertically(); + } + } + } + + // Create a texture from the bitmaps. + var texture = o3djs.texture.createTextureFromBitmaps( + pack, bitmaps, opt_generateMips); + + // Delete the bitmaps. + for (var ii = 0; ii < bitmaps.length; ++ii) { + pack.removeObject(bitmaps[ii]); + } + + return texture; +}; + +/** + * Returns whether or not a given texture format can be scaled. + * @param {!o3d.Texture.Format} format The format to check. + * @return {boolean} True if you can scale and make mips for the given format. + */ +o3djs.texture.canMakeMipsAndScale = function(format) { + switch (format) { + case o3djs.base.o3d.Texture.XRGB8: + case o3djs.base.o3d.Texture.ARGB8: + case o3djs.base.o3d.Texture.ABGR16F: + case o3djs.base.o3d.Texture.R32F: + case o3djs.base.o3d.Texture.ABGR32F: + return true; + case o3djs.base.o3d.Texture.DXT1: + case o3djs.base.o3d.Texture.DXT3: + case o3djs.base.o3d.Texture.DXT5: + return false; + } + return false; +}; + +/** + * Creates a Texture from an array of bitmaps. + * @param {!o3d.Pack} pack The pack to create the texture in. + * @param {!Array.<!o3d.Bitmap>} bitmaps An array of bitmaps to create the + * texture from. For a 2D texture this would be 1 bitmap. For a cubemap this + * would be 6 bitmaps. + * @param {boolean} opt_generateMips Whether or not to generate mips. Note, mips + * can not be generated for DXT textures although they will be loaded if they + * exist in the RawData. Default = true. + * @return {!o3d.Texture} The created texture. + */ +o3djs.texture.createTextureFromBitmaps = function( + pack, + bitmaps, + opt_generateMips) { + + if (bitmaps.length == 0) { + throw 'no bitmaps'; + } + + var srcWidth = bitmaps[0].width; + var srcHeight = bitmaps[0].height; + var format = bitmaps[0].format; + var mipMaps = bitmaps[0].numMipmaps; + var maxMips = o3djs.texture.computeNumLevels(srcWidth, srcHeight); + var targetMips = mipMaps; + var dstWidth = srcWidth; + var dstHeight = srcHeight; + if ((typeof opt_generateMips === 'undefined' || opt_generateMips) && + o3djs.texture.canMakeMipsAndScale(format) && + mipMaps == 1 && maxMips > 1) { + targetMips = maxMips; + } + + // Check that all the bitmaps are the same size and make mips + for (var ii = 0; ii < bitmaps.length; ++ii) { + var bitmap = bitmaps[ii]; + if (bitmap.width != srcWidth || + bitmap.height != srcHeight || + bitmap.format != format || + bitmap.numMipmaps != mipMaps) { + throw 'bitmaps must all be the same width, height, mips and format'; + } + if (targetMips != mipMaps) { + bitmap.generateMips(0, targetMips - 1); + } + } + + var levels = bitmap.numMipmaps > 1 ? bitmap.numMipmaps : + o3djs.texture.computeNumLevels(dstWidth, dstHeight); + var texture; + if (bitmaps.length == 6 && + bitmaps[0].semantic != o3djs.base.o3d.Bitmap.SLICE) { + if (srcWidth != srcHeight || + srcWidth != dstWidth || + srcHeight != dstHeight) { + throw 'Cubemaps must be square'; + } + texture = pack.createTextureCUBE(dstWidth, format, targetMips, false); + for (var ii = 0; ii < 6; ++ii) { + texture.setFromBitmap( + /** @type {o3d.TextureCUBE.CubeFace} */ (ii), + bitmaps[ii]); + } + } else if (bitmaps.length == 1) { + texture = pack.createTexture2D( + dstWidth, dstHeight, format, targetMips, false); + texture.setFromBitmap(bitmaps[0]); + } + + return /** @type{!o3d.Texture} */ (texture); +}; + +/** + * Creates a TextureCUBE from 6 bitmaps. The bitmaps do not have to be the same + * size thought they do have to be the same format. + * + * @param {!o3d.Pack} pack The pack to create the texture in. + * @param {number} edgeLength The size of the cubemap. + * @param {!Array.<!o3d.Bitmap>} bitmaps An array of 6 bitmaps in the order + * FACE_POSITIVE_X, FACE_NEGATIVE_X, FACE_POSITIVE_Y, FACE_NEGATIVE_Y, + * FACE_POSITIVE_Z, FACE_NEGATIVE_Z. + * @return {!o3d.Texture} The created texture. + */ +o3djs.texture.createCubeTextureFrom6Bitmaps = function( + pack, edgeLength, bitmaps) { + var numMips = o3djs.texture.computeNumLevels(edgeLength, edgeLength); + var texture = pack.createTextureCUBE( + edgeLength, bitmaps[0].format, numMips, false); + for (var ii = 0; ii < 6; ++ii) { + var bitmap = bitmaps[ii]; + texture.drawImage(bitmap, 0, 0, 0, bitmap.width, bitmap.height, + ii, 0, 0, edgeLength, edgeLength); + } + texture.generateMips(0, numMips - 1); + return texture; +}; + diff --git a/o3d/samples/siteswap/animation.js b/o3d/samples/siteswap/animation.js index eb20cc8..c9aa80a 100644 --- a/o3d/samples/siteswap/animation.js +++ b/o3d/samples/siteswap/animation.js @@ -1,198 +1,198 @@ -// @@REWRITE(insert js-copyright) -// @@REWRITE(delete-start) -// Copyright 2009 Google Inc. All Rights Reserved -// @@REWRITE(delete-end) - -/** - * This file contains the animation-management code for the siteswap animator. - * This is encapsulated in the EventQueue and QueueEvent classes, the event - * handler, and startAnimation, the main external interface to the animation. - */ - -/** - * A record, held in the EventQueue, that describes the curve that should be - * given to a shape at a time. - * @constructor - * @param {!number} time base time at which the event occurs/the curve starts. - * @param {!o3d.Shape} shape the shape to be updated. - * @param {Curve} curve the path for the shape to follow. - */ -function QueueEvent(time, shape, curve) { - this.time = time; - this.shape = shape; - this.curve = curve; - return this; -} - -/** - * A circular queue of events that will happen during the course of an animation - * that's duration beats long. The queue is ordered by the time each curve - * starts. Note that a curve may start after it ends, since time loops - * endlessly. The nextEvent field is the index of the next event to occur; it - * keeps track of how far we've gotten in the queue. - * @constructor - * @param {!number} duration the length of the animation in beats. - */ -function EventQueue(duration) { - this.events = []; - this.nextEvent = 0; - this.duration = duration; - this.timeCorrection = 0; // Corrects from queue entry time to real time. - return this; -} - -/** - * Add an event to the queue, inserting into order by its time field. - * A heap-based priority queue would be faster, but likely overkill, as this - * won't ever contain that many items, and isn't likely to be speed-critical. - * @param {!QueueEvent} event the event to add. - */ -EventQueue.prototype.addEvent = function(event) { - var i = 0; - while (i < this.events.length && event.time > this.events[i].time) { - ++i; - } - this.events.splice(i, 0, event); -}; - -/** - * Pull the next event off the queue. - * @return {!QueueEvent} the event. - */ -EventQueue.prototype.shift = function() { - var e = this.events[this.nextEvent]; - if (++this.nextEvent >= this.events.length) { - assert(this.nextEvent > 0); - this.nextEvent = 0; - this.timeCorrection += this.duration; - } - return e; -}; - -/** - * Process all current events, updating all animated objects with their new - * curves, until we find that the next event in the queue is in the future. - * @param {!number} time the current time in beats. This number is an absolute, - * not locked to the range of the duration of the pattern, so getNextTime() - * returns a doctored number to add in the offset from in-pattern time to real - * time. - * @return {!number} the time of the next future event. - */ -EventQueue.prototype.processEvents = function(time) { - while (this.getNextTime() <= time) { - var e = this.shift(); - setParamCurveInfo(e.curve, e.shape, time); - } - return this.getNextTime(); // In case you want to set a callback. -}; - -/** - * Look up the initial curve for a shape [the curve that it'll be starting or in - * the middle of at time 0]. - * @param {!CurveSet} curveSet the complete set of curves for a Shape. - * @return {!Object} the curve and the time at which it would have started. - */ -function getInitialCurveInfo(curveSet) { - var curve = curveSet.getCurveForUnsafeTime(0); - var curveBaseTime; - if (!curve.startTime) { - curveBaseTime = 0; - } else { - // If the curve isn't starting now, it must have wrapped around. - assert(curve.startTime + curve.duration > curveSet.duration); - // So subtract off one wrap so that its startTime is in the right space of - // numbers. We assume here that no curve duration is longer than the - // pattern, which must be guaranteed by the code that generates patterns. - assert(curve.duration <= curveSet.duration); - curveBaseTime = curve.startTime - curveSet.duration; - } - return { curve: curve, curveBaseTime: curveBaseTime }; -} - -/** - * Set up the event queue with a complete pattern starting at time 0. - * @param {!Array.CurveSet} curveSets the curve sets for all shapes. - * @param {!Array.o3d.Shape} shapes all the shapes to animate. - */ -EventQueue.prototype.setUp = function(curveSets, shapes) { - assert(curveSets.length == shapes.length); - for (var i = 0; i < shapes.length; ++i) { - var curveSet = curveSets[i]; - assert(this.duration % curveSet.duration == 0); - var shape = shapes[i]; - var record = getInitialCurveInfo(curveSet); - var curveBaseTime = record.curveBaseTime; - var curve = record.curve; - setParamCurveInfo(curve, shape, curveBaseTime); - do { - curveBaseTime += curve.duration; - curve = curveSet.getCurveForTime(curveBaseTime); - var e = new QueueEvent(curveBaseTime % this.duration, shape, curve); - this.addEvent(e); - } while (curveBaseTime + curve.duration <= this.duration); - } -}; - -/** - * Return the time of the next future event. - * @return {!number} the time. - */ -EventQueue.prototype.getNextTime = function() { - return this.events[this.nextEvent].time + this.timeCorrection; -}; - -/** - * This is the event handler that runs the whole animation. When triggered by - * the counter, it updates the curves on all objects whose curves have expired. - * - * The current time will be some time around when we wanted to be called. It - * might be exact, but it might be a bit off due to floating point error, or a - * lot off due to the system getting bogged down somewhere for a second or - * two. e.g. if we wanted to get a call at time 7, it's likely to be - * something like 7.04, but might even be 11. We then use 7, not 7.04, as the - * start time for each of the curves set, so as to remove clock drift. Since - * the time we wanted to be called is stored in the next item in the queue, we - * can just pull that out and use it. However, if we then find that we're - * setting our callback in the past, we repeat the process until our callback - * is set safely in the future. We may get some visual artifacts, but at - * least we won't drop any events [leading to stuff drifting endlessly off - * into the distance]. - */ -function handler() { - var eventTime = g.eventQueue.getNextTime(); - var trueCurrentTime; - do { - g.counter.removeCallback(eventTime); - eventTime = g.eventQueue.processEvents(eventTime); - g.counter.addCallback(eventTime, handler); - trueCurrentTime = g.counter.count; - } while (eventTime < trueCurrentTime); -} - -/** - * Given a precomputed juggling pattern, this sets up the O3D objects, - * EventQueue, and callback necessary to start an animation, then calls - * updateAnimating to kick it off if enabled. - * - * @param {!number} numBalls the number of balls in the animation. - * @param {!number} numHands the number of hands in the animation. - * @param {!number} duration the length of the full animation cycle in beats. - * @param {!Array.CurveSet} ballCurveSets one CurveSet per ball. - * @param {!Array.CurveSet} handCurveSets one CurveSet per hand. - */ -function startAnimation(numBalls, numHands, duration, ballCurveSets, - handCurveSets) { - g.counter.running = false; - g.counter.reset(); - - setNumBalls(numBalls); - setNumHands(numHands); - - g.eventQueue = new EventQueue(duration); - g.eventQueue.setUp(handCurveSets, g.handShapes); - g.eventQueue.setUp(ballCurveSets, g.ballShapes); - g.counter.addCallback(g.eventQueue.getNextTime(), handler); - - updateAnimating(); -} - +// @@REWRITE(insert js-copyright) +// @@REWRITE(delete-start) +// Copyright 2009 Google Inc. All Rights Reserved +// @@REWRITE(delete-end) + +/** + * This file contains the animation-management code for the siteswap animator. + * This is encapsulated in the EventQueue and QueueEvent classes, the event + * handler, and startAnimation, the main external interface to the animation. + */ + +/** + * A record, held in the EventQueue, that describes the curve that should be + * given to a shape at a time. + * @constructor + * @param {!number} time base time at which the event occurs/the curve starts. + * @param {!o3d.Shape} shape the shape to be updated. + * @param {Curve} curve the path for the shape to follow. + */ +function QueueEvent(time, shape, curve) { + this.time = time; + this.shape = shape; + this.curve = curve; + return this; +} + +/** + * A circular queue of events that will happen during the course of an animation + * that's duration beats long. The queue is ordered by the time each curve + * starts. Note that a curve may start after it ends, since time loops + * endlessly. The nextEvent field is the index of the next event to occur; it + * keeps track of how far we've gotten in the queue. + * @constructor + * @param {!number} duration the length of the animation in beats. + */ +function EventQueue(duration) { + this.events = []; + this.nextEvent = 0; + this.duration = duration; + this.timeCorrection = 0; // Corrects from queue entry time to real time. + return this; +} + +/** + * Add an event to the queue, inserting into order by its time field. + * A heap-based priority queue would be faster, but likely overkill, as this + * won't ever contain that many items, and isn't likely to be speed-critical. + * @param {!QueueEvent} event the event to add. + */ +EventQueue.prototype.addEvent = function(event) { + var i = 0; + while (i < this.events.length && event.time > this.events[i].time) { + ++i; + } + this.events.splice(i, 0, event); +}; + +/** + * Pull the next event off the queue. + * @return {!QueueEvent} the event. + */ +EventQueue.prototype.shift = function() { + var e = this.events[this.nextEvent]; + if (++this.nextEvent >= this.events.length) { + assert(this.nextEvent > 0); + this.nextEvent = 0; + this.timeCorrection += this.duration; + } + return e; +}; + +/** + * Process all current events, updating all animated objects with their new + * curves, until we find that the next event in the queue is in the future. + * @param {!number} time the current time in beats. This number is an absolute, + * not locked to the range of the duration of the pattern, so getNextTime() + * returns a doctored number to add in the offset from in-pattern time to real + * time. + * @return {!number} the time of the next future event. + */ +EventQueue.prototype.processEvents = function(time) { + while (this.getNextTime() <= time) { + var e = this.shift(); + setParamCurveInfo(e.curve, e.shape, time); + } + return this.getNextTime(); // In case you want to set a callback. +}; + +/** + * Look up the initial curve for a shape [the curve that it'll be starting or in + * the middle of at time 0]. + * @param {!CurveSet} curveSet the complete set of curves for a Shape. + * @return {!Object} the curve and the time at which it would have started. + */ +function getInitialCurveInfo(curveSet) { + var curve = curveSet.getCurveForUnsafeTime(0); + var curveBaseTime; + if (!curve.startTime) { + curveBaseTime = 0; + } else { + // If the curve isn't starting now, it must have wrapped around. + assert(curve.startTime + curve.duration > curveSet.duration); + // So subtract off one wrap so that its startTime is in the right space of + // numbers. We assume here that no curve duration is longer than the + // pattern, which must be guaranteed by the code that generates patterns. + assert(curve.duration <= curveSet.duration); + curveBaseTime = curve.startTime - curveSet.duration; + } + return { curve: curve, curveBaseTime: curveBaseTime }; +} + +/** + * Set up the event queue with a complete pattern starting at time 0. + * @param {!Array.CurveSet} curveSets the curve sets for all shapes. + * @param {!Array.o3d.Shape} shapes all the shapes to animate. + */ +EventQueue.prototype.setUp = function(curveSets, shapes) { + assert(curveSets.length == shapes.length); + for (var i = 0; i < shapes.length; ++i) { + var curveSet = curveSets[i]; + assert(this.duration % curveSet.duration == 0); + var shape = shapes[i]; + var record = getInitialCurveInfo(curveSet); + var curveBaseTime = record.curveBaseTime; + var curve = record.curve; + setParamCurveInfo(curve, shape, curveBaseTime); + do { + curveBaseTime += curve.duration; + curve = curveSet.getCurveForTime(curveBaseTime); + var e = new QueueEvent(curveBaseTime % this.duration, shape, curve); + this.addEvent(e); + } while (curveBaseTime + curve.duration <= this.duration); + } +}; + +/** + * Return the time of the next future event. + * @return {!number} the time. + */ +EventQueue.prototype.getNextTime = function() { + return this.events[this.nextEvent].time + this.timeCorrection; +}; + +/** + * This is the event handler that runs the whole animation. When triggered by + * the counter, it updates the curves on all objects whose curves have expired. + * + * The current time will be some time around when we wanted to be called. It + * might be exact, but it might be a bit off due to floating point error, or a + * lot off due to the system getting bogged down somewhere for a second or + * two. e.g. if we wanted to get a call at time 7, it's likely to be + * something like 7.04, but might even be 11. We then use 7, not 7.04, as the + * start time for each of the curves set, so as to remove clock drift. Since + * the time we wanted to be called is stored in the next item in the queue, we + * can just pull that out and use it. However, if we then find that we're + * setting our callback in the past, we repeat the process until our callback + * is set safely in the future. We may get some visual artifacts, but at + * least we won't drop any events [leading to stuff drifting endlessly off + * into the distance]. + */ +function handler() { + var eventTime = g.eventQueue.getNextTime(); + var trueCurrentTime; + do { + g.counter.removeCallback(eventTime); + eventTime = g.eventQueue.processEvents(eventTime); + g.counter.addCallback(eventTime, handler); + trueCurrentTime = g.counter.count; + } while (eventTime < trueCurrentTime); +} + +/** + * Given a precomputed juggling pattern, this sets up the O3D objects, + * EventQueue, and callback necessary to start an animation, then calls + * updateAnimating to kick it off if enabled. + * + * @param {!number} numBalls the number of balls in the animation. + * @param {!number} numHands the number of hands in the animation. + * @param {!number} duration the length of the full animation cycle in beats. + * @param {!Array.CurveSet} ballCurveSets one CurveSet per ball. + * @param {!Array.CurveSet} handCurveSets one CurveSet per hand. + */ +function startAnimation(numBalls, numHands, duration, ballCurveSets, + handCurveSets) { + g.counter.running = false; + g.counter.reset(); + + setNumBalls(numBalls); + setNumHands(numHands); + + g.eventQueue = new EventQueue(duration); + g.eventQueue.setUp(handCurveSets, g.handShapes); + g.eventQueue.setUp(ballCurveSets, g.ballShapes); + g.counter.addCallback(g.eventQueue.getNextTime(), handler); + + updateAnimating(); +} + diff --git a/o3d/samples/siteswap/math.js b/o3d/samples/siteswap/math.js index c5e2f43..e584744 100644 --- a/o3d/samples/siteswap/math.js +++ b/o3d/samples/siteswap/math.js @@ -1,1492 +1,1492 @@ -// @@REWRITE(insert js-copyright) -// @@REWRITE(delete-start) -// Copyright 2009 Google Inc. All Rights Reserved -// @@REWRITE(delete-end) - -/** - * @fileoverview This file contains all the math for the siteswap animator. It - * handles all of the site-swap-related stuff [converting a sequence of integers - * into a more-useful representation of a pattern, pattern validation, etc.] as - * well as all the physics used for the simulation. - */ - -/** - * This is a container class that holds the coefficients of an equation - * describing the motion of an object. - * The basic equation is: - * f(x) := a t^2 + b t + c + d sin (f t) + e cos (f t). - * However, sometimes we LERP between that function and this one: - * g(x) := lA t^2 + lB t + lC - * lerpRate [so far] is always either 1 [LERP from f to g over 1 beat] or -1, - * [LERP from g to f over one beat]. - * - * Just plug in t to evaluate the equation. There's no JavaScript function to - * do this because it's always done on the GPU. - * - * @constructor - */ -EquationCoefficients = function(a, b, c, d, e, f, lA, lB, lC, lerpRate) { - assert(!isNaN(a) && !isNaN(b) && !isNaN(c)); - d = d || 0; - e = e || 0; - f = f || 0; - assert(!isNaN(d) && !isNaN(e) && !isNaN(f)); - lA = lA || 0; - lB = lB || 0; - lC = lC || 0; - assert(!isNaN(lA) && !isNaN(lB) && !isNaN(lC)); - lerpRate = lerpRate || 0; - this.a = a; - this.b = b; - this.c = c; - this.d = d; - this.e = e; - this.f = f; - this.lA = lA; - this.lB = lB; - this.lC = lC; - this.lerpRate = lerpRate; -} - -/** - * Create a new equation that's equivalent to this equation's coefficients a-f - * with a LERP to the polynomial portion of the supplied equation. - * @param {!EquationCoefficients} eqn the source of coefficients. - * @param {!number} lerpRate the rate and direction of the LERP; positive for - * "from this equation to the new one" and vice-versa. - * @return {!EquationCoefficients} a new set of coefficients. - */ -EquationCoefficients.prototype.lerpIn = function(eqn, lerpRate) { - assert(!this.lerpRate); - return new EquationCoefficients(this.a, this.b, this.c, this.d, this.e, - this.f, eqn.a, eqn.b, eqn.c, lerpRate); -}; - -/** - * Convert the EquationCoefficients to a string for debugging. - * @return {String} debugging output. - */ -EquationCoefficients.prototype.toString = function() { - return 'F(t) := ' + this.a.toFixed(2) + ' t^2 + ' + this.b.toFixed(2) + - ' t + ' + this.c.toFixed(2) + ' + ' + - this.d.toFixed(2) + ' sin(' + this.f.toFixed(2) + ' t) + ' + - this.e.toFixed(2) + ' cos(' + this.f.toFixed(2) + ' t) + LERP(' + - this.lerpRate.toFixed(2) + ') of ' + - this.lA.toFixed(2) + ' t^2 + ' + this.lB.toFixed(2) + - ' t + ' + this.lC.toFixed(2); -}; - -/** - * A set of equations which describe the motion of an object over time. - * The three equations each supply one dimension of the motion, and the curve is - * valid from startTime to startTime + duration. - * @param {!number} startTime the initial time at which the curve is valid. - * @param {!number} duration how long [in beats] the curve is valid. - * @param {!EquationCoefficients} xEqn the equation for motion in x. - * @param {!EquationCoefficients} yEqn the equation for motion in y. - * @param {!EquationCoefficients} zEqn the equation for motion in z. - * @constructor - */ -Curve = function(startTime, duration, xEqn, yEqn, zEqn) { - this.startTime = startTime; - this.duration = duration; - this.xEqn = xEqn; - this.yEqn = yEqn; - this.zEqn = zEqn; -} - -/** - * Convert the Curve to a string for debugging. - * @return {String} debugging output. - */ -Curve.prototype.toString = function() { - var s = 'startTime: ' + this.startTime + '\n'; - s += 'duration: ' + this.duration + '\n'; - s += this.xEqn + '\n'; - s += this.yEqn + '\n'; - s += this.zEqn + '\n'; - return s; -}; - -/** - * Modify this curve's coefficients to include a LERP to the polynomial - * portion of the supplied curve. - * @param {!Curve} curve the source of coefficients. - * @param {!number} lerpRate the rate and direction of the LERP; positive for - * "from this equation to the new one" and vice-versa. - * @return {!Curve} a new curve. - */ -Curve.prototype.lerpIn = function(curve, lerpRate) { - assert(this.startTime == curve.startTime); - assert(this.duration == curve.duration); - var xEqn = this.xEqn.lerpIn(curve.xEqn, lerpRate); - var yEqn = this.yEqn.lerpIn(curve.yEqn, lerpRate); - var zEqn = this.zEqn.lerpIn(curve.zEqn, lerpRate); - return new Curve(this.startTime, this.duration, xEqn, yEqn, zEqn); -}; - -/** - * Produce a set of polynomial coefficients that describe linear motion between - * two points in 1 dimension. - * @param {!number} startPos the starting position. - * @param {!number} endPos the ending position. - * @param {!number} duration how long the motion takes. - * @return {!EquationCoefficients} the equation for the motion. - */ -Curve.computeLinearCoefficients = function(startPos, endPos, duration) { - return new EquationCoefficients( - 0, (endPos - startPos) / duration, startPos); -} - -var GRAVITY = 1; // Higher means higher throws for the same duration. -/** - * Produce a set of polynomial coefficients that describe parabolic motion - * between two points in 1 dimension. - * @param {!number} startPos the starting position. - * @param {!number} endPos the ending position. - * @param {!number} duration how long the motion takes. - * @return {!EquationCoefficients} the equation for the motion. - */ -Curve.computeParabolicCoefficients = function(startPos, endPos, duration) { - var dY = endPos - startPos; - return new EquationCoefficients(-GRAVITY / 2, - dY / duration + GRAVITY * duration / 2, - startPos); -} - -/** - * Compute the curve taken by a ball given its throw and catch positions, the - * time it was thrown, and how long it stayed in the air. - * - * We use duration rather than throwTime and catchTime because, what - * with the modular arithmetic used in our records, catchTime might be before - * throwTime, and in some representations the pattern could wrap around a few - * times while the ball's in the air. When the parabola computed here is used, - * time must be supplied as an offset from the time of the throw, and must of - * course not wrap at all. That is, these coefficients work for f(0) == - * throwPos, f(duration) == catchPos. - * - * We treat the y axis as vertical and thus affected by gravity. - * - * @param {!EquationCoefficients} throwPos - * @param {!EquationCoefficients} catchPos - * @param {!number} startTime - * @param {!number} duration - * @return {!Curve} - */ -Curve.computeThrowCurve = function(throwPos, catchPos, startTime, duration) { - var xEqn = Curve.computeLinearCoefficients(throwPos.x, catchPos.x, duration); - var yEqn = Curve.computeParabolicCoefficients(throwPos.y, catchPos.y, - duration); - var zEqn = Curve.computeLinearCoefficients(throwPos.z, catchPos.z, duration); - return new Curve(startTime, duration, xEqn, yEqn, zEqn); -} - -/** - * Compute a straight line Curve given start and end positions, the start time, - * and the duration of the motion. - * - * @param {!EquationCoefficients} startPos - * @param {!EquationCoefficients} endPos - * @param {!number} startTime - * @param {!number} duration - * @return {!Curve} - */ -Curve.computeStraightLineCurve = - function(startPos, endPos, startTime, duration) { - var xEqn = Curve.computeLinearCoefficients(startPos.x, endPos.x, duration); - var yEqn = Curve.computeLinearCoefficients(startPos.y, endPos.y, duration); - var zEqn = Curve.computeLinearCoefficients(startPos.z, endPos.z, duration); - return new Curve(startTime, duration, xEqn, yEqn, zEqn); -} - -/** - * Threshold horizontal distance below which computeCircularCurve won't bother - * trying to approximate a circular curve. See the comment above - * computeCircularCurve for more info. - * @type {number} - */ -Curve.EPSILON = .0001; - -/** - * Compute a circular curve, used as an approximation for the motion of a hand - * between a catch and its following throw. - * - * Assumes a lot of stuff about this looking like a "normal" throw: the catch is - * moving roughly the opposite direction as the throw, the throw and catch - * aren't at the same place, and such. Otherwise this looks very odd at best. - * This is used for the height of the curve. - * This produces coefficients for d sin(f t) + e cos(f t) for each of x, y, z. - * It produces a vertical-ish circular curve from the start to the end, going - * down, then up. So if dV [the distance from the start to finish in the x-z - * plane, ignoring y] is less than Curve.EPSILON, it doesn't know which way down - * is, and it bails by returning a straight line instead. - */ -Curve.computeCircularCurve = function(startPos, endPos, startTime, duration) { - var dX = endPos.x - startPos.x; - var dY = endPos.y - startPos.y; - var dZ = endPos.z - startPos.z; - var dV = Math.sqrt(dX * dX + dZ * dZ); - if (dV < Curve.EPSILON) { - return Curve.computeStraightLineCurve(startPos, endPos, startTime, - duration); - } - var negHalfdV = -0.5 * dV; - var negHalfdY = -0.5 * dY; - var f = Math.PI / duration; - var yEqn = new EquationCoefficients( - 0, 0, startPos.y + dY / 2, - negHalfdV, negHalfdY, f); - var ratio = dX / dV; - var xEqn = new EquationCoefficients( - 0, 0, startPos.x + dX / 2, - negHalfdY * ratio, negHalfdV * ratio, f); - ratio = dZ / dV; - var zEqn = new EquationCoefficients( - 0, 0, startPos.z + dZ / 2, - negHalfdY * ratio, negHalfdV * ratio, f); - return new Curve(startTime, duration, xEqn, yEqn, zEqn); -} - -/** - * This is the abstract base class for an object that describes a throw, catch, - * or empty hand [placeholder] in a site-swap pattern. - * @constructor - */ -Descriptor = function() { -} - -/** - * Create an otherwise-identical copy of this descriptor at a given time offset. - * Note that offset may put time past patternLength; the caller will have to fix - * this up manually. - * @param {number} offset how many beats to offset the new descriptor. - * Derived classes must override this function. - */ -Descriptor.prototype.clone = function(offset) { - throw new Error('Unimplemented.'); -}; - -/** - * Generate the Curve implied by this descriptor and the supplied hand - * positions. - * @param {!Array.HandPositionRecord} handPositions where the hands will be. - * Derived classes must override this function. - */ -Descriptor.prototype.generateCurve = function(handPositions) { - throw new Error('Unimplemented.'); -}; - -/** - * Adjust the start time of this Descriptor to be in [0, pathLength). - * @param {!number} pathLength the duration of a path, in beats. - * @return {!Descriptor} this. - */ -Descriptor.prototype.fixUpModPathLength = function(pathLength) { - this.time = this.time % pathLength; - return this; -}; - -/** - * This describes a throw in a site-swap pattern. - * @param {!number} throwNum the site-swap number of the throw. - * @param {!number} throwTime the time this throw occurs. - * @param {!number} sourceHand the index of the throwing hand. - * @param {!number} destHand the index of the catching hand. - * @constructor - */ -ThrowDescriptor = function(throwNum, throwTime, sourceHand, destHand) { - this.throwNum = throwNum; - this.sourceHand = sourceHand; - this.destHand = destHand; - this.time = throwTime; -} - -/** - * This is a subclass of Descriptor. - */ -ThrowDescriptor.prototype = new Descriptor(); - -/** - * Set up the constructor, just to be neat. - */ -ThrowDescriptor.prototype.constructor = ThrowDescriptor; - -/** - * We label each Descriptor subclass with a type for debugging. - */ -ThrowDescriptor.prototype.type = 'THROW'; - -/** - * Create an otherwise-identical copy of this descriptor at a given time offset. - * Note that offset may put time past patternLength; the caller will have to fix - * this up manually. - * @param {number} offset how many beats to offset the new descriptor. - * @return {!Descriptor} the new copy. - */ -ThrowDescriptor.prototype.clone = function(offset) { - offset = offset || 0; // Turn null into 0. - return new ThrowDescriptor(this.throwNum, this.time + offset, - this.sourceHand, this.destHand); -}; - -/** - * Convert the ThrowDescriptor to a string for debugging. - * @return {String} debugging output. - */ -ThrowDescriptor.prototype.toString = function() { - return '(' + this.throwNum + ' from hand ' + this.sourceHand + ' to hand ' + - this.destHand + ')'; -}; - -/** - * Generate the Curve implied by this descriptor and the supplied hand - * positions. - * @param {!Array.HandPositionRecord} handPositions where the hands will be. - * @return {!Curve} the curve. - */ -ThrowDescriptor.prototype.generateCurve = function(handPositions) { - var startPos = handPositions[this.sourceHand].throwPositions[this.destHand]; - var endPos = handPositions[this.destHand].catchPosition; - return Curve.computeThrowCurve(startPos, endPos, this.time, - this.throwNum - 1); }; - -/** - * This describes a catch in a site-swap pattern. - * @param {!number} hand the index of the catching hand. - * @param {!number} sourceThrowNum the site-swap number of the preceeding throw. - * @param {!number} destThrowNum the site-swap number of the following throw. - * @param {!number} sourceHand the index of the hand throwing the source throw. - * @param {!number} destHand the index of the hand catching the following throw. - * @param {!number} catchTime the time at which the catch occurs. - * @constructor - */ -CarryDescriptor = function(hand, sourceThrowNum, destThrowNum, sourceHand, - destHand, catchTime) { - this.hand = hand; - this.sourceThrowNum = sourceThrowNum; - this.destThrowNum = destThrowNum; - this.sourceHand = sourceHand; - this.destHand = destHand; - this.time = catchTime; -} - -/** - * This is a subclass of Descriptor. - */ -CarryDescriptor.prototype = new Descriptor(); - -/** - * Set up the constructor, just to be neat. - */ -CarryDescriptor.prototype.constructor = CarryDescriptor; - -/** - * We label each Descriptor subclass with a type for debugging. - */ -CarryDescriptor.prototype.type = 'CARRY'; - -/** - * Since this gets pathLength, not patternLength, we'll have to collapse sets - * of CarryDescriptors later, as they may be spread sparsely through the full - * animation and we'll only want them to be distributed over the full pattern - * length. We may have dupes to throw away as well. - * @param {!ThrowDescriptor} inThrowDescriptor - * @param {!ThrowDescriptor} outThrowDescriptor - * @param {!number} pathLength - * @return {!CarryDescriptor} - */ -CarryDescriptor.fromThrowDescriptors = function(inThrowDescriptor, - outThrowDescriptor, pathLength) { - assert(inThrowDescriptor.destHand == outThrowDescriptor.sourceHand); - assert((inThrowDescriptor.time + inThrowDescriptor.throwNum) % - pathLength == outThrowDescriptor.time); - return new CarryDescriptor(inThrowDescriptor.destHand, - inThrowDescriptor.throwNum, outThrowDescriptor.throwNum, - inThrowDescriptor.sourceHand, outThrowDescriptor.destHand, - (outThrowDescriptor.time + pathLength - 1) % pathLength); -}; - -/** - * Create an otherwise-identical copy of this descriptor at a given time offset. - * Note that offset may put time past patternLength; the caller will have to fix - * this up manually. - * @param {number} offset how many beats to offset the new descriptor. - * @return {!Descriptor} the new copy. - */ -CarryDescriptor.prototype.clone = function(offset) { - offset = offset || 0; // Turn null into 0. - return new CarryDescriptor(this.hand, this.sourceThrowNum, - this.destThrowNum, this.sourceHand, this.destHand, this.time + offset); -}; - -/** - * Convert the CarryDescriptor to a string for debugging. - * @return {String} debugging output. - */ -CarryDescriptor.prototype.toString = function() { - return 'time: ' + this.time + ' (hand ' + this.hand + ' catches ' + - this.sourceThrowNum + ' from hand ' + this.sourceHand + ' then throws ' + - this.destThrowNum + ' to hand ' + this.destHand + ')'; -}; - -/** - * Test if this CarryDescriptor is equivalent to another, mod patternLength. - * @param {!CarryDescriptor} cd the other CarryDescriptor. - * @param {!number} patternLength the length of the pattern. - * @return {!bool} - */ -CarryDescriptor.prototype.equalsWithMod = function(cd, patternLength) { - if (!(cd instanceof CarryDescriptor)) { - return false; - } - if (this.hand != cd.hand) { - return false; - } - if (this.sourceThrowNum != cd.sourceThrowNum) { - return false; - } - if (this.destThrowNum != cd.destThrowNum) { - return false; - } - if (this.sourceHand != cd.sourceHand) { - return false; - } - if (this.destHand != cd.destHand) { - return false; - } - if (this.time % patternLength != cd.time % patternLength) { - return false; - } - return true; -}; - -/** - * Generate the Curve implied by this descriptor and the supplied hand - * positions. - * @param {!Array.HandPositionRecord} handPositions where the hands will be. - * @return {!Curve} the curve. - */ -CarryDescriptor.prototype.generateCurve = function(handPositions) { - var startPos = handPositions[this.hand].catchPosition; - var endPos = handPositions[this.hand].throwPositions[this.destHand]; - return Curve.computeCircularCurve(startPos, endPos, this.time, 1); -}; - -/** - * This describes a carry of a "1" in a site-swap pattern. - * The flags isThrow and isCatch tell whether this is the actual 1 [isThrow] or - * the carry that receives the handoff [isCatch]. It's legal for both to be - * true, which happens when there are two 1s in a row. - * @param {!number} sourceThrowNum the site-swap number of the prev throw - * [including this one if isCatch]. - * @param {!number} sourceHand the index of the hand throwing sourceThrowNum. - * @param {!number} destThrowNum the site-swap number of the next throw - * [including this one if isThrow]. - * @param {!number} destHand the index of the hand catching destThrowNum. - * @param {!number} hand the index of the hand doing this carry. - * @param {!number} time the time at which the carry starts. - * @param {!bool} isThrow whether this is a 1. - * @param {!bool} isCatch whether this is the carry after a 1. - * @constructor - */ -CarryOneDescriptor = function(sourceThrowNum, sourceHand, destThrowNum, - destHand, hand, time, isThrow, isCatch) { - // It's possible to have !isCatch with sourceThrowNum == 1 temporarily, if we - // just haven't handled that 1 yet [we're doing the throw of this one, and - // will later get to the previous one, due to wraparound], and vice-versa. - assert(isThrow || (sourceThrowNum == 1)); - assert(isCatch || (destThrowNum == 1)); - this.sourceThrowNum = sourceThrowNum; - this.sourceHand = sourceHand; - this.destHand = destHand; - this.destThrowNum = destThrowNum; - this.hand = hand; - this.time = time; - this.isThrow = isThrow; - this.isCatch = isCatch; - return this; -} - -/** - * This is a subclass of Descriptor. - */ -CarryOneDescriptor.prototype = new Descriptor(); - -/** - * Set up the constructor, just to be neat. - */ -CarryOneDescriptor.prototype.constructor = CarryOneDescriptor; - -/** - * We label each Descriptor subclass with a type for debugging. - */ -CarryOneDescriptor.prototype.type = 'CARRY_ONE'; - -/** - * Create a pair of CarryOneDescriptors to describe the carry that is a throw of - * 1. A 1 spends all its time being carried, so these two carries surrounding - * it represent [and therefore don't have] a throw between them. - * Prev and post are generally the ordinary CarryDescriptors surrounding the - * throw of 1 that we're trying to implement. However, they could each [or - * both] independently be CarryOneDescriptors implementing other 1 throws. - * @param {!Descriptor} prev the carry descriptor previous to the 1. - * @param {!Descriptor} post the carry descriptor subsequent to the 1. - * @return {!Array.CarryOneDescriptor} a pair of CarryOneDescriptors. - */ -CarryOneDescriptor.getDescriptorPair = function(prev, post) { - assert(prev instanceof CarryDescriptor || prev instanceof CarryOneDescriptor); - assert(post instanceof CarryDescriptor || post instanceof CarryOneDescriptor); - assert(prev.destHand == post.hand); - assert(prev.hand == post.sourceHand); - var newPrev; - var newPost; - if (prev instanceof CarryOneDescriptor) { - assert(prev.isCatch && !prev.isThrow); - newPrev = prev; - newPrev.isThrow = true; - assert(newPrev.destHand == post.hand); - } else { - newPrev = new CarryOneDescriptor(prev.sourceThrowNum, prev.sourceHand, 1, - post.hand, prev.hand, prev.time, true, false); - } - if (post instanceof CarryOneDescriptor) { - assert(post.isThrow && !post.isCatch); - newPost = post; - newPost.isCatch = true; - assert(newPost.sourceHand == prev.hand); - assert(newPost.sourceThrowNum == 1); - } else { - newPost = new CarryOneDescriptor(1, prev.hand, post.destThrowNum, - post.destHand, post.hand, post.time, false, true); - } - return [newPrev, newPost]; -}; - -/** - * Convert the CarryOneDescriptor to a string for debugging. - * @return {String} debugging output. - */ -CarryOneDescriptor.prototype.toString = function() { - var s; - if (this.isThrow) { - s = 'Hand ' + this.hand + ' catches a ' + this.sourceThrowNum + ' from ' + - this.sourceHand + ' at time ' + this.time + ' and then passes a 1 to ' + - this.destHand + '.'; - } else { - assert(this.isCatch && this.sourceThrowNum == 1); - s = 'Hand ' + this.hand + ' catches a 1 from ' + this.sourceHand + - ' at time ' + this.time + ' and then passes a ' + this.destThrowNum + - ' to ' + this.destHand + '.'; - } - return s; -}; - -/** - * Compute the curve taken by a ball during the carry representing a 1, as long - * as it's not both a catch and a throw of a 1, which is handled elsewhere. - * It's either a LERP from a circular curve [a catch of a throw > 1] to a - * straight line to the handoff point [for isThrow] or a LERP from a straight - * line from the handoff to a circular curve for the next throw > 1 [for - * isCatch]. - * - * @param {!EquationCoefficients} catchPos - * @param {!EquationCoefficients} throwPos - * @param {!EquationCoefficients} handoffPos - * @param {!number} startTime - * @param {!bool} isCatch whether this is the carry after a 1. - * @param {!bool} isThrow whether this is a 1. - * @return {!Curve} - */ -Curve.computeCarryOneCurve = function(catchPos, throwPos, handoffPos, startTime, - isCatch, isThrow) { - assert(!isCatch != !isThrow); - var curve = Curve.computeCircularCurve(catchPos, throwPos, startTime, 1); - var curve2 = Curve.computeStraightLineCurve(handoffPos, handoffPos, - startTime, 1); - return curve.lerpIn(curve2, isThrow ? 1 : -1); -} - -/** - * Compute the curve taken by a ball during the carry representing a 1 that is - * both the catch of one 1 and the immediately-following throw of another 1. - * - * @param {!EquationCoefficients} leadingHandoffPos - * @param {!EquationCoefficients} trailingHandoffPos - * @param {!Array.HandPositionRecord} handPositions where the hands will be. - * @param {!number} hand - * @param {!number} time the time at which the first 1's catch takes place. - * @return {!Curve} - */ -Curve.computeConsecutiveCarryOneCurve = function(leadingHandoffPos, - trailingHandoffPos, handPositions, hand, time) { - var curve = Curve.computeStraightLineCurve(leadingHandoffPos, - handPositions[hand].basePosition, time, 1); - var curve2 = - Curve.computeStraightLineCurve(handPositions[hand].basePosition, - trailingHandoffPos, time, 1); - return curve.lerpIn(curve2, 1); -} - -/** - * Generate the Curve implied by this descriptor and the supplied hand - * positions. - * @param {!Array.HandPositionRecord} handPositions where the hands will be. - * @return {!Curve} the curve. - */ -CarryOneDescriptor.prototype.generateCurve = function(handPositions) { - var leadingHandoffPos, trailingHandoffPos; - if (this.isCatch) { - var p0 = handPositions[this.hand].basePosition; - var p1 = handPositions[this.sourceHand].basePosition; - handoffPos = leadingHandoffPos = p0.add(p1).scale(0.5); - } - if (this.isThrow) { - var p0 = handPositions[this.hand].basePosition; - var p1 = handPositions[this.destHand].basePosition; - handoffPos = trailingHandoffPos = p0.add(p1).scale(0.5); - } - if (!this.isCatch || !this.isThrow) { - return Curve.computeCarryOneCurve(handPositions[this.hand].catchPosition, - handPositions[this.hand].throwPositions[this.destHand], handoffPos, - this.time, this.isCatch, this.isThrow); - } else { - return Curve.computeConsecutiveCarryOneCurve(leadingHandoffPos, - trailingHandoffPos, handPositions, this.hand, this.time); - } -}; - -/** - * Create an otherwise-identical copy of this descriptor at a given time offset. - * Note that offset may put time past patternLength; the caller will have to fix - * this up manually. - * @param {number} offset how many beats to offset the new descriptor. - * @return {!Descriptor} the new copy. - */ -CarryOneDescriptor.prototype.clone = function(offset) { - offset = offset || 0; // Turn null into 0. - return new CarryOneDescriptor(this.sourceThrowNum, this.sourceHand, - this.destThrowNum, this.destHand, this.hand, this.time + offset, - this.isThrow, this.isCatch); -}; - -/** - * Test if this CarryOneDescriptor is equivalent to another, mod patternLength. - * @param {!CarryOneDescriptor} cd the other CarryOneDescriptor. - * @param {!number} patternLength the length of the pattern. - * @return {!bool} - */ -CarryOneDescriptor.prototype.equalsWithMod = function(cd, patternLength) { - if (!(cd instanceof CarryOneDescriptor)) { - return false; - } - if (this.hand != cd.hand) { - return false; - } - if (this.sourceThrowNum != cd.sourceThrowNum) { - return false; - } - if (this.destThrowNum != cd.destThrowNum) { - return false; - } - if (this.sourceHand != cd.sourceHand) { - return false; - } - if (this.destHand != cd.destHand) { - return false; - } - if (this.isCatch != cd.isCatch) { - return false; - } - if (this.isThrow != cd.isThrow) { - return false; - } - if (this.time % patternLength != cd.time % patternLength) { - return false; - } - return true; -}; - -/** - * This describes an empty hand in a site-swap pattern. - * @param {!Descriptor} cd0 the CarryDescriptor or CarryOneDescriptor describing - * this hand immediately before it was emptied. - * @param {!Descriptor} cd1 the CarryDescriptor or CarryOneDescriptor describing - * this hand immediately after it's done being empty. - * @param {!number} patternLength the length of the pattern. - * @constructor - */ -EmptyHandDescriptor = function(cd0, cd1, patternLength) { - assert(cd0.hand == cd1.hand); - this.hand = cd0.hand; - this.prevThrowDest = cd0.destHand; - this.sourceThrowNum = cd0.destThrowNum; - this.nextCatchSource = cd1.sourceHand; - this.destThrowNum = cd1.sourceThrowNum; - // This code assumes that each CarryDescriptor and CarryOneDescriptor always - // has a duration of 1 beat. If we want to be able to allow long-held balls - // [instead of thrown twos, for example], we'll have to fix that here and a - // number of other places. - this.time = (cd0.time + 1) % patternLength; - this.duration = cd1.time - this.time; - if (this.duration < 0) { - this.duration += patternLength; - assert(this.duration > 0); - } -} - -/** - * This is a subclass of Descriptor. - */ -EmptyHandDescriptor.prototype = new Descriptor(); - -/** - * Set up the constructor, just to be neat. - */ -EmptyHandDescriptor.prototype.constructor = EmptyHandDescriptor; - -/** - * We label each Descriptor subclass with a type for debugging. - */ -EmptyHandDescriptor.prototype.type = 'EMPTY'; - -/** - * Convert the EmptyHandDescriptor to a string for debugging. - * @return {String} debugging output. - */ -EmptyHandDescriptor.prototype.toString = function() { - return 'time: ' + this.time + ' for ' + this.duration + ' (hand ' + - this.hand + ', after throwing a ' + this.sourceThrowNum + ' to hand ' + - this.prevThrowDest + ' then catches a ' + this.destThrowNum + - ' from hand ' + this.nextCatchSource + ')'; -}; - -/** - * Generate the Curve implied by this descriptor and the supplied hand - * positions. - * @param {!Array.HandPositionRecord} handPositions where the hands will be. - * @return {!Curve} the curve. - */ -EmptyHandDescriptor.prototype.generateCurve = function(handPositions) { - var startPos, endPos; - if (this.sourceThrowNum == 1) { - var p0 = handPositions[this.hand].basePosition; - var p1 = handPositions[this.prevThrowDest].basePosition; - startPos = p0.add(p1).scale(0.5); - } else { - startPos = handPositions[this.hand].throwPositions[this.prevThrowDest]; - } - if (this.destThrowNum == 1) { - var p0 = handPositions[this.hand].basePosition; - var p1 = handPositions[this.nextCatchSource].basePosition; - endPos = p0.add(p1).scale(0.5); - } else { - endPos = handPositions[this.hand].catchPosition; - } - // TODO: Replace with a good empty-hand curve. - return Curve.computeStraightLineCurve(startPos, endPos, this.time, - this.duration); -}; - -/** - * A series of descriptors that describes the full path of an object during a - * pattern. - * @param {!Array.Descriptor} descriptors all descriptors for the object. - * @param {!number} pathLength the length of the path in beats. - * @constructor - */ -Path = function(descriptors, pathLength) { - this.descriptors = descriptors; - this.pathLength = pathLength; -} - -/** - * Create a Path representing a ball, filling in the gaps between the throws - * with carry descriptors. Since it's a ball's path, there are no - * EmptyHandDescriptors in the output. - * @param {!Array.ThrowDescriptor} throwDescriptors the ball's part of the - * pattern. - * @param {!number} pathLength the length of the pattern in beats. - * @return {!Path} the ball's full path. - */ -Path.ballPathFromThrowDescriptors = function(throwDescriptors, pathLength) { - return new Path( - Path.createDescriptorList(throwDescriptors, pathLength), pathLength); -}; - -/** - * Create the sequence of ThrowDescriptors, CarryDescriptors, and - * CarryOneDescriptor describing the path of a ball through a pattern. - * A sequence such as (h j k) generally maps to an alternating series of throw - * and carry descriptors [Th Chj Tj Cjk Tk Ck? ...]. However, when j is a 1, - * you remove the throw descriptor and modify the previous and subsequent carry - * descriptors, since the throw descriptor has zero duration and the carry - * descriptors need to take into account the handoff. - * @param {!Array.ThrowDescriptor} throwDescriptors the ball's part of the - * pattern. - * @param {!number} pathLength the length of the pattern in beats. - * @return {!Array.Descriptor} the full set of descriptors for the ball. - */ -Path.createDescriptorList = function(throwDescriptors, pathLength) { - var descriptors = []; - var prevThrow; - for (var index in throwDescriptors) { - var td = throwDescriptors[index]; - if (prevThrow) { - descriptors.push( - CarryDescriptor.fromThrowDescriptors(prevThrow, td, pathLength)); - } // Else it's handled after the loop. - descriptors.push(td); - prevThrow = td; - } - descriptors.push( - CarryDescriptor.fromThrowDescriptors(prevThrow, throwDescriptors[0], - pathLength)); - // Now post-process to take care of throws of 1. It's easier to do it here - // than during construction since we can now assume that the previous and - // subsequent carry descriptors are already in place [modulo pathLength]. - for (var i = 0; i < descriptors.length; ++i) { - var descriptor = descriptors[i]; - if (descriptor instanceof ThrowDescriptor) { - if (descriptor.throwNum == 1) { - var prevIndex = (i + descriptors.length - 1) % descriptors.length; - var postIndex = (i + 1) % descriptors.length; - var replacements = CarryOneDescriptor.getDescriptorPair( - descriptors[prevIndex], descriptors[postIndex]); - descriptors[prevIndex] = replacements[0]; - descriptors[postIndex] = replacements[1]; - descriptors.splice(i, 1); - // We've removed a descriptor from the array, but since we can never - // have 2 ThrowDescriptors in a row, we don't need to decrement i. - } - } - } - return descriptors; -}; - -/** - * Convert the Path to a string for debugging. - * @return {String} debugging output. - */ -Path.prototype.toString = function() { - var ret = 'pathLength is ' + this.pathLength + '; ['; - for (var index in this.descriptors) { - ret += this.descriptors[index].toString(); - } - ret += ']'; - return ret; -}; - -/** - * Create an otherwise-identical copy of this path at a given time offset. - * Note that offset may put time references in the Path past the length of the - * pattern. The caller must fix this up manually. - * @param {number} offset how many beats to offset the new Path. - * @return {!Path} the new copy. - */ -Path.prototype.clone = function(offset) { - offset = offset || 0; // Turn null into 0. - var descriptors = []; - for (var index in this.descriptors) { - descriptors.push(this.descriptors[index].clone(offset)); - } - return new Path(descriptors, this.pathLength); -}; - -/** - * Adjust the start time of all descriptors to be in [0, pathLength) via modular - * arithmetic. Reorder the array such that they're sorted in increasing order - * of time. - * @return {!Path} this. - */ -Path.prototype.fixUpModPathLength = function() { - var splitIndex; - var prevTime = 0; - for (var index in this.descriptors) { - var d = this.descriptors[index]; - d.fixUpModPathLength(this.pathLength); - if (d.time < prevTime) { - assert(null == splitIndex); - splitIndex = index; // From here to the end should move to the start. - } - prevTime = d.time; - } - if (null != splitIndex) { - var temp = this.descriptors.slice(splitIndex); - this.descriptors.length = splitIndex; - this.descriptors = temp.concat(this.descriptors); - } - return this; -}; - -/** - * Take a standard asynch siteswap pattern [expressed as an array of ints] and - * a number of hands, and expand it into a 2D grid of ThrowDescriptors with one - * row per hand. - * Non-asynch patterns are more complicated, since their linear forms aren't - * fully-specified, so we don't handle them here. - * You'll want to expand your pattern to the LCM of numHands and minimal pattern - * length before calling this. - * The basic approach doesn't really work for one-handed patterns. It ends up - * with catches and throws happening at the same time [having removed all - * empty-hand time in between them]. To fix this, we double all throw heights - * and space them out, as if doing a two-handed pattern with all zeroes from the - * other hand. Yes, this points out that the overall approach we're taking is a - * bit odd [since you end up with hands empty for time proportional to the - * number of hands], but you have to make some sort of assumptions to generalize - * siteswaps to N hands, and that's what I chose. - * @param {!Array.number} pattern an asynch siteswap pattern. - * @param {!number} numHands the number of hands. - * @return {!Array.Array.ThrowDescriptor} the expanded pattern. - */ -function expandPattern(pattern, numHands) { - var fullPattern = []; - assert(numHands > 0); - if (numHands == 1) { - numHands = 2; - var temp = []; - for (var i = 0; i < pattern.length; ++i) { - temp[2 * i] = 2 * pattern[i]; - temp[2 * i + 1] = 0; - } - pattern = temp; - } - for (var hand = 0; hand < numHands; ++hand) { - fullPattern[hand] = []; - } - for (var time = 0; time < pattern.length; ++time) { - for (var hand = 0; hand < numHands; ++hand) { - var t; - if (hand == time % numHands) { - t = new ThrowDescriptor(pattern[time], time, hand, - (hand + pattern[time]) % numHands); - } else { - // These are ignored during analysis, so they don't appear in BallPaths. - t = new ThrowDescriptor(0, time, hand, hand); - } - fullPattern[hand].push(t); - } - } - return fullPattern; -} - -// TODO: Wrap the final pattern in a class, then make the remaining few global -// functions be members of that class to clean up the global namespace. - -/** - * Given a valid site-swap for a nonzero number of balls, stored as an expanded - * pattern array-of-arrays, with pattern length the LCM of hands and minimal - * pattern length, produce Paths for all the balls. - * @param {!Array.Array.ThrowDescriptor} pattern a valid pattern. - * @return {!Array.Path} the paths of all the balls. - */ -function generateBallPaths(pattern) { - var numHands = pattern.length; - assert(numHands > 0); - var patternLength = pattern[0].length; - assert(patternLength > 0); - var sum = 0; - for (var hand in pattern) { - for (var time in pattern[hand]) { - sum += pattern[hand][time].throwNum; - } - } - var numBalls = sum / patternLength; - assert(numBalls == Math.round(numBalls)); - assert(numBalls > 0); - - var ballsToAllocate = numBalls; - var ballPaths = []; - // NOTE: The indices of locationsChecked are reversed from those of pattern - // for simplicity of allocation. This might be worth flipping to match. - var locationsChecked = []; - for (var time = 0; time < patternLength && ballsToAllocate; ++time) { - locationsChecked[time] = locationsChecked[time] || []; - for (var hand = 0; hand < numHands && ballsToAllocate; ++hand) { - if (locationsChecked[time][hand]) { - continue; - } - var curThrowDesc = pattern[hand][time]; - var curThrow = curThrowDesc.throwNum; - if (!curThrow) { - assert(curThrow === 0); - continue; - } - var throwDescriptors = []; - var curTime = time; - var curHand = hand; - var wraps = 0; - do { - if (!locationsChecked[curTime]) { - locationsChecked[curTime] = []; - } - assert(!locationsChecked[curTime][curHand]); - locationsChecked[curTime][curHand] = true; - // We copy curThrowDesc here, adding wraps * patternLength, to get - // the true throw time relative to offset. Later we'll add in offset - // when we clone again, then mod by pathLength. - throwDescriptors.push(curThrowDesc.clone(wraps * patternLength)); - var nextThrowTime = curThrow + curTime; - wraps += Math.floor(nextThrowTime / patternLength); - curTime = nextThrowTime % patternLength; - assert(curTime >= time); // Else we'd have covered it earlier. - curHand = curThrowDesc.destHand; - var tempThrowDesc = curThrowDesc; - curThrowDesc = pattern[curHand][curTime]; - curThrow = curThrowDesc.throwNum; - assert(tempThrowDesc.destHand == curThrowDesc.sourceHand); - assert(curThrowDesc.time == - (tempThrowDesc.throwNum + tempThrowDesc.time) % patternLength); - } while (curTime != time || curHand != hand); - var pathLength = wraps * patternLength; - var ballPath = - Path.ballPathFromThrowDescriptors(throwDescriptors, pathLength); - for (var i = 0; i < wraps; ++i) { - var offset = i * patternLength % pathLength; - ballPaths.push(ballPath.clone(offset, pathLength).fixUpModPathLength()); - } - ballsToAllocate -= wraps; - assert(ballsToAllocate >= 0); - } - } - return ballPaths; -} - -/** - * Given an array of ball paths, produce the corresponding set of hand paths. - * @param {!Array.Path} ballPaths the Paths of all the balls in the pattern. - * @param {!number} numHands how many hands to use in the pattern. - * @param {!number} patternLength the length, in beats, of the pattern. - * @return {!Array.Path} the paths of all the hands. - */ -function generateHandPaths(ballPaths, numHands, patternLength) { - assert(numHands > 0); - assert(patternLength > 0); - var handRecords = []; // One record per hand. - for (var idxBR in ballPaths) { - var descriptors = ballPaths[idxBR].descriptors; - for (var idxD in descriptors) { - var descriptor = descriptors[idxD]; - // TODO: Fix likely needed for throws of 1. - if (!(descriptor instanceof ThrowDescriptor)) { - // It's a CarryDescriptor or a CarryOneDescriptor. - var hand = descriptor.hand; - if (!handRecords[hand]) { - handRecords[hand] = []; - } - // TODO: Should we not shorten stuff here if we're going to lengthen - // everything later anyway? Is there a risk of inconsistency due to - // ball paths of different lengths? - var catchTime = descriptor.time % patternLength; - if (!handRecords[hand][catchTime]) { - // We pass in this offset to set the new descriptor's time to - // catchTime, so as to keep it within [0, patternLength). - handRecords[hand][catchTime] = - descriptor.clone(catchTime - descriptor.time); - } else { - assert( - handRecords[hand][catchTime].equalsWithMod( - descriptor, patternLength)); - } - } - } - } - var handPaths = []; - for (var hand in handRecords) { - var outDescriptors = []; - var inDescriptors = handRecords[hand]; - var prevDescriptor = null; - var descriptor; - for (var idxD in inDescriptors) { - descriptor = inDescriptors[idxD]; - assert(descriptor); // Enumeration should skip array holes. - assert(descriptor.hand == hand); - if (prevDescriptor) { - outDescriptors.push(new EmptyHandDescriptor(prevDescriptor, descriptor, - patternLength)); - } - outDescriptors.push(descriptor.clone()); - prevDescriptor = descriptor; - } - // Note that this EmptyHandDescriptor that wraps around the end lives at the - // end of the array, not the beginning, despite the fact that it may be the - // active one at time zero. This is the same behavior as with Paths for - // balls. - descriptor = new EmptyHandDescriptor(prevDescriptor, outDescriptors[0], - patternLength); - if (descriptor.time < outDescriptors[0].time) { - assert(descriptor.time + descriptor.duration == outDescriptors[0].time); - outDescriptors.unshift(descriptor); - } else { - assert(descriptor.time == - outDescriptors[outDescriptors.length - 1].time + 1); - outDescriptors.push(descriptor); - } - handPaths[hand] = - new Path(outDescriptors, patternLength).fixUpModPathLength(); - } - return handPaths; -} - -// NOTE: All this Vector stuff does lots of object allocations. If that's a -// problem for your browser [e.g. IE6], you'd better stick with the embedded V8. -// This code predates the creation of o3djs/math.js; I should probably switch it -// over at some point, but for now it's not worth the trouble. - -/** - * A simple 3-dimensional vector. - * @constructor - */ -Vector = function(x, y, z) { - this.x = x; - this.y = y; - this.z = z; -} - -Vector.prototype.sub = function(v) { - return new Vector(this.x - v.x, this.y - v.y, this.z - v.z); -}; - -Vector.prototype.add = function(v) { - return new Vector(this.x + v.x, this.y + v.y, this.z + v.z); -}; - -Vector.prototype.dot = function(v) { - return this.x * v.x + this.y * v.y + this.z * v.z; -}; - -Vector.prototype.length = function() { - return Math.sqrt(this.dot(this)); -}; - -Vector.prototype.scale = function(s) { - return new Vector(this.x * s, this.y * s, this.z * s); -}; - -Vector.prototype.set = function(v) { - this.x = v.x; - this.y = v.y; - this.z = v.z; -}; - -Vector.prototype.normalize = function() { - var length = this.length(); - assert(length); - this.set(this.scale(1 / length)); - return this; -}; - -/** - * Convert the Vector to a string for debugging. - * @return {String} debugging output. - */ -Vector.prototype.toString = function() { - return '{' + this.x.toFixed(3) + ', ' + this.y.toFixed(3) + ', ' + - this.z.toFixed(3) + '}'; -}; - -/** - * A container class that holds the positions relevant to a hand: where it is - * when it's not doing anything, where it likes to catch balls, and where it - * likes to throw balls to each of the other hands. - * @param {!Vector} basePosition the centroid of throw and catch positions when - * the hand throws to itself. - * @param {!Vector} catchPosition where the hand likes to catch balls. - * @constructor - */ -HandPositionRecord = function(basePosition, catchPosition) { - this.basePosition = basePosition; - this.catchPosition = catchPosition; - this.throwPositions = []; -} - -/** - * Convert the HandPositionRecord to a string for debugging. - * @return {String} debugging output. - */ -HandPositionRecord.prototype.toString = function() { - var s = 'base: ' + this.basePosition.toString() + ';\n'; - s += 'catch: ' + this.catchPosition.toString() + ';\n'; - s += 'throws:\n'; - for (var i = 0; i < this.throwPositions.length; ++i) { - s += '[' + i + '] ' + this.throwPositions[i].toString() + '\n'; - } - return s; -}; - -/** - * Compute all the hand positions used in a pattern given a number of hands and - * a grouping style ["even" for evenly-spaced hands, "pairs" to group them in - * pairs, as with 2-handed jugglers]. - * @param {!number} numHands the number of hands to use. - * @param {!String} style the grouping style. - * @return {!Array.HandPositionRecord} a full set of hand positions. - */ -function computeHandPositions(numHands, style) { - assert(numHands > 0); - var majorRadiusScale = 0.75; - var majorRadius = majorRadiusScale * (numHands - 1); - var throwCatchOffset = 0.45; - var catchRadius = majorRadius + throwCatchOffset; - var handPositionRecords = []; - for (var hand = 0; hand < numHands; ++hand) { - var circleFraction; - if (style == 'even') { - circleFraction = hand / numHands; - } else { - assert(style == 'pairs'); - circleFraction = (hand + Math.floor(hand / 2)) / (1.5 * numHands); - } - var cos = Math.cos(Math.PI * 2 * circleFraction); - var sin = Math.sin(Math.PI * 2 * circleFraction); - var cX = catchRadius * cos; - var cY = 0; - var cZ = catchRadius * sin; - var bX = majorRadius * cos; - var bY = 0; - var bZ = majorRadius * sin; - handPositionRecords[hand] = new HandPositionRecord( - new Vector(bX, bY, bZ), new Vector(cX, cY, cZ)); - } - // Now that we've got all the hands' base and catch positions, we need to - // compute the appropriate throw positions for each hand pair. - for (var source = 0; source < numHands; ++source) { - var throwHand = handPositionRecords[source]; - for (var target = 0; target < numHands; ++target) { - var catchHand = handPositionRecords[target]; - if (throwHand == catchHand) { - var baseV = throwHand.basePosition; - throwHand.throwPositions[target] = - baseV.add(baseV.sub(throwHand.catchPosition)); - } else { - var directionV = - catchHand.catchPosition.sub(throwHand.basePosition).normalize(); - var offsetV = directionV.scale(throwCatchOffset); - throwHand.throwPositions[target] = - throwHand.basePosition.add(offsetV); - } - } - } - return handPositionRecords; -} - -/** - * Convert an array of HandPositionRecord to a string for debugging. - * @param {!Array.HandPositionRecord} positions the positions to display. - * @return {String} debugging output. - */ -function getStringFromHandPositions(positions) { - var s = ''; - for (index in positions) { - s += positions[index].toString(); - } - return s; -} - -/** - * The set of curves an object passes through throughout a full animation cycle. - * @param {!number} duration the length of the animation in beats. - * @param {!Array.Curve} curves the full set of Curves. - * @constructor - */ -CurveSet = function(duration, curves) { - this.duration = duration; - this.curves = curves; -} - -/** - * Looks up what curve is active at a particular time. This is slower than - * getCurveForTime, but can be used even if no Curve starts precisely at - * unsafeTime % this.duration. - * @param {!number} unsafeTime the time at which to check. - * @return {!Curve} the curve active at unsafeTime. - */ -CurveSet.prototype.getCurveForUnsafeTime = function(unsafeTime) { - unsafeTime %= this.duration; - time = Math.floor(unsafeTime); - if (this.curves[time]) { - return this.curves[time]; - } - var curve; - for (var i = time; i >= 0; --i) { - curve = this.curves[i]; - if (curve) { - assert(i + curve.duration >= unsafeTime); - return curve; - } - } - // We must want the last one. There's always a last one, given how we - // construct the CurveSets; they're sparse, but the length gets set by adding - // elements at the end. - curve = this.curves[this.curves.length - 1]; - unsafeTime += this.duration; - assert(curve.startTime <= unsafeTime); - assert(curve.startTime + curve.duration > unsafeTime); - return curve; -}; - -/** - * Looks up what curve is active at a particular time. This is faster than - * getCurveForUnsafeTime, but can only be used if if a Curve starts precisely at - * unsafeTime % this.duration. - * @param {!number} time the time at which to check. - * @return {!Curve} the curve starting at time. - */ -CurveSet.prototype.getCurveForTime = function(time) { - return this.curves[time % this.duration]; -}; - -/** - * Convert the CurveSet to a string for debugging. - * @return {String} debugging output. - */ -CurveSet.prototype.toString = function() { - var s = 'Duration: ' + this.duration + '\n'; - for (var c in this.curves) { - s += this.curves[c].toString(); - } - return s; -}; - -/** - * Namespace object to hold the pure math functions. - * TODO: Consider just rolling these into the Pattern object, when it gets - * created. - */ -var JugglingMath = {}; - -/** - * Computes the greatest common devisor of integers a and b. - * @param {!number} a an integer. - * @param {!number} b an integer. - * @return {!number} the GCD of a and b. - */ -JugglingMath.computeGCD = function(a, b) { - assert(Math.round(a) == a); - assert(Math.round(b) == b); - assert(a >= 0); - assert(b >= 0); - if (!b) { - return a; - } else { - return JugglingMath.computeGCD(b, a % b); - } -} - -/** - * Computes the least common multiple of integers a and b, by making use of the - * fact that LCM(a, b) * GCD(a, b) == a * b. - * @param {!number} a an integer. - * @param {!number} b an integer. - * @return {!number} the LCM of a and b. - */ -JugglingMath.computeLCM = function(a, b) { - assert(Math.round(a) == a); - assert(Math.round(b) == b); - assert(a >= 0); - assert(b >= 0); - var ret = a * b / JugglingMath.computeGCD(a, b); - assert(Math.round(ret) == ret); - return ret; -} - -/** - * Given a Path and a set of hand positions, compute the corresponding set of - * Curves. - * @param {!Path} path the path of an object. - * @param {!Array.HandPositionRecord} handPositions the positions of the hands - * juggling the pattern containing the path. - * @return {!CurveSet} the full set of curves. - */ -CurveSet.getCurveSetFromPath = function(path, handPositions) { - var curves = []; - var pathLength = path.pathLength; - for (var index in path.descriptors) { - var descriptor = path.descriptors[index]; - var curve = descriptor.generateCurve(handPositions); - assert(!curves[curve.startTime]); - assert(curve.startTime < pathLength); - curves[curve.startTime] = curve; - } - return new CurveSet(pathLength, curves); -} - -/** - * Given a set of Paths and a set of hand positions, compute the corresponding - * CurveSets. - * @param {!Array.Path} paths the paths of a number of objects. - * @param {!Array.HandPositionRecord} handPositions the positions of the hands - * juggling the pattern containing the paths. - * @return {!Array.CurveSet} the CurveSets. - */ -CurveSet.getCurveSetsFromPaths = function(paths, handPositions) { - var curveSets = []; - for (var index in paths) { - var path = paths[index]; - curveSets[index] = CurveSet.getCurveSetFromPath(path, handPositions); - } - return curveSets; -} - -/** - * This is a temporary top-level calculation function that converts a standard - * asynchronous siteswap, expressed as a string of digits, into a full - * ready-to-animate set of CurveSets. Later on we'll be using an interface that - * can create a richer set of patterns than those expressable in the traditional - * string-of-ints format. - * @param {!String} patternString the siteswap. - * @param {!number} numHands the number of hands to use for the pattern. - * @param {!String} style how to space the hands ["pairs" or "even"]. - * @return {!Object} a fully-analyzed pattern as CurveSets and associated data. - */ -function computeFullPatternFromString(patternString, numHands, style) { - var patternAsStrings = patternString.split(/[ ,]+ */); - var patternSegment = []; - for (var index in patternAsStrings) { - if (patternAsStrings[index]) { // Beware extra whitespace at the ends. - patternSegment.push(parseInt(patternAsStrings[index])); - } - } - var pattern = []; - // Now expand the pattern out to the length of the LCM of pattern length and - // number of hands, so that each throw gets done in each of its incarnations. - var multiple = JugglingMath.computeLCM(patternSegment.length, numHands) / - patternSegment.length; - for (var i = 0; i < multiple; ++i) { - pattern = pattern.concat(patternSegment); - } - - var fullPattern = expandPattern(pattern, numHands); - var patternLength = fullPattern[0].length; - - var ballPaths = generateBallPaths(fullPattern); - var handPaths = generateHandPaths(ballPaths, numHands, patternLength); - - var handPositions = computeHandPositions(numHands, style); - var ballCurveSets = CurveSet.getCurveSetsFromPaths(ballPaths, handPositions); - var handCurveSets = CurveSet.getCurveSetsFromPaths(handPaths, handPositions); - - // Find the LCM of all the curveSet durations. This will be the length of the - // fully-expanded queue. We could expand to this before computing the - // CurveSets, but this way's probably just a little cheaper. - var lcmDuration = 1; - for (var i in ballCurveSets) { - var duration = ballCurveSets[i].duration; - if (duration > lcmDuration || lcmDuration % duration) { - lcmDuration = JugglingMath.computeLCM(lcmDuration, duration); - } - } - for (var i in handCurveSets) { - var duration = handCurveSets[i].duration; - if (duration > lcmDuration || lcmDuration % duration) { - lcmDuration = JugglingMath.computeLCM(lcmDuration, duration); - } - } - return { - numBalls: ballPaths.length, - numHands: handPaths.length, - duration: lcmDuration, - handCurveSets: handCurveSets, - ballCurveSets: ballCurveSets - } -} +// @@REWRITE(insert js-copyright) +// @@REWRITE(delete-start) +// Copyright 2009 Google Inc. All Rights Reserved +// @@REWRITE(delete-end) + +/** + * @fileoverview This file contains all the math for the siteswap animator. It + * handles all of the site-swap-related stuff [converting a sequence of integers + * into a more-useful representation of a pattern, pattern validation, etc.] as + * well as all the physics used for the simulation. + */ + +/** + * This is a container class that holds the coefficients of an equation + * describing the motion of an object. + * The basic equation is: + * f(x) := a t^2 + b t + c + d sin (f t) + e cos (f t). + * However, sometimes we LERP between that function and this one: + * g(x) := lA t^2 + lB t + lC + * lerpRate [so far] is always either 1 [LERP from f to g over 1 beat] or -1, + * [LERP from g to f over one beat]. + * + * Just plug in t to evaluate the equation. There's no JavaScript function to + * do this because it's always done on the GPU. + * + * @constructor + */ +EquationCoefficients = function(a, b, c, d, e, f, lA, lB, lC, lerpRate) { + assert(!isNaN(a) && !isNaN(b) && !isNaN(c)); + d = d || 0; + e = e || 0; + f = f || 0; + assert(!isNaN(d) && !isNaN(e) && !isNaN(f)); + lA = lA || 0; + lB = lB || 0; + lC = lC || 0; + assert(!isNaN(lA) && !isNaN(lB) && !isNaN(lC)); + lerpRate = lerpRate || 0; + this.a = a; + this.b = b; + this.c = c; + this.d = d; + this.e = e; + this.f = f; + this.lA = lA; + this.lB = lB; + this.lC = lC; + this.lerpRate = lerpRate; +} + +/** + * Create a new equation that's equivalent to this equation's coefficients a-f + * with a LERP to the polynomial portion of the supplied equation. + * @param {!EquationCoefficients} eqn the source of coefficients. + * @param {!number} lerpRate the rate and direction of the LERP; positive for + * "from this equation to the new one" and vice-versa. + * @return {!EquationCoefficients} a new set of coefficients. + */ +EquationCoefficients.prototype.lerpIn = function(eqn, lerpRate) { + assert(!this.lerpRate); + return new EquationCoefficients(this.a, this.b, this.c, this.d, this.e, + this.f, eqn.a, eqn.b, eqn.c, lerpRate); +}; + +/** + * Convert the EquationCoefficients to a string for debugging. + * @return {String} debugging output. + */ +EquationCoefficients.prototype.toString = function() { + return 'F(t) := ' + this.a.toFixed(2) + ' t^2 + ' + this.b.toFixed(2) + + ' t + ' + this.c.toFixed(2) + ' + ' + + this.d.toFixed(2) + ' sin(' + this.f.toFixed(2) + ' t) + ' + + this.e.toFixed(2) + ' cos(' + this.f.toFixed(2) + ' t) + LERP(' + + this.lerpRate.toFixed(2) + ') of ' + + this.lA.toFixed(2) + ' t^2 + ' + this.lB.toFixed(2) + + ' t + ' + this.lC.toFixed(2); +}; + +/** + * A set of equations which describe the motion of an object over time. + * The three equations each supply one dimension of the motion, and the curve is + * valid from startTime to startTime + duration. + * @param {!number} startTime the initial time at which the curve is valid. + * @param {!number} duration how long [in beats] the curve is valid. + * @param {!EquationCoefficients} xEqn the equation for motion in x. + * @param {!EquationCoefficients} yEqn the equation for motion in y. + * @param {!EquationCoefficients} zEqn the equation for motion in z. + * @constructor + */ +Curve = function(startTime, duration, xEqn, yEqn, zEqn) { + this.startTime = startTime; + this.duration = duration; + this.xEqn = xEqn; + this.yEqn = yEqn; + this.zEqn = zEqn; +} + +/** + * Convert the Curve to a string for debugging. + * @return {String} debugging output. + */ +Curve.prototype.toString = function() { + var s = 'startTime: ' + this.startTime + '\n'; + s += 'duration: ' + this.duration + '\n'; + s += this.xEqn + '\n'; + s += this.yEqn + '\n'; + s += this.zEqn + '\n'; + return s; +}; + +/** + * Modify this curve's coefficients to include a LERP to the polynomial + * portion of the supplied curve. + * @param {!Curve} curve the source of coefficients. + * @param {!number} lerpRate the rate and direction of the LERP; positive for + * "from this equation to the new one" and vice-versa. + * @return {!Curve} a new curve. + */ +Curve.prototype.lerpIn = function(curve, lerpRate) { + assert(this.startTime == curve.startTime); + assert(this.duration == curve.duration); + var xEqn = this.xEqn.lerpIn(curve.xEqn, lerpRate); + var yEqn = this.yEqn.lerpIn(curve.yEqn, lerpRate); + var zEqn = this.zEqn.lerpIn(curve.zEqn, lerpRate); + return new Curve(this.startTime, this.duration, xEqn, yEqn, zEqn); +}; + +/** + * Produce a set of polynomial coefficients that describe linear motion between + * two points in 1 dimension. + * @param {!number} startPos the starting position. + * @param {!number} endPos the ending position. + * @param {!number} duration how long the motion takes. + * @return {!EquationCoefficients} the equation for the motion. + */ +Curve.computeLinearCoefficients = function(startPos, endPos, duration) { + return new EquationCoefficients( + 0, (endPos - startPos) / duration, startPos); +} + +var GRAVITY = 1; // Higher means higher throws for the same duration. +/** + * Produce a set of polynomial coefficients that describe parabolic motion + * between two points in 1 dimension. + * @param {!number} startPos the starting position. + * @param {!number} endPos the ending position. + * @param {!number} duration how long the motion takes. + * @return {!EquationCoefficients} the equation for the motion. + */ +Curve.computeParabolicCoefficients = function(startPos, endPos, duration) { + var dY = endPos - startPos; + return new EquationCoefficients(-GRAVITY / 2, + dY / duration + GRAVITY * duration / 2, + startPos); +} + +/** + * Compute the curve taken by a ball given its throw and catch positions, the + * time it was thrown, and how long it stayed in the air. + * + * We use duration rather than throwTime and catchTime because, what + * with the modular arithmetic used in our records, catchTime might be before + * throwTime, and in some representations the pattern could wrap around a few + * times while the ball's in the air. When the parabola computed here is used, + * time must be supplied as an offset from the time of the throw, and must of + * course not wrap at all. That is, these coefficients work for f(0) == + * throwPos, f(duration) == catchPos. + * + * We treat the y axis as vertical and thus affected by gravity. + * + * @param {!EquationCoefficients} throwPos + * @param {!EquationCoefficients} catchPos + * @param {!number} startTime + * @param {!number} duration + * @return {!Curve} + */ +Curve.computeThrowCurve = function(throwPos, catchPos, startTime, duration) { + var xEqn = Curve.computeLinearCoefficients(throwPos.x, catchPos.x, duration); + var yEqn = Curve.computeParabolicCoefficients(throwPos.y, catchPos.y, + duration); + var zEqn = Curve.computeLinearCoefficients(throwPos.z, catchPos.z, duration); + return new Curve(startTime, duration, xEqn, yEqn, zEqn); +} + +/** + * Compute a straight line Curve given start and end positions, the start time, + * and the duration of the motion. + * + * @param {!EquationCoefficients} startPos + * @param {!EquationCoefficients} endPos + * @param {!number} startTime + * @param {!number} duration + * @return {!Curve} + */ +Curve.computeStraightLineCurve = + function(startPos, endPos, startTime, duration) { + var xEqn = Curve.computeLinearCoefficients(startPos.x, endPos.x, duration); + var yEqn = Curve.computeLinearCoefficients(startPos.y, endPos.y, duration); + var zEqn = Curve.computeLinearCoefficients(startPos.z, endPos.z, duration); + return new Curve(startTime, duration, xEqn, yEqn, zEqn); +} + +/** + * Threshold horizontal distance below which computeCircularCurve won't bother + * trying to approximate a circular curve. See the comment above + * computeCircularCurve for more info. + * @type {number} + */ +Curve.EPSILON = .0001; + +/** + * Compute a circular curve, used as an approximation for the motion of a hand + * between a catch and its following throw. + * + * Assumes a lot of stuff about this looking like a "normal" throw: the catch is + * moving roughly the opposite direction as the throw, the throw and catch + * aren't at the same place, and such. Otherwise this looks very odd at best. + * This is used for the height of the curve. + * This produces coefficients for d sin(f t) + e cos(f t) for each of x, y, z. + * It produces a vertical-ish circular curve from the start to the end, going + * down, then up. So if dV [the distance from the start to finish in the x-z + * plane, ignoring y] is less than Curve.EPSILON, it doesn't know which way down + * is, and it bails by returning a straight line instead. + */ +Curve.computeCircularCurve = function(startPos, endPos, startTime, duration) { + var dX = endPos.x - startPos.x; + var dY = endPos.y - startPos.y; + var dZ = endPos.z - startPos.z; + var dV = Math.sqrt(dX * dX + dZ * dZ); + if (dV < Curve.EPSILON) { + return Curve.computeStraightLineCurve(startPos, endPos, startTime, + duration); + } + var negHalfdV = -0.5 * dV; + var negHalfdY = -0.5 * dY; + var f = Math.PI / duration; + var yEqn = new EquationCoefficients( + 0, 0, startPos.y + dY / 2, + negHalfdV, negHalfdY, f); + var ratio = dX / dV; + var xEqn = new EquationCoefficients( + 0, 0, startPos.x + dX / 2, + negHalfdY * ratio, negHalfdV * ratio, f); + ratio = dZ / dV; + var zEqn = new EquationCoefficients( + 0, 0, startPos.z + dZ / 2, + negHalfdY * ratio, negHalfdV * ratio, f); + return new Curve(startTime, duration, xEqn, yEqn, zEqn); +} + +/** + * This is the abstract base class for an object that describes a throw, catch, + * or empty hand [placeholder] in a site-swap pattern. + * @constructor + */ +Descriptor = function() { +} + +/** + * Create an otherwise-identical copy of this descriptor at a given time offset. + * Note that offset may put time past patternLength; the caller will have to fix + * this up manually. + * @param {number} offset how many beats to offset the new descriptor. + * Derived classes must override this function. + */ +Descriptor.prototype.clone = function(offset) { + throw new Error('Unimplemented.'); +}; + +/** + * Generate the Curve implied by this descriptor and the supplied hand + * positions. + * @param {!Array.HandPositionRecord} handPositions where the hands will be. + * Derived classes must override this function. + */ +Descriptor.prototype.generateCurve = function(handPositions) { + throw new Error('Unimplemented.'); +}; + +/** + * Adjust the start time of this Descriptor to be in [0, pathLength). + * @param {!number} pathLength the duration of a path, in beats. + * @return {!Descriptor} this. + */ +Descriptor.prototype.fixUpModPathLength = function(pathLength) { + this.time = this.time % pathLength; + return this; +}; + +/** + * This describes a throw in a site-swap pattern. + * @param {!number} throwNum the site-swap number of the throw. + * @param {!number} throwTime the time this throw occurs. + * @param {!number} sourceHand the index of the throwing hand. + * @param {!number} destHand the index of the catching hand. + * @constructor + */ +ThrowDescriptor = function(throwNum, throwTime, sourceHand, destHand) { + this.throwNum = throwNum; + this.sourceHand = sourceHand; + this.destHand = destHand; + this.time = throwTime; +} + +/** + * This is a subclass of Descriptor. + */ +ThrowDescriptor.prototype = new Descriptor(); + +/** + * Set up the constructor, just to be neat. + */ +ThrowDescriptor.prototype.constructor = ThrowDescriptor; + +/** + * We label each Descriptor subclass with a type for debugging. + */ +ThrowDescriptor.prototype.type = 'THROW'; + +/** + * Create an otherwise-identical copy of this descriptor at a given time offset. + * Note that offset may put time past patternLength; the caller will have to fix + * this up manually. + * @param {number} offset how many beats to offset the new descriptor. + * @return {!Descriptor} the new copy. + */ +ThrowDescriptor.prototype.clone = function(offset) { + offset = offset || 0; // Turn null into 0. + return new ThrowDescriptor(this.throwNum, this.time + offset, + this.sourceHand, this.destHand); +}; + +/** + * Convert the ThrowDescriptor to a string for debugging. + * @return {String} debugging output. + */ +ThrowDescriptor.prototype.toString = function() { + return '(' + this.throwNum + ' from hand ' + this.sourceHand + ' to hand ' + + this.destHand + ')'; +}; + +/** + * Generate the Curve implied by this descriptor and the supplied hand + * positions. + * @param {!Array.HandPositionRecord} handPositions where the hands will be. + * @return {!Curve} the curve. + */ +ThrowDescriptor.prototype.generateCurve = function(handPositions) { + var startPos = handPositions[this.sourceHand].throwPositions[this.destHand]; + var endPos = handPositions[this.destHand].catchPosition; + return Curve.computeThrowCurve(startPos, endPos, this.time, + this.throwNum - 1); }; + +/** + * This describes a catch in a site-swap pattern. + * @param {!number} hand the index of the catching hand. + * @param {!number} sourceThrowNum the site-swap number of the preceeding throw. + * @param {!number} destThrowNum the site-swap number of the following throw. + * @param {!number} sourceHand the index of the hand throwing the source throw. + * @param {!number} destHand the index of the hand catching the following throw. + * @param {!number} catchTime the time at which the catch occurs. + * @constructor + */ +CarryDescriptor = function(hand, sourceThrowNum, destThrowNum, sourceHand, + destHand, catchTime) { + this.hand = hand; + this.sourceThrowNum = sourceThrowNum; + this.destThrowNum = destThrowNum; + this.sourceHand = sourceHand; + this.destHand = destHand; + this.time = catchTime; +} + +/** + * This is a subclass of Descriptor. + */ +CarryDescriptor.prototype = new Descriptor(); + +/** + * Set up the constructor, just to be neat. + */ +CarryDescriptor.prototype.constructor = CarryDescriptor; + +/** + * We label each Descriptor subclass with a type for debugging. + */ +CarryDescriptor.prototype.type = 'CARRY'; + +/** + * Since this gets pathLength, not patternLength, we'll have to collapse sets + * of CarryDescriptors later, as they may be spread sparsely through the full + * animation and we'll only want them to be distributed over the full pattern + * length. We may have dupes to throw away as well. + * @param {!ThrowDescriptor} inThrowDescriptor + * @param {!ThrowDescriptor} outThrowDescriptor + * @param {!number} pathLength + * @return {!CarryDescriptor} + */ +CarryDescriptor.fromThrowDescriptors = function(inThrowDescriptor, + outThrowDescriptor, pathLength) { + assert(inThrowDescriptor.destHand == outThrowDescriptor.sourceHand); + assert((inThrowDescriptor.time + inThrowDescriptor.throwNum) % + pathLength == outThrowDescriptor.time); + return new CarryDescriptor(inThrowDescriptor.destHand, + inThrowDescriptor.throwNum, outThrowDescriptor.throwNum, + inThrowDescriptor.sourceHand, outThrowDescriptor.destHand, + (outThrowDescriptor.time + pathLength - 1) % pathLength); +}; + +/** + * Create an otherwise-identical copy of this descriptor at a given time offset. + * Note that offset may put time past patternLength; the caller will have to fix + * this up manually. + * @param {number} offset how many beats to offset the new descriptor. + * @return {!Descriptor} the new copy. + */ +CarryDescriptor.prototype.clone = function(offset) { + offset = offset || 0; // Turn null into 0. + return new CarryDescriptor(this.hand, this.sourceThrowNum, + this.destThrowNum, this.sourceHand, this.destHand, this.time + offset); +}; + +/** + * Convert the CarryDescriptor to a string for debugging. + * @return {String} debugging output. + */ +CarryDescriptor.prototype.toString = function() { + return 'time: ' + this.time + ' (hand ' + this.hand + ' catches ' + + this.sourceThrowNum + ' from hand ' + this.sourceHand + ' then throws ' + + this.destThrowNum + ' to hand ' + this.destHand + ')'; +}; + +/** + * Test if this CarryDescriptor is equivalent to another, mod patternLength. + * @param {!CarryDescriptor} cd the other CarryDescriptor. + * @param {!number} patternLength the length of the pattern. + * @return {!bool} + */ +CarryDescriptor.prototype.equalsWithMod = function(cd, patternLength) { + if (!(cd instanceof CarryDescriptor)) { + return false; + } + if (this.hand != cd.hand) { + return false; + } + if (this.sourceThrowNum != cd.sourceThrowNum) { + return false; + } + if (this.destThrowNum != cd.destThrowNum) { + return false; + } + if (this.sourceHand != cd.sourceHand) { + return false; + } + if (this.destHand != cd.destHand) { + return false; + } + if (this.time % patternLength != cd.time % patternLength) { + return false; + } + return true; +}; + +/** + * Generate the Curve implied by this descriptor and the supplied hand + * positions. + * @param {!Array.HandPositionRecord} handPositions where the hands will be. + * @return {!Curve} the curve. + */ +CarryDescriptor.prototype.generateCurve = function(handPositions) { + var startPos = handPositions[this.hand].catchPosition; + var endPos = handPositions[this.hand].throwPositions[this.destHand]; + return Curve.computeCircularCurve(startPos, endPos, this.time, 1); +}; + +/** + * This describes a carry of a "1" in a site-swap pattern. + * The flags isThrow and isCatch tell whether this is the actual 1 [isThrow] or + * the carry that receives the handoff [isCatch]. It's legal for both to be + * true, which happens when there are two 1s in a row. + * @param {!number} sourceThrowNum the site-swap number of the prev throw + * [including this one if isCatch]. + * @param {!number} sourceHand the index of the hand throwing sourceThrowNum. + * @param {!number} destThrowNum the site-swap number of the next throw + * [including this one if isThrow]. + * @param {!number} destHand the index of the hand catching destThrowNum. + * @param {!number} hand the index of the hand doing this carry. + * @param {!number} time the time at which the carry starts. + * @param {!bool} isThrow whether this is a 1. + * @param {!bool} isCatch whether this is the carry after a 1. + * @constructor + */ +CarryOneDescriptor = function(sourceThrowNum, sourceHand, destThrowNum, + destHand, hand, time, isThrow, isCatch) { + // It's possible to have !isCatch with sourceThrowNum == 1 temporarily, if we + // just haven't handled that 1 yet [we're doing the throw of this one, and + // will later get to the previous one, due to wraparound], and vice-versa. + assert(isThrow || (sourceThrowNum == 1)); + assert(isCatch || (destThrowNum == 1)); + this.sourceThrowNum = sourceThrowNum; + this.sourceHand = sourceHand; + this.destHand = destHand; + this.destThrowNum = destThrowNum; + this.hand = hand; + this.time = time; + this.isThrow = isThrow; + this.isCatch = isCatch; + return this; +} + +/** + * This is a subclass of Descriptor. + */ +CarryOneDescriptor.prototype = new Descriptor(); + +/** + * Set up the constructor, just to be neat. + */ +CarryOneDescriptor.prototype.constructor = CarryOneDescriptor; + +/** + * We label each Descriptor subclass with a type for debugging. + */ +CarryOneDescriptor.prototype.type = 'CARRY_ONE'; + +/** + * Create a pair of CarryOneDescriptors to describe the carry that is a throw of + * 1. A 1 spends all its time being carried, so these two carries surrounding + * it represent [and therefore don't have] a throw between them. + * Prev and post are generally the ordinary CarryDescriptors surrounding the + * throw of 1 that we're trying to implement. However, they could each [or + * both] independently be CarryOneDescriptors implementing other 1 throws. + * @param {!Descriptor} prev the carry descriptor previous to the 1. + * @param {!Descriptor} post the carry descriptor subsequent to the 1. + * @return {!Array.CarryOneDescriptor} a pair of CarryOneDescriptors. + */ +CarryOneDescriptor.getDescriptorPair = function(prev, post) { + assert(prev instanceof CarryDescriptor || prev instanceof CarryOneDescriptor); + assert(post instanceof CarryDescriptor || post instanceof CarryOneDescriptor); + assert(prev.destHand == post.hand); + assert(prev.hand == post.sourceHand); + var newPrev; + var newPost; + if (prev instanceof CarryOneDescriptor) { + assert(prev.isCatch && !prev.isThrow); + newPrev = prev; + newPrev.isThrow = true; + assert(newPrev.destHand == post.hand); + } else { + newPrev = new CarryOneDescriptor(prev.sourceThrowNum, prev.sourceHand, 1, + post.hand, prev.hand, prev.time, true, false); + } + if (post instanceof CarryOneDescriptor) { + assert(post.isThrow && !post.isCatch); + newPost = post; + newPost.isCatch = true; + assert(newPost.sourceHand == prev.hand); + assert(newPost.sourceThrowNum == 1); + } else { + newPost = new CarryOneDescriptor(1, prev.hand, post.destThrowNum, + post.destHand, post.hand, post.time, false, true); + } + return [newPrev, newPost]; +}; + +/** + * Convert the CarryOneDescriptor to a string for debugging. + * @return {String} debugging output. + */ +CarryOneDescriptor.prototype.toString = function() { + var s; + if (this.isThrow) { + s = 'Hand ' + this.hand + ' catches a ' + this.sourceThrowNum + ' from ' + + this.sourceHand + ' at time ' + this.time + ' and then passes a 1 to ' + + this.destHand + '.'; + } else { + assert(this.isCatch && this.sourceThrowNum == 1); + s = 'Hand ' + this.hand + ' catches a 1 from ' + this.sourceHand + + ' at time ' + this.time + ' and then passes a ' + this.destThrowNum + + ' to ' + this.destHand + '.'; + } + return s; +}; + +/** + * Compute the curve taken by a ball during the carry representing a 1, as long + * as it's not both a catch and a throw of a 1, which is handled elsewhere. + * It's either a LERP from a circular curve [a catch of a throw > 1] to a + * straight line to the handoff point [for isThrow] or a LERP from a straight + * line from the handoff to a circular curve for the next throw > 1 [for + * isCatch]. + * + * @param {!EquationCoefficients} catchPos + * @param {!EquationCoefficients} throwPos + * @param {!EquationCoefficients} handoffPos + * @param {!number} startTime + * @param {!bool} isCatch whether this is the carry after a 1. + * @param {!bool} isThrow whether this is a 1. + * @return {!Curve} + */ +Curve.computeCarryOneCurve = function(catchPos, throwPos, handoffPos, startTime, + isCatch, isThrow) { + assert(!isCatch != !isThrow); + var curve = Curve.computeCircularCurve(catchPos, throwPos, startTime, 1); + var curve2 = Curve.computeStraightLineCurve(handoffPos, handoffPos, + startTime, 1); + return curve.lerpIn(curve2, isThrow ? 1 : -1); +} + +/** + * Compute the curve taken by a ball during the carry representing a 1 that is + * both the catch of one 1 and the immediately-following throw of another 1. + * + * @param {!EquationCoefficients} leadingHandoffPos + * @param {!EquationCoefficients} trailingHandoffPos + * @param {!Array.HandPositionRecord} handPositions where the hands will be. + * @param {!number} hand + * @param {!number} time the time at which the first 1's catch takes place. + * @return {!Curve} + */ +Curve.computeConsecutiveCarryOneCurve = function(leadingHandoffPos, + trailingHandoffPos, handPositions, hand, time) { + var curve = Curve.computeStraightLineCurve(leadingHandoffPos, + handPositions[hand].basePosition, time, 1); + var curve2 = + Curve.computeStraightLineCurve(handPositions[hand].basePosition, + trailingHandoffPos, time, 1); + return curve.lerpIn(curve2, 1); +} + +/** + * Generate the Curve implied by this descriptor and the supplied hand + * positions. + * @param {!Array.HandPositionRecord} handPositions where the hands will be. + * @return {!Curve} the curve. + */ +CarryOneDescriptor.prototype.generateCurve = function(handPositions) { + var leadingHandoffPos, trailingHandoffPos; + if (this.isCatch) { + var p0 = handPositions[this.hand].basePosition; + var p1 = handPositions[this.sourceHand].basePosition; + handoffPos = leadingHandoffPos = p0.add(p1).scale(0.5); + } + if (this.isThrow) { + var p0 = handPositions[this.hand].basePosition; + var p1 = handPositions[this.destHand].basePosition; + handoffPos = trailingHandoffPos = p0.add(p1).scale(0.5); + } + if (!this.isCatch || !this.isThrow) { + return Curve.computeCarryOneCurve(handPositions[this.hand].catchPosition, + handPositions[this.hand].throwPositions[this.destHand], handoffPos, + this.time, this.isCatch, this.isThrow); + } else { + return Curve.computeConsecutiveCarryOneCurve(leadingHandoffPos, + trailingHandoffPos, handPositions, this.hand, this.time); + } +}; + +/** + * Create an otherwise-identical copy of this descriptor at a given time offset. + * Note that offset may put time past patternLength; the caller will have to fix + * this up manually. + * @param {number} offset how many beats to offset the new descriptor. + * @return {!Descriptor} the new copy. + */ +CarryOneDescriptor.prototype.clone = function(offset) { + offset = offset || 0; // Turn null into 0. + return new CarryOneDescriptor(this.sourceThrowNum, this.sourceHand, + this.destThrowNum, this.destHand, this.hand, this.time + offset, + this.isThrow, this.isCatch); +}; + +/** + * Test if this CarryOneDescriptor is equivalent to another, mod patternLength. + * @param {!CarryOneDescriptor} cd the other CarryOneDescriptor. + * @param {!number} patternLength the length of the pattern. + * @return {!bool} + */ +CarryOneDescriptor.prototype.equalsWithMod = function(cd, patternLength) { + if (!(cd instanceof CarryOneDescriptor)) { + return false; + } + if (this.hand != cd.hand) { + return false; + } + if (this.sourceThrowNum != cd.sourceThrowNum) { + return false; + } + if (this.destThrowNum != cd.destThrowNum) { + return false; + } + if (this.sourceHand != cd.sourceHand) { + return false; + } + if (this.destHand != cd.destHand) { + return false; + } + if (this.isCatch != cd.isCatch) { + return false; + } + if (this.isThrow != cd.isThrow) { + return false; + } + if (this.time % patternLength != cd.time % patternLength) { + return false; + } + return true; +}; + +/** + * This describes an empty hand in a site-swap pattern. + * @param {!Descriptor} cd0 the CarryDescriptor or CarryOneDescriptor describing + * this hand immediately before it was emptied. + * @param {!Descriptor} cd1 the CarryDescriptor or CarryOneDescriptor describing + * this hand immediately after it's done being empty. + * @param {!number} patternLength the length of the pattern. + * @constructor + */ +EmptyHandDescriptor = function(cd0, cd1, patternLength) { + assert(cd0.hand == cd1.hand); + this.hand = cd0.hand; + this.prevThrowDest = cd0.destHand; + this.sourceThrowNum = cd0.destThrowNum; + this.nextCatchSource = cd1.sourceHand; + this.destThrowNum = cd1.sourceThrowNum; + // This code assumes that each CarryDescriptor and CarryOneDescriptor always + // has a duration of 1 beat. If we want to be able to allow long-held balls + // [instead of thrown twos, for example], we'll have to fix that here and a + // number of other places. + this.time = (cd0.time + 1) % patternLength; + this.duration = cd1.time - this.time; + if (this.duration < 0) { + this.duration += patternLength; + assert(this.duration > 0); + } +} + +/** + * This is a subclass of Descriptor. + */ +EmptyHandDescriptor.prototype = new Descriptor(); + +/** + * Set up the constructor, just to be neat. + */ +EmptyHandDescriptor.prototype.constructor = EmptyHandDescriptor; + +/** + * We label each Descriptor subclass with a type for debugging. + */ +EmptyHandDescriptor.prototype.type = 'EMPTY'; + +/** + * Convert the EmptyHandDescriptor to a string for debugging. + * @return {String} debugging output. + */ +EmptyHandDescriptor.prototype.toString = function() { + return 'time: ' + this.time + ' for ' + this.duration + ' (hand ' + + this.hand + ', after throwing a ' + this.sourceThrowNum + ' to hand ' + + this.prevThrowDest + ' then catches a ' + this.destThrowNum + + ' from hand ' + this.nextCatchSource + ')'; +}; + +/** + * Generate the Curve implied by this descriptor and the supplied hand + * positions. + * @param {!Array.HandPositionRecord} handPositions where the hands will be. + * @return {!Curve} the curve. + */ +EmptyHandDescriptor.prototype.generateCurve = function(handPositions) { + var startPos, endPos; + if (this.sourceThrowNum == 1) { + var p0 = handPositions[this.hand].basePosition; + var p1 = handPositions[this.prevThrowDest].basePosition; + startPos = p0.add(p1).scale(0.5); + } else { + startPos = handPositions[this.hand].throwPositions[this.prevThrowDest]; + } + if (this.destThrowNum == 1) { + var p0 = handPositions[this.hand].basePosition; + var p1 = handPositions[this.nextCatchSource].basePosition; + endPos = p0.add(p1).scale(0.5); + } else { + endPos = handPositions[this.hand].catchPosition; + } + // TODO: Replace with a good empty-hand curve. + return Curve.computeStraightLineCurve(startPos, endPos, this.time, + this.duration); +}; + +/** + * A series of descriptors that describes the full path of an object during a + * pattern. + * @param {!Array.Descriptor} descriptors all descriptors for the object. + * @param {!number} pathLength the length of the path in beats. + * @constructor + */ +Path = function(descriptors, pathLength) { + this.descriptors = descriptors; + this.pathLength = pathLength; +} + +/** + * Create a Path representing a ball, filling in the gaps between the throws + * with carry descriptors. Since it's a ball's path, there are no + * EmptyHandDescriptors in the output. + * @param {!Array.ThrowDescriptor} throwDescriptors the ball's part of the + * pattern. + * @param {!number} pathLength the length of the pattern in beats. + * @return {!Path} the ball's full path. + */ +Path.ballPathFromThrowDescriptors = function(throwDescriptors, pathLength) { + return new Path( + Path.createDescriptorList(throwDescriptors, pathLength), pathLength); +}; + +/** + * Create the sequence of ThrowDescriptors, CarryDescriptors, and + * CarryOneDescriptor describing the path of a ball through a pattern. + * A sequence such as (h j k) generally maps to an alternating series of throw + * and carry descriptors [Th Chj Tj Cjk Tk Ck? ...]. However, when j is a 1, + * you remove the throw descriptor and modify the previous and subsequent carry + * descriptors, since the throw descriptor has zero duration and the carry + * descriptors need to take into account the handoff. + * @param {!Array.ThrowDescriptor} throwDescriptors the ball's part of the + * pattern. + * @param {!number} pathLength the length of the pattern in beats. + * @return {!Array.Descriptor} the full set of descriptors for the ball. + */ +Path.createDescriptorList = function(throwDescriptors, pathLength) { + var descriptors = []; + var prevThrow; + for (var index in throwDescriptors) { + var td = throwDescriptors[index]; + if (prevThrow) { + descriptors.push( + CarryDescriptor.fromThrowDescriptors(prevThrow, td, pathLength)); + } // Else it's handled after the loop. + descriptors.push(td); + prevThrow = td; + } + descriptors.push( + CarryDescriptor.fromThrowDescriptors(prevThrow, throwDescriptors[0], + pathLength)); + // Now post-process to take care of throws of 1. It's easier to do it here + // than during construction since we can now assume that the previous and + // subsequent carry descriptors are already in place [modulo pathLength]. + for (var i = 0; i < descriptors.length; ++i) { + var descriptor = descriptors[i]; + if (descriptor instanceof ThrowDescriptor) { + if (descriptor.throwNum == 1) { + var prevIndex = (i + descriptors.length - 1) % descriptors.length; + var postIndex = (i + 1) % descriptors.length; + var replacements = CarryOneDescriptor.getDescriptorPair( + descriptors[prevIndex], descriptors[postIndex]); + descriptors[prevIndex] = replacements[0]; + descriptors[postIndex] = replacements[1]; + descriptors.splice(i, 1); + // We've removed a descriptor from the array, but since we can never + // have 2 ThrowDescriptors in a row, we don't need to decrement i. + } + } + } + return descriptors; +}; + +/** + * Convert the Path to a string for debugging. + * @return {String} debugging output. + */ +Path.prototype.toString = function() { + var ret = 'pathLength is ' + this.pathLength + '; ['; + for (var index in this.descriptors) { + ret += this.descriptors[index].toString(); + } + ret += ']'; + return ret; +}; + +/** + * Create an otherwise-identical copy of this path at a given time offset. + * Note that offset may put time references in the Path past the length of the + * pattern. The caller must fix this up manually. + * @param {number} offset how many beats to offset the new Path. + * @return {!Path} the new copy. + */ +Path.prototype.clone = function(offset) { + offset = offset || 0; // Turn null into 0. + var descriptors = []; + for (var index in this.descriptors) { + descriptors.push(this.descriptors[index].clone(offset)); + } + return new Path(descriptors, this.pathLength); +}; + +/** + * Adjust the start time of all descriptors to be in [0, pathLength) via modular + * arithmetic. Reorder the array such that they're sorted in increasing order + * of time. + * @return {!Path} this. + */ +Path.prototype.fixUpModPathLength = function() { + var splitIndex; + var prevTime = 0; + for (var index in this.descriptors) { + var d = this.descriptors[index]; + d.fixUpModPathLength(this.pathLength); + if (d.time < prevTime) { + assert(null == splitIndex); + splitIndex = index; // From here to the end should move to the start. + } + prevTime = d.time; + } + if (null != splitIndex) { + var temp = this.descriptors.slice(splitIndex); + this.descriptors.length = splitIndex; + this.descriptors = temp.concat(this.descriptors); + } + return this; +}; + +/** + * Take a standard asynch siteswap pattern [expressed as an array of ints] and + * a number of hands, and expand it into a 2D grid of ThrowDescriptors with one + * row per hand. + * Non-asynch patterns are more complicated, since their linear forms aren't + * fully-specified, so we don't handle them here. + * You'll want to expand your pattern to the LCM of numHands and minimal pattern + * length before calling this. + * The basic approach doesn't really work for one-handed patterns. It ends up + * with catches and throws happening at the same time [having removed all + * empty-hand time in between them]. To fix this, we double all throw heights + * and space them out, as if doing a two-handed pattern with all zeroes from the + * other hand. Yes, this points out that the overall approach we're taking is a + * bit odd [since you end up with hands empty for time proportional to the + * number of hands], but you have to make some sort of assumptions to generalize + * siteswaps to N hands, and that's what I chose. + * @param {!Array.number} pattern an asynch siteswap pattern. + * @param {!number} numHands the number of hands. + * @return {!Array.Array.ThrowDescriptor} the expanded pattern. + */ +function expandPattern(pattern, numHands) { + var fullPattern = []; + assert(numHands > 0); + if (numHands == 1) { + numHands = 2; + var temp = []; + for (var i = 0; i < pattern.length; ++i) { + temp[2 * i] = 2 * pattern[i]; + temp[2 * i + 1] = 0; + } + pattern = temp; + } + for (var hand = 0; hand < numHands; ++hand) { + fullPattern[hand] = []; + } + for (var time = 0; time < pattern.length; ++time) { + for (var hand = 0; hand < numHands; ++hand) { + var t; + if (hand == time % numHands) { + t = new ThrowDescriptor(pattern[time], time, hand, + (hand + pattern[time]) % numHands); + } else { + // These are ignored during analysis, so they don't appear in BallPaths. + t = new ThrowDescriptor(0, time, hand, hand); + } + fullPattern[hand].push(t); + } + } + return fullPattern; +} + +// TODO: Wrap the final pattern in a class, then make the remaining few global +// functions be members of that class to clean up the global namespace. + +/** + * Given a valid site-swap for a nonzero number of balls, stored as an expanded + * pattern array-of-arrays, with pattern length the LCM of hands and minimal + * pattern length, produce Paths for all the balls. + * @param {!Array.Array.ThrowDescriptor} pattern a valid pattern. + * @return {!Array.Path} the paths of all the balls. + */ +function generateBallPaths(pattern) { + var numHands = pattern.length; + assert(numHands > 0); + var patternLength = pattern[0].length; + assert(patternLength > 0); + var sum = 0; + for (var hand in pattern) { + for (var time in pattern[hand]) { + sum += pattern[hand][time].throwNum; + } + } + var numBalls = sum / patternLength; + assert(numBalls == Math.round(numBalls)); + assert(numBalls > 0); + + var ballsToAllocate = numBalls; + var ballPaths = []; + // NOTE: The indices of locationsChecked are reversed from those of pattern + // for simplicity of allocation. This might be worth flipping to match. + var locationsChecked = []; + for (var time = 0; time < patternLength && ballsToAllocate; ++time) { + locationsChecked[time] = locationsChecked[time] || []; + for (var hand = 0; hand < numHands && ballsToAllocate; ++hand) { + if (locationsChecked[time][hand]) { + continue; + } + var curThrowDesc = pattern[hand][time]; + var curThrow = curThrowDesc.throwNum; + if (!curThrow) { + assert(curThrow === 0); + continue; + } + var throwDescriptors = []; + var curTime = time; + var curHand = hand; + var wraps = 0; + do { + if (!locationsChecked[curTime]) { + locationsChecked[curTime] = []; + } + assert(!locationsChecked[curTime][curHand]); + locationsChecked[curTime][curHand] = true; + // We copy curThrowDesc here, adding wraps * patternLength, to get + // the true throw time relative to offset. Later we'll add in offset + // when we clone again, then mod by pathLength. + throwDescriptors.push(curThrowDesc.clone(wraps * patternLength)); + var nextThrowTime = curThrow + curTime; + wraps += Math.floor(nextThrowTime / patternLength); + curTime = nextThrowTime % patternLength; + assert(curTime >= time); // Else we'd have covered it earlier. + curHand = curThrowDesc.destHand; + var tempThrowDesc = curThrowDesc; + curThrowDesc = pattern[curHand][curTime]; + curThrow = curThrowDesc.throwNum; + assert(tempThrowDesc.destHand == curThrowDesc.sourceHand); + assert(curThrowDesc.time == + (tempThrowDesc.throwNum + tempThrowDesc.time) % patternLength); + } while (curTime != time || curHand != hand); + var pathLength = wraps * patternLength; + var ballPath = + Path.ballPathFromThrowDescriptors(throwDescriptors, pathLength); + for (var i = 0; i < wraps; ++i) { + var offset = i * patternLength % pathLength; + ballPaths.push(ballPath.clone(offset, pathLength).fixUpModPathLength()); + } + ballsToAllocate -= wraps; + assert(ballsToAllocate >= 0); + } + } + return ballPaths; +} + +/** + * Given an array of ball paths, produce the corresponding set of hand paths. + * @param {!Array.Path} ballPaths the Paths of all the balls in the pattern. + * @param {!number} numHands how many hands to use in the pattern. + * @param {!number} patternLength the length, in beats, of the pattern. + * @return {!Array.Path} the paths of all the hands. + */ +function generateHandPaths(ballPaths, numHands, patternLength) { + assert(numHands > 0); + assert(patternLength > 0); + var handRecords = []; // One record per hand. + for (var idxBR in ballPaths) { + var descriptors = ballPaths[idxBR].descriptors; + for (var idxD in descriptors) { + var descriptor = descriptors[idxD]; + // TODO: Fix likely needed for throws of 1. + if (!(descriptor instanceof ThrowDescriptor)) { + // It's a CarryDescriptor or a CarryOneDescriptor. + var hand = descriptor.hand; + if (!handRecords[hand]) { + handRecords[hand] = []; + } + // TODO: Should we not shorten stuff here if we're going to lengthen + // everything later anyway? Is there a risk of inconsistency due to + // ball paths of different lengths? + var catchTime = descriptor.time % patternLength; + if (!handRecords[hand][catchTime]) { + // We pass in this offset to set the new descriptor's time to + // catchTime, so as to keep it within [0, patternLength). + handRecords[hand][catchTime] = + descriptor.clone(catchTime - descriptor.time); + } else { + assert( + handRecords[hand][catchTime].equalsWithMod( + descriptor, patternLength)); + } + } + } + } + var handPaths = []; + for (var hand in handRecords) { + var outDescriptors = []; + var inDescriptors = handRecords[hand]; + var prevDescriptor = null; + var descriptor; + for (var idxD in inDescriptors) { + descriptor = inDescriptors[idxD]; + assert(descriptor); // Enumeration should skip array holes. + assert(descriptor.hand == hand); + if (prevDescriptor) { + outDescriptors.push(new EmptyHandDescriptor(prevDescriptor, descriptor, + patternLength)); + } + outDescriptors.push(descriptor.clone()); + prevDescriptor = descriptor; + } + // Note that this EmptyHandDescriptor that wraps around the end lives at the + // end of the array, not the beginning, despite the fact that it may be the + // active one at time zero. This is the same behavior as with Paths for + // balls. + descriptor = new EmptyHandDescriptor(prevDescriptor, outDescriptors[0], + patternLength); + if (descriptor.time < outDescriptors[0].time) { + assert(descriptor.time + descriptor.duration == outDescriptors[0].time); + outDescriptors.unshift(descriptor); + } else { + assert(descriptor.time == + outDescriptors[outDescriptors.length - 1].time + 1); + outDescriptors.push(descriptor); + } + handPaths[hand] = + new Path(outDescriptors, patternLength).fixUpModPathLength(); + } + return handPaths; +} + +// NOTE: All this Vector stuff does lots of object allocations. If that's a +// problem for your browser [e.g. IE6], you'd better stick with the embedded V8. +// This code predates the creation of o3djs/math.js; I should probably switch it +// over at some point, but for now it's not worth the trouble. + +/** + * A simple 3-dimensional vector. + * @constructor + */ +Vector = function(x, y, z) { + this.x = x; + this.y = y; + this.z = z; +} + +Vector.prototype.sub = function(v) { + return new Vector(this.x - v.x, this.y - v.y, this.z - v.z); +}; + +Vector.prototype.add = function(v) { + return new Vector(this.x + v.x, this.y + v.y, this.z + v.z); +}; + +Vector.prototype.dot = function(v) { + return this.x * v.x + this.y * v.y + this.z * v.z; +}; + +Vector.prototype.length = function() { + return Math.sqrt(this.dot(this)); +}; + +Vector.prototype.scale = function(s) { + return new Vector(this.x * s, this.y * s, this.z * s); +}; + +Vector.prototype.set = function(v) { + this.x = v.x; + this.y = v.y; + this.z = v.z; +}; + +Vector.prototype.normalize = function() { + var length = this.length(); + assert(length); + this.set(this.scale(1 / length)); + return this; +}; + +/** + * Convert the Vector to a string for debugging. + * @return {String} debugging output. + */ +Vector.prototype.toString = function() { + return '{' + this.x.toFixed(3) + ', ' + this.y.toFixed(3) + ', ' + + this.z.toFixed(3) + '}'; +}; + +/** + * A container class that holds the positions relevant to a hand: where it is + * when it's not doing anything, where it likes to catch balls, and where it + * likes to throw balls to each of the other hands. + * @param {!Vector} basePosition the centroid of throw and catch positions when + * the hand throws to itself. + * @param {!Vector} catchPosition where the hand likes to catch balls. + * @constructor + */ +HandPositionRecord = function(basePosition, catchPosition) { + this.basePosition = basePosition; + this.catchPosition = catchPosition; + this.throwPositions = []; +} + +/** + * Convert the HandPositionRecord to a string for debugging. + * @return {String} debugging output. + */ +HandPositionRecord.prototype.toString = function() { + var s = 'base: ' + this.basePosition.toString() + ';\n'; + s += 'catch: ' + this.catchPosition.toString() + ';\n'; + s += 'throws:\n'; + for (var i = 0; i < this.throwPositions.length; ++i) { + s += '[' + i + '] ' + this.throwPositions[i].toString() + '\n'; + } + return s; +}; + +/** + * Compute all the hand positions used in a pattern given a number of hands and + * a grouping style ["even" for evenly-spaced hands, "pairs" to group them in + * pairs, as with 2-handed jugglers]. + * @param {!number} numHands the number of hands to use. + * @param {!String} style the grouping style. + * @return {!Array.HandPositionRecord} a full set of hand positions. + */ +function computeHandPositions(numHands, style) { + assert(numHands > 0); + var majorRadiusScale = 0.75; + var majorRadius = majorRadiusScale * (numHands - 1); + var throwCatchOffset = 0.45; + var catchRadius = majorRadius + throwCatchOffset; + var handPositionRecords = []; + for (var hand = 0; hand < numHands; ++hand) { + var circleFraction; + if (style == 'even') { + circleFraction = hand / numHands; + } else { + assert(style == 'pairs'); + circleFraction = (hand + Math.floor(hand / 2)) / (1.5 * numHands); + } + var cos = Math.cos(Math.PI * 2 * circleFraction); + var sin = Math.sin(Math.PI * 2 * circleFraction); + var cX = catchRadius * cos; + var cY = 0; + var cZ = catchRadius * sin; + var bX = majorRadius * cos; + var bY = 0; + var bZ = majorRadius * sin; + handPositionRecords[hand] = new HandPositionRecord( + new Vector(bX, bY, bZ), new Vector(cX, cY, cZ)); + } + // Now that we've got all the hands' base and catch positions, we need to + // compute the appropriate throw positions for each hand pair. + for (var source = 0; source < numHands; ++source) { + var throwHand = handPositionRecords[source]; + for (var target = 0; target < numHands; ++target) { + var catchHand = handPositionRecords[target]; + if (throwHand == catchHand) { + var baseV = throwHand.basePosition; + throwHand.throwPositions[target] = + baseV.add(baseV.sub(throwHand.catchPosition)); + } else { + var directionV = + catchHand.catchPosition.sub(throwHand.basePosition).normalize(); + var offsetV = directionV.scale(throwCatchOffset); + throwHand.throwPositions[target] = + throwHand.basePosition.add(offsetV); + } + } + } + return handPositionRecords; +} + +/** + * Convert an array of HandPositionRecord to a string for debugging. + * @param {!Array.HandPositionRecord} positions the positions to display. + * @return {String} debugging output. + */ +function getStringFromHandPositions(positions) { + var s = ''; + for (index in positions) { + s += positions[index].toString(); + } + return s; +} + +/** + * The set of curves an object passes through throughout a full animation cycle. + * @param {!number} duration the length of the animation in beats. + * @param {!Array.Curve} curves the full set of Curves. + * @constructor + */ +CurveSet = function(duration, curves) { + this.duration = duration; + this.curves = curves; +} + +/** + * Looks up what curve is active at a particular time. This is slower than + * getCurveForTime, but can be used even if no Curve starts precisely at + * unsafeTime % this.duration. + * @param {!number} unsafeTime the time at which to check. + * @return {!Curve} the curve active at unsafeTime. + */ +CurveSet.prototype.getCurveForUnsafeTime = function(unsafeTime) { + unsafeTime %= this.duration; + time = Math.floor(unsafeTime); + if (this.curves[time]) { + return this.curves[time]; + } + var curve; + for (var i = time; i >= 0; --i) { + curve = this.curves[i]; + if (curve) { + assert(i + curve.duration >= unsafeTime); + return curve; + } + } + // We must want the last one. There's always a last one, given how we + // construct the CurveSets; they're sparse, but the length gets set by adding + // elements at the end. + curve = this.curves[this.curves.length - 1]; + unsafeTime += this.duration; + assert(curve.startTime <= unsafeTime); + assert(curve.startTime + curve.duration > unsafeTime); + return curve; +}; + +/** + * Looks up what curve is active at a particular time. This is faster than + * getCurveForUnsafeTime, but can only be used if if a Curve starts precisely at + * unsafeTime % this.duration. + * @param {!number} time the time at which to check. + * @return {!Curve} the curve starting at time. + */ +CurveSet.prototype.getCurveForTime = function(time) { + return this.curves[time % this.duration]; +}; + +/** + * Convert the CurveSet to a string for debugging. + * @return {String} debugging output. + */ +CurveSet.prototype.toString = function() { + var s = 'Duration: ' + this.duration + '\n'; + for (var c in this.curves) { + s += this.curves[c].toString(); + } + return s; +}; + +/** + * Namespace object to hold the pure math functions. + * TODO: Consider just rolling these into the Pattern object, when it gets + * created. + */ +var JugglingMath = {}; + +/** + * Computes the greatest common devisor of integers a and b. + * @param {!number} a an integer. + * @param {!number} b an integer. + * @return {!number} the GCD of a and b. + */ +JugglingMath.computeGCD = function(a, b) { + assert(Math.round(a) == a); + assert(Math.round(b) == b); + assert(a >= 0); + assert(b >= 0); + if (!b) { + return a; + } else { + return JugglingMath.computeGCD(b, a % b); + } +} + +/** + * Computes the least common multiple of integers a and b, by making use of the + * fact that LCM(a, b) * GCD(a, b) == a * b. + * @param {!number} a an integer. + * @param {!number} b an integer. + * @return {!number} the LCM of a and b. + */ +JugglingMath.computeLCM = function(a, b) { + assert(Math.round(a) == a); + assert(Math.round(b) == b); + assert(a >= 0); + assert(b >= 0); + var ret = a * b / JugglingMath.computeGCD(a, b); + assert(Math.round(ret) == ret); + return ret; +} + +/** + * Given a Path and a set of hand positions, compute the corresponding set of + * Curves. + * @param {!Path} path the path of an object. + * @param {!Array.HandPositionRecord} handPositions the positions of the hands + * juggling the pattern containing the path. + * @return {!CurveSet} the full set of curves. + */ +CurveSet.getCurveSetFromPath = function(path, handPositions) { + var curves = []; + var pathLength = path.pathLength; + for (var index in path.descriptors) { + var descriptor = path.descriptors[index]; + var curve = descriptor.generateCurve(handPositions); + assert(!curves[curve.startTime]); + assert(curve.startTime < pathLength); + curves[curve.startTime] = curve; + } + return new CurveSet(pathLength, curves); +} + +/** + * Given a set of Paths and a set of hand positions, compute the corresponding + * CurveSets. + * @param {!Array.Path} paths the paths of a number of objects. + * @param {!Array.HandPositionRecord} handPositions the positions of the hands + * juggling the pattern containing the paths. + * @return {!Array.CurveSet} the CurveSets. + */ +CurveSet.getCurveSetsFromPaths = function(paths, handPositions) { + var curveSets = []; + for (var index in paths) { + var path = paths[index]; + curveSets[index] = CurveSet.getCurveSetFromPath(path, handPositions); + } + return curveSets; +} + +/** + * This is a temporary top-level calculation function that converts a standard + * asynchronous siteswap, expressed as a string of digits, into a full + * ready-to-animate set of CurveSets. Later on we'll be using an interface that + * can create a richer set of patterns than those expressable in the traditional + * string-of-ints format. + * @param {!String} patternString the siteswap. + * @param {!number} numHands the number of hands to use for the pattern. + * @param {!String} style how to space the hands ["pairs" or "even"]. + * @return {!Object} a fully-analyzed pattern as CurveSets and associated data. + */ +function computeFullPatternFromString(patternString, numHands, style) { + var patternAsStrings = patternString.split(/[ ,]+ */); + var patternSegment = []; + for (var index in patternAsStrings) { + if (patternAsStrings[index]) { // Beware extra whitespace at the ends. + patternSegment.push(parseInt(patternAsStrings[index])); + } + } + var pattern = []; + // Now expand the pattern out to the length of the LCM of pattern length and + // number of hands, so that each throw gets done in each of its incarnations. + var multiple = JugglingMath.computeLCM(patternSegment.length, numHands) / + patternSegment.length; + for (var i = 0; i < multiple; ++i) { + pattern = pattern.concat(patternSegment); + } + + var fullPattern = expandPattern(pattern, numHands); + var patternLength = fullPattern[0].length; + + var ballPaths = generateBallPaths(fullPattern); + var handPaths = generateHandPaths(ballPaths, numHands, patternLength); + + var handPositions = computeHandPositions(numHands, style); + var ballCurveSets = CurveSet.getCurveSetsFromPaths(ballPaths, handPositions); + var handCurveSets = CurveSet.getCurveSetsFromPaths(handPaths, handPositions); + + // Find the LCM of all the curveSet durations. This will be the length of the + // fully-expanded queue. We could expand to this before computing the + // CurveSets, but this way's probably just a little cheaper. + var lcmDuration = 1; + for (var i in ballCurveSets) { + var duration = ballCurveSets[i].duration; + if (duration > lcmDuration || lcmDuration % duration) { + lcmDuration = JugglingMath.computeLCM(lcmDuration, duration); + } + } + for (var i in handCurveSets) { + var duration = handCurveSets[i].duration; + if (duration > lcmDuration || lcmDuration % duration) { + lcmDuration = JugglingMath.computeLCM(lcmDuration, duration); + } + } + return { + numBalls: ballPaths.length, + numHands: handPaths.length, + duration: lcmDuration, + handCurveSets: handCurveSets, + ballCurveSets: ballCurveSets + } +} diff --git a/o3d/samples/siteswap/siteswap.html b/o3d/samples/siteswap/siteswap.html index 15d7497..06656eb 100644 --- a/o3d/samples/siteswap/siteswap.html +++ b/o3d/samples/siteswap/siteswap.html @@ -1,323 +1,323 @@ -<!-- @@REWRITE(insert html-copyright) --> -<!-- -O3D Siteswap animator -@@REWRITE(delete-start) -Author: Eric Uhrhane (ericu@google.com) -@@REWRITE(delete-end) ---> -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" - "http://www.w3.org/TR/html4/loose.dtd"> -<html> -<head> -<meta http-equiv="content-type" content="text/html; charset=UTF-8"> -<title> - Site Swap Simulator -</title> -<style type="text/css"> - html, body { - height: 100%; - margin: 0; - padding: 0; - border: none; - } -</style> -<!-- Our javascript code --> -<script type="text/javascript" src="../o3djs/base.js"></script> -<script type="text/javascript" src="siteswap.js"></script> -<script type="text/javascript" src="math.js"></script> -<script type="text/javascript" src="animation.js"></script> -<script type="text/javascript" id="o3dscript"> - -o3djs.require('o3djs.util'); - -// Set up the client area. -function init() { - o3djs.util.setMainEngine(o3djs.util.Engine.V8); - o3djs.util.addScriptUri(''); // Allow V8 to load scripts in the cwd. - o3djs.util.makeClients(initStep2); - setUpSelection(); -} - -// Select the entire input pattern, so that the user can just type. -function setUpSelection() { - var input_pattern = document.getElementById('input_pattern'); - input_pattern.focus(); - input_pattern.selectionStart = 0; - input_pattern.selectionEnd = input_pattern.value.length; -} - -/* - * Wrappers to enable button presses to call through to the embedded V8 engine. - */ - -// Update whether we're animating or frozen based on the value of the checkbox. -function updateAnimatingWrapper() { - g.o3dElement.eval('updateAnimating()'); -} - -// Compute a new pattern. -function onComputePatternWrapper() { - g.o3dElement.eval('onComputePattern()'); -} - -// Adjust the view matrix to the new proportions of the plugin window. -// TODO: Switch to using the resize event once that's checked in. -function onResizeWrapper() { - g.o3dElement.eval('onResize()'); -} -window.onresize = onResizeWrapper; - -// Clean up all our callbacks, etc. -function cleanupWrapper() { - if (g && g.o3dElement) { - g.o3dElement.eval('cleanup()'); - } -} - -// The point of this function is to suppress form submit; we want to use the -// pattern entered when the user hits return, not submit the page to the -// webserver. -function suppressSubmit(event) { - onComputePatternWrapper(); // TODO: This suppresses form autocomplete storage. - return false; -} - -</script> -</head> -<body onload="init()" onunload="cleanupWrapper()"> -<table width="100%" style="height:100%;"> - <tr> - <td> - <h1>Juggler</h1> - This sample displays a site-swap juggling pattern with animation done by - a vertex shader. - <form name="the_form" onsubmit="return suppressSubmit(event)"> - <table> - <tr> - <td> - <table> - <tr> - <td> - <input - type="radio" - name="radio_group_hands" - value="1" - onclick=onComputePatternWrapper()> - 1 Hand<br> - <input - type="radio" - name="radio_group_hands" - value="2" - onclick=onComputePatternWrapper() - checked> - 2 Hands<br> - <input - type="radio" - name="radio_group_hands" - value="3" - onclick=onComputePatternWrapper()> - 3 Hands<br> - <input - type="radio" - name="radio_group_hands" - value="4" - onclick=onComputePatternWrapper()> - 4 Hands<br> - </td> - <td> - <input type="text" name="input_pattern" id="input_pattern" - value="3 4 5"> - </td> - <td> - <input type="checkbox" name="check_box" checked - onclick=updateAnimatingWrapper()>Animate - <input - type="checkbox" - name="pair_hands" - onclick=onComputePatternWrapper(); - >Pair Hands - </td> - <td> - <input type="button" name="computePattern" - value="Compute Pattern" - onclick=onComputePatternWrapper()> - </td> - </tr> - </table> - </td> - <td> - </td> - </tr> - </table> - </form> - <table id="container" width="90%" style="height:70%;"> - <tr> - <td height="100%"> - <!-- Start of g.o3d plugin --> - <div id="o3d" style="width: 100%; height: 100%;"></div> - <!-- End of g.o3d plugin --> - </td> - </tr> - </table> - <!-- a simple way to get a multiline string --> - <textarea id="shader" name="shader" cols="80" rows="20" - style="display: none;"> -// the 4x4 world view projection matrix -float4x4 worldViewProjection : WorldViewProjection; - -// positions of the light and camera -float3 light_pos; -float3 camera_pos; - -// phong lighting properties of the material -float4 light_ambient; -float4 light_diffuse; -float4 light_specular; - -// shininess of the material (for specular lighting) -float shininess; - -// time for animation -float time; - -// coefficients for a t^2 + b t + c for each of x, y, z -float3 coeff_a; -float3 coeff_b; -float3 coeff_c; - -// flag and coefficient for optional LERP with rate t * coeff_lerp; -float coeff_lerp; - -// coefficients for a t^2 + b t + c for each of x, y, z, for optional LERP -float3 coeff_l_a; -float3 coeff_l_b; -float3 coeff_l_c; - -// coefficients for d sin(f t) + e cos(e t) for each of x, y, z -float3 coeff_d; -float3 coeff_e; -float3 coeff_f; - -// to be subtracted from time to get the t for the above equation -float time_base; - -// input parameters for our vertex shader -struct VertexShaderInput { - float4 position : POSITION; - float3 normal : NORMAL; - float4 color : COLOR; -}; - -// input parameters for our pixel shader -// also the output parameters for our vertex shader -struct PixelShaderInput { - float4 position : POSITION; - float3 lightVector : TEXCOORD0; - float3 normal : TEXCOORD1; - float3 viewPosition : TEXCOORD2; - float4 color : COLOR; -}; - -/** - * Vertex Shader - vertex shader for phong illumination - */ -PixelShaderInput vertexShaderFunction(VertexShaderInput input) { - /** - * We use the standard phong illumination equation here. - * We restrict (clamp) the dot products so that we - * don't get any negative values. - * All vectors are normalized for proper calculations. - * - * The output color is the summation of the - * ambient, diffuse, and specular contributions. - * - * Note that we have to transform each vertex and normal - * by the world view projection matrix first. - */ - PixelShaderInput output; - - float t = time - time_base; - float3 offset = float3(0, 0, 0); - offset += t * t * coeff_a + t * coeff_b + coeff_c; - offset += coeff_d * sin(t * coeff_f); - offset += coeff_e * cos(t * coeff_f); - - float3 lerpOffset = t * t * coeff_l_a + t * coeff_l_b + coeff_l_c; - if (coeff_lerp > 0) { - float rate = min(coeff_lerp * t, 1); - float3 lerpVector = float3(rate, rate, rate); - offset = lerp(offset, lerpOffset, lerpVector); - } else if (coeff_lerp < 0) { - float rate = min(-coeff_lerp * t, 1); - float3 lerpVector = float3(rate, rate, rate); - offset = lerp(lerpOffset, offset, lerpVector); - } - - input.position = input.position + float4(offset.xyz, 0); - - output.position = mul(input.position, worldViewProjection); - - /** - * lightVector - light vector - * normal - normal vector - * viewPosition - view vector (from camera) - */ - - // NOTE: In this case we do not need to multiply by any matrices since the - // WORLD transformation matrix is the identity. If you were moving the - // object such that the WORLD transform matrix was not the identity, you - // would need to multiply the normal by the WORLDINVERSETTRANSFORM matrix - // since the normal is in object space. Other values (light_pos, camera_pos) - // are already in world space. - float3 lightVector = light_pos - input.position.xyz; - float3 normal = input.normal; - float3 viewPosition = camera_pos - input.position.xyz; - - output.lightVector = lightVector; - output.normal = normal; - output.viewPosition = viewPosition; - output.color = input.color; - return output; -} - -/** - * Pixel Shader - */ -float4 pixelShaderFunction(PixelShaderInput input): COLOR { - float3 lightVector = normalize(input.lightVector); - float3 normal = normalize(input.normal); - float3 viewPosition = normalize(input.viewPosition); - float3 halfVector = normalize(lightVector + viewPosition); - - // use lit function to calculate phong shading - // x component contains the ambient coefficient - // y component contains the diffuse coefficient: - // max(dot(normal, lightVector),0) - // z component contains the specular coefficient: - // dot(normal, lightVector) < 0 || dot(normal, halfVector) < 0 ? - // 0 : pow(dot(normal, halfVector), shininess) - // NOTE: This is actually Blinn-Phong shading, not Phong shading - // which would use the reflection vector instead of the half vector - - float4 phong_coeff = lit(dot(normal, lightVector), - dot(normal, halfVector), shininess); - - float4 ambient = light_ambient * phong_coeff.x * input.color; - float4 diffuse = light_diffuse * phong_coeff.y * input.color; - float4 specular = light_specular * phong_coeff.z * input.color; - - return ambient + diffuse + specular; -} - -// Here we tell our effect file *which* functions are -// our vertex and pixel shaders. - -// #o3d VertexShaderEntryPoint vertexShaderFunction -// #o3d PixelShaderEntryPoint pixelShaderFunction -// #o3d MatrixLoadOrder RowMajor - </textarea> - </td> - </tr> -</table> -</body> -</html> +<!-- @@REWRITE(insert html-copyright) --> +<!-- +O3D Siteswap animator +@@REWRITE(delete-start) +Author: Eric Uhrhane (ericu@google.com) +@@REWRITE(delete-end) +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> + Site Swap Simulator +</title> +<style type="text/css"> + html, body { + height: 100%; + margin: 0; + padding: 0; + border: none; + } +</style> +<!-- Our javascript code --> +<script type="text/javascript" src="../o3djs/base.js"></script> +<script type="text/javascript" src="siteswap.js"></script> +<script type="text/javascript" src="math.js"></script> +<script type="text/javascript" src="animation.js"></script> +<script type="text/javascript" id="o3dscript"> + +o3djs.require('o3djs.util'); + +// Set up the client area. +function init() { + o3djs.util.setMainEngine(o3djs.util.Engine.V8); + o3djs.util.addScriptUri(''); // Allow V8 to load scripts in the cwd. + o3djs.util.makeClients(initStep2); + setUpSelection(); +} + +// Select the entire input pattern, so that the user can just type. +function setUpSelection() { + var input_pattern = document.getElementById('input_pattern'); + input_pattern.focus(); + input_pattern.selectionStart = 0; + input_pattern.selectionEnd = input_pattern.value.length; +} + +/* + * Wrappers to enable button presses to call through to the embedded V8 engine. + */ + +// Update whether we're animating or frozen based on the value of the checkbox. +function updateAnimatingWrapper() { + g.o3dElement.eval('updateAnimating()'); +} + +// Compute a new pattern. +function onComputePatternWrapper() { + g.o3dElement.eval('onComputePattern()'); +} + +// Adjust the view matrix to the new proportions of the plugin window. +// TODO: Switch to using the resize event once that's checked in. +function onResizeWrapper() { + g.o3dElement.eval('onResize()'); +} +window.onresize = onResizeWrapper; + +// Clean up all our callbacks, etc. +function cleanupWrapper() { + if (g && g.o3dElement) { + g.o3dElement.eval('cleanup()'); + } +} + +// The point of this function is to suppress form submit; we want to use the +// pattern entered when the user hits return, not submit the page to the +// webserver. +function suppressSubmit(event) { + onComputePatternWrapper(); // TODO: This suppresses form autocomplete storage. + return false; +} + +</script> +</head> +<body onload="init()" onunload="cleanupWrapper()"> +<table width="100%" style="height:100%;"> + <tr> + <td> + <h1>Juggler</h1> + This sample displays a site-swap juggling pattern with animation done by + a vertex shader. + <form name="the_form" onsubmit="return suppressSubmit(event)"> + <table> + <tr> + <td> + <table> + <tr> + <td> + <input + type="radio" + name="radio_group_hands" + value="1" + onclick=onComputePatternWrapper()> + 1 Hand<br> + <input + type="radio" + name="radio_group_hands" + value="2" + onclick=onComputePatternWrapper() + checked> + 2 Hands<br> + <input + type="radio" + name="radio_group_hands" + value="3" + onclick=onComputePatternWrapper()> + 3 Hands<br> + <input + type="radio" + name="radio_group_hands" + value="4" + onclick=onComputePatternWrapper()> + 4 Hands<br> + </td> + <td> + <input type="text" name="input_pattern" id="input_pattern" + value="3 4 5"> + </td> + <td> + <input type="checkbox" name="check_box" checked + onclick=updateAnimatingWrapper()>Animate + <input + type="checkbox" + name="pair_hands" + onclick=onComputePatternWrapper(); + >Pair Hands + </td> + <td> + <input type="button" name="computePattern" + value="Compute Pattern" + onclick=onComputePatternWrapper()> + </td> + </tr> + </table> + </td> + <td> + </td> + </tr> + </table> + </form> + <table id="container" width="90%" style="height:70%;"> + <tr> + <td height="100%"> + <!-- Start of g.o3d plugin --> + <div id="o3d" style="width: 100%; height: 100%;"></div> + <!-- End of g.o3d plugin --> + </td> + </tr> + </table> + <!-- a simple way to get a multiline string --> + <textarea id="shader" name="shader" cols="80" rows="20" + style="display: none;"> +// the 4x4 world view projection matrix +float4x4 worldViewProjection : WorldViewProjection; + +// positions of the light and camera +float3 light_pos; +float3 camera_pos; + +// phong lighting properties of the material +float4 light_ambient; +float4 light_diffuse; +float4 light_specular; + +// shininess of the material (for specular lighting) +float shininess; + +// time for animation +float time; + +// coefficients for a t^2 + b t + c for each of x, y, z +float3 coeff_a; +float3 coeff_b; +float3 coeff_c; + +// flag and coefficient for optional LERP with rate t * coeff_lerp; +float coeff_lerp; + +// coefficients for a t^2 + b t + c for each of x, y, z, for optional LERP +float3 coeff_l_a; +float3 coeff_l_b; +float3 coeff_l_c; + +// coefficients for d sin(f t) + e cos(e t) for each of x, y, z +float3 coeff_d; +float3 coeff_e; +float3 coeff_f; + +// to be subtracted from time to get the t for the above equation +float time_base; + +// input parameters for our vertex shader +struct VertexShaderInput { + float4 position : POSITION; + float3 normal : NORMAL; + float4 color : COLOR; +}; + +// input parameters for our pixel shader +// also the output parameters for our vertex shader +struct PixelShaderInput { + float4 position : POSITION; + float3 lightVector : TEXCOORD0; + float3 normal : TEXCOORD1; + float3 viewPosition : TEXCOORD2; + float4 color : COLOR; +}; + +/** + * Vertex Shader - vertex shader for phong illumination + */ +PixelShaderInput vertexShaderFunction(VertexShaderInput input) { + /** + * We use the standard phong illumination equation here. + * We restrict (clamp) the dot products so that we + * don't get any negative values. + * All vectors are normalized for proper calculations. + * + * The output color is the summation of the + * ambient, diffuse, and specular contributions. + * + * Note that we have to transform each vertex and normal + * by the world view projection matrix first. + */ + PixelShaderInput output; + + float t = time - time_base; + float3 offset = float3(0, 0, 0); + offset += t * t * coeff_a + t * coeff_b + coeff_c; + offset += coeff_d * sin(t * coeff_f); + offset += coeff_e * cos(t * coeff_f); + + float3 lerpOffset = t * t * coeff_l_a + t * coeff_l_b + coeff_l_c; + if (coeff_lerp > 0) { + float rate = min(coeff_lerp * t, 1); + float3 lerpVector = float3(rate, rate, rate); + offset = lerp(offset, lerpOffset, lerpVector); + } else if (coeff_lerp < 0) { + float rate = min(-coeff_lerp * t, 1); + float3 lerpVector = float3(rate, rate, rate); + offset = lerp(lerpOffset, offset, lerpVector); + } + + input.position = input.position + float4(offset.xyz, 0); + + output.position = mul(input.position, worldViewProjection); + + /** + * lightVector - light vector + * normal - normal vector + * viewPosition - view vector (from camera) + */ + + // NOTE: In this case we do not need to multiply by any matrices since the + // WORLD transformation matrix is the identity. If you were moving the + // object such that the WORLD transform matrix was not the identity, you + // would need to multiply the normal by the WORLDINVERSETTRANSFORM matrix + // since the normal is in object space. Other values (light_pos, camera_pos) + // are already in world space. + float3 lightVector = light_pos - input.position.xyz; + float3 normal = input.normal; + float3 viewPosition = camera_pos - input.position.xyz; + + output.lightVector = lightVector; + output.normal = normal; + output.viewPosition = viewPosition; + output.color = input.color; + return output; +} + +/** + * Pixel Shader + */ +float4 pixelShaderFunction(PixelShaderInput input): COLOR { + float3 lightVector = normalize(input.lightVector); + float3 normal = normalize(input.normal); + float3 viewPosition = normalize(input.viewPosition); + float3 halfVector = normalize(lightVector + viewPosition); + + // use lit function to calculate phong shading + // x component contains the ambient coefficient + // y component contains the diffuse coefficient: + // max(dot(normal, lightVector),0) + // z component contains the specular coefficient: + // dot(normal, lightVector) < 0 || dot(normal, halfVector) < 0 ? + // 0 : pow(dot(normal, halfVector), shininess) + // NOTE: This is actually Blinn-Phong shading, not Phong shading + // which would use the reflection vector instead of the half vector + + float4 phong_coeff = lit(dot(normal, lightVector), + dot(normal, halfVector), shininess); + + float4 ambient = light_ambient * phong_coeff.x * input.color; + float4 diffuse = light_diffuse * phong_coeff.y * input.color; + float4 specular = light_specular * phong_coeff.z * input.color; + + return ambient + diffuse + specular; +} + +// Here we tell our effect file *which* functions are +// our vertex and pixel shaders. + +// #o3d VertexShaderEntryPoint vertexShaderFunction +// #o3d PixelShaderEntryPoint pixelShaderFunction +// #o3d MatrixLoadOrder RowMajor + </textarea> + </td> + </tr> +</table> +</body> +</html> diff --git a/o3d/samples/siteswap/siteswap.js b/o3d/samples/siteswap/siteswap.js index 24a59a7cd..6b77aff 100644 --- a/o3d/samples/siteswap/siteswap.js +++ b/o3d/samples/siteswap/siteswap.js @@ -1,415 +1,415 @@ -// @@REWRITE(insert js-copyright) -// @@REWRITE(delete-start) -// Copyright 2009 Google Inc. All Rights Reserved -// @@REWRITE(delete-end) - -/** - * This file contains the top-level logic and o3d-related code for the siteswap - * animator. - */ - -o3djs.require('o3djs.rendergraph'); -o3djs.require('o3djs.math'); -o3djs.require('o3djs.primitives'); -o3djs.require('o3djs.dump'); - -// Global variables are all referenced via g, so that either interpreter can -// find them easily. -var g = {}; - -/** - * Creates a color based on an input index as a seed. - * @param {!number} index the seed value to select the color. - * @return {!Array.number} an [r g b a] color. - */ -function createColor(index) { - var N = 12; // Number of distinct colors. - var root3 = Math.sqrt(3); - var theta = 2 * Math.PI * index / N; - var sin = Math.sin(theta); - var cos = Math.cos(theta); - return [(1 / 3 + 2 / 3 * cos) + (1 / 3 - cos / 3 - sin / root3), - (1 / 3 - cos / 3 + sin / root3) + (1 / 3 + 2 / 3 * cos), - (1 / 3 - cos / 3 - sin / root3) + (1 / 3 - cos / 3 + sin / root3), - 1]; -} - -/** - * Creates a material, given the index as a seed to make it distinguishable. - * @param {number} index an integer used to create a distinctive color. - * @return {!o3d.Material} the material. - */ -function createMaterial(index) { - var material = g.pack.createObject('Material'); - - // Apply our effect to this material. The effect tells the 3D hardware - // which shader to use. - material.effect = g.effect; - - // Set the material's drawList - material.drawList = g.viewInfo.performanceDrawList; - - // This will create our quadColor parameter on the material. - g.effect.createUniformParameters(material); - - // Set up the individual parameters in our effect file. - - // Light position - var light_pos_param = material.getParam('light_pos'); - light_pos_param.value = [10, 10, 20]; - - // Phong components of the light source - var light_ambient_param = material.getParam('light_ambient'); - var light_diffuse_param = material.getParam('light_diffuse'); - var light_specular_param = material.getParam('light_specular'); - - // White ambient light - light_ambient_param.value = [0.04, 0.04, 0.04, 1]; - - light_diffuse_param.value = createColor(index); - // White specular light - light_specular_param.value = [0.5, 0.5, 0.5, 1]; - - // Shininess of the material (for specular lighting) - var shininess_param = material.getParam('shininess'); - shininess_param.value = 30.0; - - // Bind the counter's count to the input of the FunctionEval. - var paramTime = material.getParam('time'); - paramTime.bind(g.counter.getParam('count')); - - material.getParam('camera_pos').value = g.eye; - - return material; -} - -/** - * Gets a material from our cache, creating it if it's not yet been made. - * Uses index as a seed to make the material distinguishable. - * @param {number} index an integer used to create/fetch a distinctive color. - * @return {!o3d.Material} the material. - */ -function getMaterial(index) { - g.materials = g.materials || []; // See initStep2 for a comment. - if (!g.materials[index]) { - g.materials[index] = createMaterial(index); - } - return g.materials[index]; -} - -/** - * Initializes g.o3d. - * @param {Array} clientElements Array of o3d object elements. - */ -function initStep2(clientElements) { - // Initializes global variables and libraries. - window.g = g; - - // Used to tell whether we need to recompute our view on resize. - g.o3dWidth = -1; - g.o3dHeight = -1; - - // We create a different material for each color of object. - //g.materials = []; // TODO(ericu): If this is executed, we fail. Why? - - // We hold on to all the shapes here so that we can clean them up when we want - // to change patterns. - g.ballShapes = []; - g.handShapes = []; - - g.o3dElement = clientElements[0]; - g.o3d = g.o3dElement.o3d; - g.math = o3djs.math; - g.client = g.o3dElement.client; - - // Initialize client sample libraries. - o3djs.base.init(g.o3dElement); - - // Create a g.pack to manage our resources/assets - g.pack = g.client.createPack(); - - // Create the render graph for a view. - g.viewInfo = o3djs.rendergraph.createBasicView( - g.pack, - g.client.root, - g.client.renderGraphRoot); - - // Get the default context to hold view/projection matrices. - g.context = g.viewInfo.drawContext; - - // Load a simple effect from a textarea. - g.effect = g.pack.createObject('Effect'); - g.effect.loadFromFXString(document.getElementById('shader').value); - - // Eye-position: this is where our camera is located. - // Global because each material we create must also know where it is, so that - // the shader works properly. - g.eye = [1, 6, 10]; - - // Target, this is the point at which our camera is pointed. - var target = [0, 2, 0]; - - // Up-vector, this tells the camera which direction is 'up'. - // We define the positive y-direction to be up in this example. - var up = [0, 1, 0]; - - g.context.view = g.math.matrix4.lookAt(g.eye, target, up); - - // Make a SecondCounter to provide the time for our animation. - g.counter = g.pack.createObject('SecondCounter'); - g.counter.multiplier = 3; // Speed up time; this is in throws per second. - - // Generate the projection and viewProjection matrices based - // on the g.o3d plugin size by calling onResize(). - onResize(); - - // If we don't check the size of the client area every frame we don't get a - // chance to adjust the perspective matrix fast enough to keep up with the - // browser resizing us. - // TODO(ericu): Switch to using the resize event once it's checked in. - g.client.setRenderCallback(onResize); -} - -/** - * Stops or starts the animation based on the state of an html checkbox. - */ -function updateAnimating() { - var box = document.the_form.check_box; - g.counter.running = box.checked; -} - -/** - * Generates the projection matrix based on the size of the g.o3d plugin - * and calculates the view-projection matrix. - */ -function onResize() { - var newWidth = g.client.width; - var newHeight = g.client.height; - - if (newWidth != g.o3dWidth || newHeight != g.o3dHeight) { - debug('resizing'); - g.o3dWidth = newWidth; - g.o3dHeight = newHeight; - - // Create our projection matrix, with a vertical field of view of 45 degrees - // a near clipping plane of 0.1 and far clipping plane of 100. - g.context.projection = g.math.matrix4.perspective( - 45 * Math.PI / 180, - g.o3dWidth / g.o3dHeight, - 0.1, - 100); - } -} - -/** - * Computes and prepares animation of the pattern input via the html form. If - * the box is checked, this will immediately begin animation as well. - */ -function onComputePattern() { - try { - g.counter.removeAllCallbacks(); - var group = document.the_form.radio_group_hands; - var numHands = -1; - for (var i = 0; i < group.length; ++i) { - if (group[i].checked) { - numHands = parseInt(group[i].value); - } - } - var style = 'even'; - if (document.the_form.pair_hands.checked) { - style = 'pairs'; - } - var patternString = document.getElementById('input_pattern').value; - var patternData = - computeFullPatternFromString(patternString, numHands, style); - startAnimation( - patternData.numBalls, - patternData.numHands, - patternData.duration, - patternData.ballCurveSets, - patternData.handCurveSets); - } catch (ex) { - popup(stringifyObj(ex)); - throw ex; - } - setUpSelection(); -} - -/** - * Removes any callbacks so they don't get called after the page has unloaded. - */ -function cleanup() { - g.client.cleanup(); -} - - -/** - * Dump out a newline-terminated string to the debug console, if available. - * @param {!string} s the string to output. - */ -function debug(s) { - o3djs.dump.dump(s + '\n'); -} - -/** - * Dump out a newline-terminated string to the debug console, if available, - * then display it via an alert. - * @param {!string} s the string to output. - */ -function popup(s) { - debug(s); - window.alert(s); -} - -/** - * If t, throw an exception. - * @param {!bool} t the value to test. - */ -function assert(t) { - if (!t) { - throw new Error('Assertion failed!'); - } -} - -/** - * Convert an object to a string containing a full one-level-deep property - * listing, with values. - * @param {!Object} o the object to convert. - * @return {!string} the converted object. - */ -function stringifyObj(o) { - var s = ''; - for (var i in o) { - s += i + ':' + o[i] + '\n'; - } - return s; -} - -/** - * Add the information in a curve to the params on a shape, such that the vertex - * shader will move the shape along the curve at times after timeBase. - * @param {!Curve} curve the curve the shape should follow. - * @param {!o3d.Shape} shape the shape being moved. - * @param {!number} timeBase the base to subtract from the current time when - * giving the curve calculation its time input. - */ -function setParamCurveInfo(curve, shape, timeBase) { - assert(curve); - assert(shape); - try { - shape.elements[0].getParam('time_base').value = timeBase; - shape.elements[0].getParam('coeff_a').value = - [curve.xEqn.a, curve.yEqn.a, curve.zEqn.a]; - shape.elements[0].getParam('coeff_b').value = - [curve.xEqn.b, curve.yEqn.b, curve.zEqn.b]; - shape.elements[0].getParam('coeff_c').value = - [curve.xEqn.c, curve.yEqn.c, curve.zEqn.c]; - shape.elements[0].getParam('coeff_d').value = - [curve.xEqn.d, curve.yEqn.d, curve.zEqn.d]; - shape.elements[0].getParam('coeff_e').value = - [curve.xEqn.e, curve.yEqn.e, curve.zEqn.e]; - shape.elements[0].getParam('coeff_f').value = - [curve.xEqn.f, curve.yEqn.f, curve.zEqn.f]; - - assert(curve.xEqn.lerpRate == curve.yEqn.lerpRate); - assert(curve.xEqn.lerpRate == curve.zEqn.lerpRate); - shape.elements[0].getParam('coeff_lerp').value = curve.xEqn.lerpRate; - if (curve.xEqn.lerpRate) { - shape.elements[0].getParam('coeff_l_a').value = - [curve.xEqn.lA, curve.yEqn.lA, curve.zEqn.lA]; - shape.elements[0].getParam('coeff_l_b').value = - [curve.xEqn.lB, curve.yEqn.lB, curve.zEqn.lB]; - shape.elements[0].getParam('coeff_l_c').value = - [curve.xEqn.lC, curve.yEqn.lC, curve.zEqn.lC]; - } - } catch (ex) { - debug(ex); - throw ex; - } -} - -/** - * Create the params that the shader expects on the supplied shape's first - * element. - * @param {!o3d.Shape} shape the shape on whose first element to create params. - */ -function createParams(shape) { - shape.elements[0].createParam('coeff_a', 'ParamFloat3').value = [0, 0, 0]; - shape.elements[0].createParam('coeff_b', 'ParamFloat3').value = [0, 0, 0]; - shape.elements[0].createParam('coeff_c', 'ParamFloat3').value = [0, 0, 0]; - shape.elements[0].createParam('coeff_d', 'ParamFloat3').value = [0, 0, 0]; - shape.elements[0].createParam('coeff_e', 'ParamFloat3').value = [0, 0, 0]; - shape.elements[0].createParam('coeff_f', 'ParamFloat3').value = [0, 0, 0]; - shape.elements[0].createParam('coeff_l_a', 'ParamFloat3').value = [0, 0, 0]; - shape.elements[0].createParam('coeff_l_b', 'ParamFloat3').value = [0, 0, 0]; - shape.elements[0].createParam('coeff_l_c', 'ParamFloat3').value = [0, 0, 0]; - shape.elements[0].createParam('coeff_lerp', 'ParamFloat').value = 0; - shape.elements[0].createParam('time_base', 'ParamFloat').value = 0; -} - -/** - * Adjust the number of ball shapes in g.pack. - * @param {!number} numBalls the number of balls desired. - */ -function setNumBalls(numBalls) { - for (var i = 0; i < g.ballShapes.length; ++i) { - g.pack.removeObject(g.ballShapes[i]); - g.client.root.removeShape(g.ballShapes[i]); - } - g.ballShapes = []; - - for (var i = 0; i < numBalls; ++i) { - var shape = o3djs.primitives.createSphere(g.pack, - getMaterial(5 * i), - 0.10, - 70, - 70); - shape.name = 'Ball ' + i; - - // generate the draw elements. - shape.createDrawElements(g.pack, null); - - // Now attach the sphere to the root of the scene graph. - g.client.root.addShape(shape); - - // Create the material params for the shader. - createParams(shape); - - g.ballShapes[i] = shape; - } -} - -/** - * Adjust the number of hand shapes in g.pack. - * @param {!number} numHands the number of hands desired. - */ -function setNumHands(numHands) { - g.counter.removeAllCallbacks(); - - for (var i = 0; i < g.handShapes.length; ++i) { - g.pack.removeObject(g.handShapes[i]); - g.client.root.removeShape(g.handShapes[i]); - } - g.handShapes = []; - - for (var i = 0; i < numHands; ++i) { - var shape = o3djs.primitives.createBox(g.pack, - getMaterial(3 * (i + 1)), - 0.25, - 0.05, - 0.25); - shape.name = 'Hand ' + i; - - // generate the draw elements. - shape.createDrawElements(g.pack, null); - - // Now attach the sphere to the root of the scene graph. - g.client.root.addShape(shape); - - // Create the material params for the shader. - createParams(shape); - - g.handShapes[i] = shape; - } -} - +// @@REWRITE(insert js-copyright) +// @@REWRITE(delete-start) +// Copyright 2009 Google Inc. All Rights Reserved +// @@REWRITE(delete-end) + +/** + * This file contains the top-level logic and o3d-related code for the siteswap + * animator. + */ + +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.primitives'); +o3djs.require('o3djs.dump'); + +// Global variables are all referenced via g, so that either interpreter can +// find them easily. +var g = {}; + +/** + * Creates a color based on an input index as a seed. + * @param {!number} index the seed value to select the color. + * @return {!Array.number} an [r g b a] color. + */ +function createColor(index) { + var N = 12; // Number of distinct colors. + var root3 = Math.sqrt(3); + var theta = 2 * Math.PI * index / N; + var sin = Math.sin(theta); + var cos = Math.cos(theta); + return [(1 / 3 + 2 / 3 * cos) + (1 / 3 - cos / 3 - sin / root3), + (1 / 3 - cos / 3 + sin / root3) + (1 / 3 + 2 / 3 * cos), + (1 / 3 - cos / 3 - sin / root3) + (1 / 3 - cos / 3 + sin / root3), + 1]; +} + +/** + * Creates a material, given the index as a seed to make it distinguishable. + * @param {number} index an integer used to create a distinctive color. + * @return {!o3d.Material} the material. + */ +function createMaterial(index) { + var material = g.pack.createObject('Material'); + + // Apply our effect to this material. The effect tells the 3D hardware + // which shader to use. + material.effect = g.effect; + + // Set the material's drawList + material.drawList = g.viewInfo.performanceDrawList; + + // This will create our quadColor parameter on the material. + g.effect.createUniformParameters(material); + + // Set up the individual parameters in our effect file. + + // Light position + var light_pos_param = material.getParam('light_pos'); + light_pos_param.value = [10, 10, 20]; + + // Phong components of the light source + var light_ambient_param = material.getParam('light_ambient'); + var light_diffuse_param = material.getParam('light_diffuse'); + var light_specular_param = material.getParam('light_specular'); + + // White ambient light + light_ambient_param.value = [0.04, 0.04, 0.04, 1]; + + light_diffuse_param.value = createColor(index); + // White specular light + light_specular_param.value = [0.5, 0.5, 0.5, 1]; + + // Shininess of the material (for specular lighting) + var shininess_param = material.getParam('shininess'); + shininess_param.value = 30.0; + + // Bind the counter's count to the input of the FunctionEval. + var paramTime = material.getParam('time'); + paramTime.bind(g.counter.getParam('count')); + + material.getParam('camera_pos').value = g.eye; + + return material; +} + +/** + * Gets a material from our cache, creating it if it's not yet been made. + * Uses index as a seed to make the material distinguishable. + * @param {number} index an integer used to create/fetch a distinctive color. + * @return {!o3d.Material} the material. + */ +function getMaterial(index) { + g.materials = g.materials || []; // See initStep2 for a comment. + if (!g.materials[index]) { + g.materials[index] = createMaterial(index); + } + return g.materials[index]; +} + +/** + * Initializes g.o3d. + * @param {Array} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Initializes global variables and libraries. + window.g = g; + + // Used to tell whether we need to recompute our view on resize. + g.o3dWidth = -1; + g.o3dHeight = -1; + + // We create a different material for each color of object. + //g.materials = []; // TODO(ericu): If this is executed, we fail. Why? + + // We hold on to all the shapes here so that we can clean them up when we want + // to change patterns. + g.ballShapes = []; + g.handShapes = []; + + g.o3dElement = clientElements[0]; + g.o3d = g.o3dElement.o3d; + g.math = o3djs.math; + g.client = g.o3dElement.client; + + // Initialize client sample libraries. + o3djs.base.init(g.o3dElement); + + // Create a g.pack to manage our resources/assets + g.pack = g.client.createPack(); + + // Create the render graph for a view. + g.viewInfo = o3djs.rendergraph.createBasicView( + g.pack, + g.client.root, + g.client.renderGraphRoot); + + // Get the default context to hold view/projection matrices. + g.context = g.viewInfo.drawContext; + + // Load a simple effect from a textarea. + g.effect = g.pack.createObject('Effect'); + g.effect.loadFromFXString(document.getElementById('shader').value); + + // Eye-position: this is where our camera is located. + // Global because each material we create must also know where it is, so that + // the shader works properly. + g.eye = [1, 6, 10]; + + // Target, this is the point at which our camera is pointed. + var target = [0, 2, 0]; + + // Up-vector, this tells the camera which direction is 'up'. + // We define the positive y-direction to be up in this example. + var up = [0, 1, 0]; + + g.context.view = g.math.matrix4.lookAt(g.eye, target, up); + + // Make a SecondCounter to provide the time for our animation. + g.counter = g.pack.createObject('SecondCounter'); + g.counter.multiplier = 3; // Speed up time; this is in throws per second. + + // Generate the projection and viewProjection matrices based + // on the g.o3d plugin size by calling onResize(). + onResize(); + + // If we don't check the size of the client area every frame we don't get a + // chance to adjust the perspective matrix fast enough to keep up with the + // browser resizing us. + // TODO(ericu): Switch to using the resize event once it's checked in. + g.client.setRenderCallback(onResize); +} + +/** + * Stops or starts the animation based on the state of an html checkbox. + */ +function updateAnimating() { + var box = document.the_form.check_box; + g.counter.running = box.checked; +} + +/** + * Generates the projection matrix based on the size of the g.o3d plugin + * and calculates the view-projection matrix. + */ +function onResize() { + var newWidth = g.client.width; + var newHeight = g.client.height; + + if (newWidth != g.o3dWidth || newHeight != g.o3dHeight) { + debug('resizing'); + g.o3dWidth = newWidth; + g.o3dHeight = newHeight; + + // Create our projection matrix, with a vertical field of view of 45 degrees + // a near clipping plane of 0.1 and far clipping plane of 100. + g.context.projection = g.math.matrix4.perspective( + 45 * Math.PI / 180, + g.o3dWidth / g.o3dHeight, + 0.1, + 100); + } +} + +/** + * Computes and prepares animation of the pattern input via the html form. If + * the box is checked, this will immediately begin animation as well. + */ +function onComputePattern() { + try { + g.counter.removeAllCallbacks(); + var group = document.the_form.radio_group_hands; + var numHands = -1; + for (var i = 0; i < group.length; ++i) { + if (group[i].checked) { + numHands = parseInt(group[i].value); + } + } + var style = 'even'; + if (document.the_form.pair_hands.checked) { + style = 'pairs'; + } + var patternString = document.getElementById('input_pattern').value; + var patternData = + computeFullPatternFromString(patternString, numHands, style); + startAnimation( + patternData.numBalls, + patternData.numHands, + patternData.duration, + patternData.ballCurveSets, + patternData.handCurveSets); + } catch (ex) { + popup(stringifyObj(ex)); + throw ex; + } + setUpSelection(); +} + +/** + * Removes any callbacks so they don't get called after the page has unloaded. + */ +function cleanup() { + g.client.cleanup(); +} + + +/** + * Dump out a newline-terminated string to the debug console, if available. + * @param {!string} s the string to output. + */ +function debug(s) { + o3djs.dump.dump(s + '\n'); +} + +/** + * Dump out a newline-terminated string to the debug console, if available, + * then display it via an alert. + * @param {!string} s the string to output. + */ +function popup(s) { + debug(s); + window.alert(s); +} + +/** + * If t, throw an exception. + * @param {!bool} t the value to test. + */ +function assert(t) { + if (!t) { + throw new Error('Assertion failed!'); + } +} + +/** + * Convert an object to a string containing a full one-level-deep property + * listing, with values. + * @param {!Object} o the object to convert. + * @return {!string} the converted object. + */ +function stringifyObj(o) { + var s = ''; + for (var i in o) { + s += i + ':' + o[i] + '\n'; + } + return s; +} + +/** + * Add the information in a curve to the params on a shape, such that the vertex + * shader will move the shape along the curve at times after timeBase. + * @param {!Curve} curve the curve the shape should follow. + * @param {!o3d.Shape} shape the shape being moved. + * @param {!number} timeBase the base to subtract from the current time when + * giving the curve calculation its time input. + */ +function setParamCurveInfo(curve, shape, timeBase) { + assert(curve); + assert(shape); + try { + shape.elements[0].getParam('time_base').value = timeBase; + shape.elements[0].getParam('coeff_a').value = + [curve.xEqn.a, curve.yEqn.a, curve.zEqn.a]; + shape.elements[0].getParam('coeff_b').value = + [curve.xEqn.b, curve.yEqn.b, curve.zEqn.b]; + shape.elements[0].getParam('coeff_c').value = + [curve.xEqn.c, curve.yEqn.c, curve.zEqn.c]; + shape.elements[0].getParam('coeff_d').value = + [curve.xEqn.d, curve.yEqn.d, curve.zEqn.d]; + shape.elements[0].getParam('coeff_e').value = + [curve.xEqn.e, curve.yEqn.e, curve.zEqn.e]; + shape.elements[0].getParam('coeff_f').value = + [curve.xEqn.f, curve.yEqn.f, curve.zEqn.f]; + + assert(curve.xEqn.lerpRate == curve.yEqn.lerpRate); + assert(curve.xEqn.lerpRate == curve.zEqn.lerpRate); + shape.elements[0].getParam('coeff_lerp').value = curve.xEqn.lerpRate; + if (curve.xEqn.lerpRate) { + shape.elements[0].getParam('coeff_l_a').value = + [curve.xEqn.lA, curve.yEqn.lA, curve.zEqn.lA]; + shape.elements[0].getParam('coeff_l_b').value = + [curve.xEqn.lB, curve.yEqn.lB, curve.zEqn.lB]; + shape.elements[0].getParam('coeff_l_c').value = + [curve.xEqn.lC, curve.yEqn.lC, curve.zEqn.lC]; + } + } catch (ex) { + debug(ex); + throw ex; + } +} + +/** + * Create the params that the shader expects on the supplied shape's first + * element. + * @param {!o3d.Shape} shape the shape on whose first element to create params. + */ +function createParams(shape) { + shape.elements[0].createParam('coeff_a', 'ParamFloat3').value = [0, 0, 0]; + shape.elements[0].createParam('coeff_b', 'ParamFloat3').value = [0, 0, 0]; + shape.elements[0].createParam('coeff_c', 'ParamFloat3').value = [0, 0, 0]; + shape.elements[0].createParam('coeff_d', 'ParamFloat3').value = [0, 0, 0]; + shape.elements[0].createParam('coeff_e', 'ParamFloat3').value = [0, 0, 0]; + shape.elements[0].createParam('coeff_f', 'ParamFloat3').value = [0, 0, 0]; + shape.elements[0].createParam('coeff_l_a', 'ParamFloat3').value = [0, 0, 0]; + shape.elements[0].createParam('coeff_l_b', 'ParamFloat3').value = [0, 0, 0]; + shape.elements[0].createParam('coeff_l_c', 'ParamFloat3').value = [0, 0, 0]; + shape.elements[0].createParam('coeff_lerp', 'ParamFloat').value = 0; + shape.elements[0].createParam('time_base', 'ParamFloat').value = 0; +} + +/** + * Adjust the number of ball shapes in g.pack. + * @param {!number} numBalls the number of balls desired. + */ +function setNumBalls(numBalls) { + for (var i = 0; i < g.ballShapes.length; ++i) { + g.pack.removeObject(g.ballShapes[i]); + g.client.root.removeShape(g.ballShapes[i]); + } + g.ballShapes = []; + + for (var i = 0; i < numBalls; ++i) { + var shape = o3djs.primitives.createSphere(g.pack, + getMaterial(5 * i), + 0.10, + 70, + 70); + shape.name = 'Ball ' + i; + + // generate the draw elements. + shape.createDrawElements(g.pack, null); + + // Now attach the sphere to the root of the scene graph. + g.client.root.addShape(shape); + + // Create the material params for the shader. + createParams(shape); + + g.ballShapes[i] = shape; + } +} + +/** + * Adjust the number of hand shapes in g.pack. + * @param {!number} numHands the number of hands desired. + */ +function setNumHands(numHands) { + g.counter.removeAllCallbacks(); + + for (var i = 0; i < g.handShapes.length; ++i) { + g.pack.removeObject(g.handShapes[i]); + g.client.root.removeShape(g.handShapes[i]); + } + g.handShapes = []; + + for (var i = 0; i < numHands; ++i) { + var shape = o3djs.primitives.createBox(g.pack, + getMaterial(3 * (i + 1)), + 0.25, + 0.05, + 0.25); + shape.name = 'Hand ' + i; + + // generate the draw elements. + shape.createDrawElements(g.pack, null); + + // Now attach the sphere to the root of the scene graph. + g.client.root.addShape(shape); + + // Create the material params for the shader. + createParams(shape); + + g.handShapes[i] = shape; + } +} + -- cgit v1.1