diff options
Diffstat (limited to 'o3d/samples/box2d-3d')
79 files changed, 16762 insertions, 0 deletions
diff --git a/o3d/samples/box2d-3d/box2d-3d.html b/o3d/samples/box2d-3d/box2d-3d.html new file mode 100644 index 0000000..cfaa622 --- /dev/null +++ b/o3d/samples/box2d-3d/box2d-3d.html @@ -0,0 +1,201 @@ +<!-- +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. +--> + +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title>Box2DJS in 3D</title> + + <!--=============================--> + <!-- Copy this part to your app. --> + <!-- START --> + <!--=============================--> + <!-- libs --> + <script type="text/javascript" src="third_party/prototype-1.6.0.2.js"></script> + + <!-- box2djs --> + <script type="text/javascript" src='third_party/box2d/common/b2Settings.js'></script> + <script type="text/javascript" src='third_party/box2d/common/math/b2Vec2.js'></script> + <script type="text/javascript" src='third_party/box2d/common/math/b2Mat22.js'></script> + <script type="text/javascript" src='third_party/box2d/common/math/b2Math.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2AABB.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2Bound.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2BoundValues.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2Pair.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2PairCallback.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2BufferedPair.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2PairManager.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2BroadPhase.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2Collision.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/Features.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2ContactID.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2ContactPoint.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2Distance.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2Manifold.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2OBB.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/b2Proxy.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/ClipVertex.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/shapes/b2Shape.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/shapes/b2ShapeDef.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/shapes/b2BoxDef.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/shapes/b2CircleDef.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/shapes/b2CircleShape.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/shapes/b2MassData.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/shapes/b2PolyDef.js'></script> + <script type="text/javascript" src='third_party/box2d/collision/shapes/b2PolyShape.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/b2Body.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/b2BodyDef.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/b2CollisionFilter.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/b2Island.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/b2TimeStep.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2ContactNode.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2Contact.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2ContactConstraint.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2ContactConstraintPoint.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2ContactRegister.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2ContactSolver.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2CircleContact.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2Conservative.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2NullContact.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2PolyAndCircleContact.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/contacts/b2PolyContact.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/b2ContactManager.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/b2World.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/b2WorldListener.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2JointNode.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2Joint.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2JointDef.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2DistanceJoint.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2DistanceJointDef.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2Jacobian.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2GearJoint.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2GearJointDef.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2MouseJoint.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2MouseJointDef.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2PrismaticJoint.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2PrismaticJointDef.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2PulleyJoint.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2PulleyJointDef.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2RevoluteJoint.js'></script> + <script type="text/javascript" src='third_party/box2d/dynamics/joints/b2RevoluteJointDef.js'></script> + <!--=============================--> + <!-- Copy this part to your app. --> + <!-- END --> + <!--=============================--> +<script type="text/javascript" src="../o3djs/base.js"></script> + + <!-- demos --> + <script src='demos/manager.js'></script> + <script src='demos/draw_world.js'></script> + <script src='demos/demo_base.js'></script> + <script src='demos/top.js'></script> + <script src='demos/stack.js'></script> + <script src='demos/compound.js'></script> + <script src='demos/pendulum.js'></script> + <script src='demos/crank.js'></script> + <script src='demos/demos.js'></script> + <style type="text/css"> + html, body { + border: 0; + margin: 0; + height: 100%; + } + </style> + </head> +<body onload="init()" onunload="uninit()"> +<table style="width: 100%; height: 100%;"><tr valign="top" style="height: 50px;"><td> +<h1>Box2DJS in 3D</h1> +<p>Based on <a href="http://box2d-js.sourceforge.net/">box2d-js</a>.<br/> +Left Click to create a new object.<br/> +Right Click (shift-click on OSX) to switch to the next demo. +</p> +</td></tr> +<tr style="height: 100%;"><td> +<div id="o3d" style="width: 100%; height: 100%;"></div> +</td></tr></table> +<div style="display:none;"> +<textarea id="shader"> +uniform float4x4 worldViewProj : WorldViewProjection; +uniform float3 lightWorldPos; +uniform float4 lightColor; +uniform float4x4 world : World; +uniform float4x4 view : View; +uniform float4x4 worldIT : WorldInverseTranspose; +uniform float4 emissive; +uniform float4 ambient; +uniform float4 colorMult; +sampler2D diffuseSampler; +uniform float4 specular; +uniform float shininess; + +struct InVertex { + float4 position : POSITION; + float4 normal : NORMAL; + float2 diffuseUV : TEXCOORD0; +}; + +struct OutVertex { + float4 position : POSITION; + float2 diffuseUV : TEXCOORD0; + float3 n : TEXCOORD1; + float3 l : TEXCOORD2; + float3 v : TEXCOORD3; +}; + +OutVertex vs(InVertex IN) { + OutVertex OUT; + OUT.position = mul(IN.position, worldViewProj); + OUT.diffuseUV = IN.diffuseUV; + OUT.n = mul(IN.normal, worldIT).xyz; + OUT.l = lightWorldPos - mul(IN.position, world).xyz; + OUT.v = (view[3] - mul(IN.position, world)).xyz; + return OUT; +} + +float4 ps(OutVertex IN) : COLOR { + float4 diffuse = tex2D(diffuseSampler, IN.diffuseUV) * colorMult; + float3 l = normalize(IN.l); float3 n = normalize(IN.n); + float3 r = normalize(reflect(n, l)); + float3 v = normalize(IN.v); + float4 litR = lit(dot(n, l), dot(r, v), shininess); + return float4((emissive + lightColor * + (ambient + diffuse * litR.y + specular * litR.z)).rgb, + diffuse.a); +} + +// #o3d VertexShaderEntryPoint vs +// #o3d PixelShaderEntryPoint ps +// #o3d MatrixLoadOrder RowMajor +</textarea> +</div> +</body> +</html> diff --git a/o3d/samples/box2d-3d/demos/LICENSE.txt b/o3d/samples/box2d-3d/demos/LICENSE.txt new file mode 100644 index 0000000..c224b40 --- /dev/null +++ b/o3d/samples/box2d-3d/demos/LICENSE.txt @@ -0,0 +1,14 @@ +The zlib/libpng License + +Copyright (c) 2008 ANDO Yasushi + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + diff --git a/o3d/samples/box2d-3d/demos/README.o3d b/o3d/samples/box2d-3d/demos/README.o3d new file mode 100644 index 0000000..2d3f954 --- /dev/null +++ b/o3d/samples/box2d-3d/demos/README.o3d @@ -0,0 +1,5 @@ +This directory contains a subset of Box2D-JS, available at +http://box2d-js.sourceforge.net/ under the zlib/libpng license (see +License.txt). +The software was modified in certain places to make it work with O3D. +Individual files are marked to indicate modifications occured. diff --git a/o3d/samples/box2d-3d/demos/compound.js b/o3d/samples/box2d-3d/demos/compound.js new file mode 100644 index 0000000..e63b983 --- /dev/null +++ b/o3d/samples/box2d-3d/demos/compound.js @@ -0,0 +1,71 @@ +// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi. +// The original version is available at http://box2d-js.sourceforge.net/ under the +// zlib/libpng license (see License.txt). +// This version has been modified to make it work with O3D. + +demos.compound = {}; +demos.compound.createCompoundBall = function(world, x, y) { + var ballSd1 = new b2CircleDef(); + ballSd1.density = 1.0; + ballSd1.radius = 20; + ballSd1.restitution = 0.2; + ballSd1.localPosition.Set(-20, 0); + var ballSd2 = new b2CircleDef(); + ballSd2.density = 1.0; + ballSd2.radius = 20; + ballSd2.restitution = 0.2; + ballSd2.localPosition.Set(20, 0); + var ballBd = new b2BodyDef(); + ballBd.AddShape(ballSd1); + ballBd.AddShape(ballSd2); + ballBd.position.Set(x, y); + // NOTE: Added the following line to create a 3d object to display. + ballBd.userData = g.mgr.createCompoundCylinder(20, -20, 20, 20); + return world.CreateBody(ballBd); +} + +demos.compound.createCompoundPoly = function(world, x, y) { + var points = [[-30, 0], [30, 0], [0, 15]]; + var polySd1 = new b2PolyDef(); + polySd1.vertexCount = points.length; + for (var i = 0; i < points.length; i++) { + polySd1.vertices[i].Set(points[i][0], points[i][1]); + } + polySd1.localRotation = 0.3524 * Math.PI; + var R1 = new b2Mat22(polySd1.localRotation); + polySd1.localPosition = b2Math.b2MulMV(R1, new b2Vec2(30, 0)); + polySd1.density = 1.0; + var polySd2 = new b2PolyDef(); + polySd2.vertexCount = points.length; + for (var i = 0; i < points.length; i++) { + polySd2.vertices[i].Set(points[i][0], points[i][1]); + } + polySd2.localRotation = -0.3524 * Math.PI; + var R2 = new b2Mat22(polySd2.localRotation); + polySd2.localPosition = b2Math.b2MulMV(R2, new b2Vec2(-30, 0)); + var polyBd = new b2BodyDef(); + polyBd.AddShape(polySd1); + polyBd.AddShape(polySd2); + polyBd.position.Set(x,y); + // NOTE: Added the following line to create a 3d object to display. + polyBd.userData = g.mgr.createCompoundWedge(points, + polySd1.localPosition, + polySd1.localRotation, + points, + polySd2.localPosition, + polySd2.localRotation); + return world.CreateBody(polyBd) +} + +demos.compound.initWorld = function(world) { + var i; + for (i = 1; i <= 4; i++) { + demos.compound.createCompoundPoly(world, 150 + 3 * Math.random(), 40 * i); + } + for (i = 1; i <= 4; i++) { + demos.compound.createCompoundBall(world, 350 + 3 * Math.random(), 45 * i); + } +} +demos.InitWorlds.push(demos.compound.initWorld); + + diff --git a/o3d/samples/box2d-3d/demos/crank.js b/o3d/samples/box2d-3d/demos/crank.js new file mode 100644 index 0000000..007c1dc --- /dev/null +++ b/o3d/samples/box2d-3d/demos/crank.js @@ -0,0 +1,79 @@ +// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi. +// The original version is available at http://box2d-js.sourceforge.net/ under the +// zlib/libpng license (see License.txt). +// This version has been modified to make it work with O3D. + +demos.crank = {}; +demos.crank.initWorld = function(world) { + var ground = world.m_groundBody; + + // Define crank. + var sd = new b2BoxDef(); + sd.extents.Set(5, 25); + sd.density = 1.0; + + var bd = new b2BodyDef(); + bd.userData = g.mgr.createBox(5, 25); + bd.AddShape(sd); + + var rjd = new b2RevoluteJointDef(); + + var prevBody = ground; + + bd.position.Set(500/2, 210); + bd.userData = g.mgr.createBox(5, 25); + var body = world.CreateBody(bd); + + rjd.anchorPoint.Set(500/2, 235); + rjd.body1 = prevBody; + rjd.body2 = body; + rjd.motorSpeed = -1.0 * Math.PI; + rjd.motorTorque = 500000000.0; + rjd.enableMotor = true; + world.CreateJoint(rjd); + + prevBody = body; + + // Define follower. + sd.extents.Set(5, 45); + bd.position.Set(500/2, 140); + bd.userData = g.mgr.createBox(5, 45); + body = world.CreateBody(bd); + + rjd.anchorPoint.Set(500/2, 185); + rjd.body1 = prevBody; + rjd.body2 = body; + rjd.enableMotor = false; + world.CreateJoint(rjd); + + prevBody = body; + + // Define piston + sd.extents.Set(20, 20); + bd.position.Set(500/2, 95); + bd.userData = g.mgr.createBox(20, 20); + body = world.CreateBody(bd); + + rjd.anchorPoint.Set(500/2, 95); + rjd.body1 = prevBody; + rjd.body2 = body; + world.CreateJoint(rjd); + + var pjd = new b2PrismaticJointDef(); + pjd.anchorPoint.Set(500/2, 95); + pjd.body1 = ground; + pjd.body2 = body; + pjd.axis.Set(0.0, 1.0); + pjd.motorSpeed = 0.0; // joint friction + pjd.motorForce = 100000.0; + pjd.enableMotor = true; + + world.CreateJoint(pjd); + + // Create a payload + sd.density = 2.0; + bd.position.Set(500/2, 10); + bd.userData = g.mgr.createBox(20, 20); + world.CreateBody(bd); +} +demos.InitWorlds.push(demos.crank.initWorld); diff --git a/o3d/samples/box2d-3d/demos/demo_base.js b/o3d/samples/box2d-3d/demos/demo_base.js new file mode 100644 index 0000000..3379691 --- /dev/null +++ b/o3d/samples/box2d-3d/demos/demo_base.js @@ -0,0 +1,59 @@ +// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi. +// The original version is available at http://box2d-js.sourceforge.net/ under the +// zlib/libpng license (see License.txt). +// This version has been modified to make it work with O3D. + +function createWorld() { + var worldAABB = new b2AABB(); + worldAABB.minVertex.Set(-1000, -1000); + worldAABB.maxVertex.Set(1000, 1000); + var gravity = new b2Vec2(0, 300); + var doSleep = true; + var world = new b2World(worldAABB, gravity, doSleep); + createGround(world); + createBox(world, 0, 125, 10, 250); + createBox(world, 500, 125, 10, 250); + return world; +} + +function createGround(world) { + var groundSd = new b2BoxDef(); + groundSd.extents.Set(250, 50); + groundSd.restitution = 0.2; + var groundBd = new b2BodyDef(); + groundBd.AddShape(groundSd); + groundBd.position.Set(250, 340); + // NOTE: Added the following line to create a 3d object to display. + groundBd.userData = g.mgr.createBox(250, 50); + return world.CreateBody(groundBd) +} + +function createBall(world, x, y) { + var ballSd = new b2CircleDef(); + ballSd.density = 1.0; + ballSd.radius = 20; + ballSd.restitution = 1.0; + ballSd.friction = 0; + var ballBd = new b2BodyDef(); + ballBd.AddShape(ballSd); + ballBd.position.Set(x,y); + // NOTE: Added the following line to create a 3d object to display. + ballBd.userData = g.mgr.createCylinder(ballSd.radius); + return world.CreateBody(ballBd); +} + +function createBox(world, x, y, width, height, fixed) { + if (typeof(fixed) == 'undefined') fixed = true; + var boxSd = new b2BoxDef(); + if (!fixed) boxSd.density = 1.0; + boxSd.extents.Set(width, height); + var boxBd = new b2BodyDef(); + // NOTE: Added the following line to create a 3d object to display. + boxBd.userData = g.mgr.createBox(width, height); + boxBd.AddShape(boxSd); + boxBd.position.Set(x,y); + return world.CreateBody(boxBd) +} + +var demos = {}; +demos.InitWorlds = []; diff --git a/o3d/samples/box2d-3d/demos/demos.js b/o3d/samples/box2d-3d/demos/demos.js new file mode 100644 index 0000000..8834790 --- /dev/null +++ b/o3d/samples/box2d-3d/demos/demos.js @@ -0,0 +1,271 @@ +// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi. +// The original version is available at http://box2d-js.sourceforge.net/ under the +// zlib/libpng license (see License.txt). +// This version has been modified to make it work with O3D. + +o3djs.require('o3djs.util'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.primitives'); +o3djs.require('o3djs.picking'); + +var initId = 0; +var world; +var canvasWidth; +var canvasHeight; +var canvasTop; +var canvasLeft; + +function setupWorld(did) { + var transforms = g.client.getObjectsByClassName('o3d.Transform'); + for (var tt = 0; tt < transforms.length; ++tt) { + var transform = transforms[tt]; + if (transform.clientId != g.root.clientId && + transform.clientId != g.client.root.clientId) { + transform.parent = null; + g.pack.removeObject(transform); + } + } + if (!did) did = 0; + world = createWorld(); + initId += did; + initId %= demos.InitWorlds.length; + if (initId < 0) initId = demos.InitWorlds.length + initId; + demos.InitWorlds[initId](world); +} +function setupNextWorld() { setupWorld(1); } +function setupPrevWorld() { setupWorld(-1); } +function step(elapsedTime) { + // NOTE: changed to use the renderEvent's elapsed time instead of + // javascript timers. The check below makes the physics never do a time step + // slower that 1/30th of a second because collision bugs will happen. + if (elapsedTime > 1 / 30) { + elapsedTime = 1 / 30; + } + var stepping = false; + var timeStep = elapsedTime; + var iteration = 1; + if (world) { + world.Step(timeStep, iteration); + drawWorld(world); + } +} + +// When the sample is running in V8, window.g is the browser's global object and +// g is v8's. When the sample is running only in the browser, they are the same +// object. +window.g = {}; + +// A global object to track stuff with. +var g = { + o3dWidth: -1, // width of our client area + o3dHeight: -1, // height of our client area + materials: [], // all our materials + shapes: [] // all our shapes +}; + +/** + * Sets up the o3d stuff. + * @param {HTML element} o3dElement The o3d object element. + */ +function setupO3D(o3dElement) { + // Initializes global variables and libraries. + g.o3dElement = o3dElement; + g.o3d = o3dElement.o3d; + g.math = o3djs.math; + g.client = o3dElement.client; + g.mgr = new O3DManager(); + + // The browser needs to be able to see these globals so that it can + // call unload later. + window.g.client = g.client; + + // Get the width and height of our client area. + g.o3dWidth = g.client.width; + g.o3dHeight = g.client.height; + + // Creates 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); + + // Create an object to hold global params. + g.globalParamObject = g.pack.createObject('ParamObject'); + g.lightWorldPosParam = g.globalParamObject.createParam('lightWorldPos', + 'ParamFloat3'); + g.lightColorParam = g.globalParamObject.createParam('lightColor', + 'ParamFloat4'); + g.lightWorldPosParam.value = [200, -1500, -1000]; + g.lightColorParam.value = [1, 1, 1.0, 1.0]; + + // Create Materials. + g.materials = []; + var material = g.pack.createObject('Material'); + var effect = g.pack.createObject('Effect'); + effect.loadFromFXString(document.getElementById('shader').value); + material.effect = effect; + effect.createUniformParameters(material); + material.getParam('lightWorldPos').bind(g.lightWorldPosParam); + material.getParam('lightColor').bind(g.lightColorParam); + material.getParam('emissive').set(0, 0, 0, 1); + material.getParam('ambient').set(0, 0, 0, 1); + material.getParam('specular').set(1, 1, 1, 1); + material.getParam('shininess').value = 20; + material.drawList = g.viewInfo.performanceDrawList; + g.materials[0] = material; + + // Create a kind of checkboardish texture. + var pixels = []; + for (var y = 0; y < 32; ++y) { + for (var x = 0; x < 32; ++x) { + var offset = (y * 32 + x) * 3; // rgb + var u = x / 32 * Math.PI * 0.5; + var v = y / 32 * Math.PI * 0.5; + pixels[offset + 0] = 1;//Math.sin(Math.PI * 32 / x) * 0.2 + 0.8; // red + pixels[offset + 1] = Math.floor(x / 8) % 2 * 0.2 + 0.8; // green + pixels[offset + 2] = Math.floor(y / 8) % 2 * 0.5 + 0.5; // blue + } + } + var texture = g.pack.createTexture2D(32, 32, g.o3d.Texture.XRGB8, 1, false); + texture.set(0, pixels); + var samplerParam = material.getParam('diffuseSampler'); + var sampler = g.pack.createObject('Sampler'); + samplerParam.value = sampler; + sampler.texture = texture; + + // Creates a transform to put our data on. + g.root = g.pack.createObject('Transform'); + g.root.parent = g.client.root; + + // make a cube for drawing lines + g.lineShape = o3djs.primitives.createRainbowCube( + g.pack, + g.materials[0], + 1, + g.math.matrix4.translation([0, 0.5, 0])); + + // Set the projection matrix + g.viewInfo.drawContext.projection = g.math.matrix4.perspective( + g.math.degToRad(45), + g.o3dWidth / g.o3dHeight, + 0.1, + 10000); + + // Set the view matrix. + g.viewInfo.drawContext.view = g.math.matrix4.lookAt( + [100, -50, -600], + [250, 80, 0], + [0, -1, 0]); + + // Make copies of the view and projection matrix to get fast access to them. + g.projectionMatrix = g.math.matrix4.copy(g.viewInfo.drawContext.projection); + g.viewMatrix = g.math.matrix4.copy(g.viewInfo.drawContext.view); + + // Make a bounding box to use for picking. + g.worldBox = g.o3d.BoundingBox([0, -100, 0], + [500, 250, 5]); + + // Steps the physics with a time of zero. This is left over from the orignial + // code. + step(0); + + g.client.setRenderCallback(o3dOnRender); +} + +/** + * Called just before rendering each frame. + * @param {o3d.RenderEvent} renderEvent The render event passed by + * o3d. + */ +function o3dOnRender(renderEvent) { + var newWidth = g.client.width; + var newHeight = g.client.height; + if (newWidth != g.o3dWidth || newHeight != g.o3dHeight) { + g.o3dWidth = newWidth; + g.o3dHeight = newHeight; + + // Adjust the projection matrix. + var projectionMatrix = g.math.matrix4.perspective(g.math.degToRad(45), + g.o3dWidth / g.o3dHeight, + 0.1, + 10000); + g.viewInfo.drawContext.projection = projectionMatrix; + g.projectionMatrix = projectionMatrix; + } + step(renderEvent.elapsedTime); +} + +/** + * This is the original setup code modified to work with o3d. + * @param {Array} clientElements Array of o3d objects. + */ +function setupStep2(clientElements) { + var canvasElm = clientElements[0]; + canvasElm.id = 'canvas'; + setupO3D(canvasElm); + setupWorld(); + canvasWidth = g.client.width; + canvasHeight = g.client.height; + canvasTop = parseInt(canvasElm.style.top); + canvasLeft = parseInt(canvasElm.style.left); + o3djs.event.addEventListener(canvasElm, 'mousedown', function(e) { + if (e.shiftKey || e.button == g.o3d.Event.BUTTON_RIGHT) { + setupPrevWorld(); + } else { + createNewRandomObject(world, e); + } + }); + + window.g_finished = true; // for selenium +} + +/** + * Creates a new random object using 3d picking to figure out where to start it. + * @param {Object} world A B2World object. + * @param {Object} offset Mouse position relative to o3d area. + */ +function createNewRandomObject(world, offset) { + var worldRay = o3djs.picking.clientPositionToWorldRayEx( + offset.x, + offset.y, + g.viewMatrix, + g.projectionMatrix, + g.o3dWidth, + g.o3dHeight); + var rayIntersectionInfo = g.worldBox.intersectRay(worldRay.near, + worldRay.far); + if (rayIntersectionInfo.intersected) { + var position = rayIntersectionInfo.position; + if (Math.random() < 0.5) { + demos.top.createBall(world, position[0], position[1]); + } else { + createBox(world, position[0], position[1], 10, 10, false); + } + } +} + +function init() { + window.g_finished = false; // for selenium. + + // 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. This sample cannot + // currently run in V8 on IE because it access the DOM. + if (!o3djs.base.IsMSIE()) { + o3djs.util.setMainEngine(o3djs.util.Engine.V8); + o3djs.util.addScriptUri('third_party'); + o3djs.util.addScriptUri('demos'); + o3djs.util.addScriptUri('style'); + } + + o3djs.util.makeClients(setupStep2); +} + +function uninit() { + if (g.client) { + g.client.cleanup(); + } +} diff --git a/o3d/samples/box2d-3d/demos/draw_world.js b/o3d/samples/box2d-3d/demos/draw_world.js new file mode 100644 index 0000000..b49ef1a --- /dev/null +++ b/o3d/samples/box2d-3d/demos/draw_world.js @@ -0,0 +1,90 @@ +// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi. +// The original version is available at http://box2d-js.sourceforge.net/ under the +// zlib/libpng license (see License.txt). +// This version has been modified to make it work with O3D. + +// NOTE: Changed this file pretty significantly. The original uses a +// canvas element and drew lines on it. This one just updates the positions and +// orientations of o3d objects to match the physics as well as using a +// scaled box to stand in for joints. + +/** + * Draws all the object in the world. + * @param {Object} world A B2World object managing the physics world. + */ +function drawWorld(world) { + for (var j = world.m_jointList; j; j = j.m_next) { + drawJoint(j); + } + for (var b = world.m_bodyList; b; b = b.m_next) { + var o3dShape = b.GetUserData(); + if (o3dShape) { + o3dShape.updateTransform(b); + } + } +} +/** + * Scales a joint transform to represnet a line. + * @param {Object} transformInfo An object with o3d information for the + * joint. + * @param {Object} p1 An object with x and y fields that specify the origin of + * the joint in 2d. + * @param {Object} p2 An object with x an dy fields that specify the far end of + * the joint in 2d. + */ +function scaleJointTransform(transformInfo, p1, p2) { + var dx = p2.x - p1.x; + var dy = p2.y - p1.y; + var length = Math.sqrt(dx * dx + dy * dy); + var transform = transformInfo.transform; + transform.identity(); + transform.translate(p1.x, p1.y, 0); + transform.rotateZ(Math.atan2(-dx, dy)); + transform.scale(2, length, 2); +} + +/** + * Draws a joint + * @param {Object} joint A b2Joint object representing a joint. + */ +function drawJoint(joint) { + var transformInfo = joint.m_o3dTransformInfo; + if (!transformInfo) { + // This joint did not already have something in o3d to represent it + // so we create one here. + var transform = g.pack.createObject('Transform'); + transform.parent = g.root; + transform.addShape(g.lineShape); + transformInfo = { + transform: transform + }; + joint.m_o3dTransformInfo = transformInfo; + } + var b1 = joint.m_body1; + var b2 = joint.m_body2; + var x1 = b1.m_position; + var x2 = b2.m_position; + var p1 = joint.GetAnchor1(); + var p2 = joint.GetAnchor2(); + switch (joint.m_type) { + case b2Joint.e_distanceJoint: + scaleJointTransform(transformInfo, p1, p2); + break; + + case b2Joint.e_pulleyJoint: + break; + + default: + if (b1 == world.m_groundBody) { + scaleJointTransform(transformInfo, p1, x2); + } + else if (b2 == world.m_groundBody) { + scaleJointTransform(transformInfo, p1, x1); + } + else { + scaleJointTransform(transformInfo, x1, x2); + } + break; + } +} + diff --git a/o3d/samples/box2d-3d/demos/manager.js b/o3d/samples/box2d-3d/demos/manager.js new file mode 100644 index 0000000..8fc177b --- /dev/null +++ b/o3d/samples/box2d-3d/demos/manager.js @@ -0,0 +1,210 @@ +// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi. +// The original version is available at http://box2d-js.sourceforge.net/ under the +// zlib/libpng license (see License.txt). +// This version has been modified to make it work with O3D. + +/** + * O3DManager manages o3d objects for this demo. + * @constructor + */ +function O3DManager() { + this.shapes = []; +} + +/** + * Gets or creates a cylinder shape. If a cylinder of the same radius already + * exists that one will be returned, otherwise a new one will be created. + * @param {number} radius Radius of cylinder to create. + * @return {o3d.Shape} The shape. + */ +O3DManager.prototype.createCylinder = function(radius) { + var id = 'cylinder-' + radius; + var shape = this.shapes[id]; + if (!shape) { + shape = o3djs.primitives.createCylinder(g.pack, + g.materials[0], + radius, 40, 20, 1, + [[1, 0, 0, 0], + [0, 0, 1, 0], + [0, -1, 0, 0], + [0, 0, 0, 1]]); + this.shapes[id] = shape; + } + return new O3DShape({shape: shape}); +}; + +/** + * Gets or creates a compound cylinder shape. If a compound cylinder shape of + * the same parameters already exists that one will be returned, otherwise a new + * one will be created. + * @param {number} radius1 Radius of first cylinder. + * @param {number} offset1 X Offset for first cylinder. + * @param {number} radius2 Radius of second cylinder. + * @param {number} offset2 X Offset for second cylinder. + * @return {o3d.Shape} The shape. + */ +O3DManager.prototype.createCompoundCylinder = function(radius1, + offset1, + radius2, + offset2) { + var id = 'compoundCylinder-' + radius1 + '-' + offset1 + + '-' + radius2 + '-' + offset2; + var shape = this.shapes[id]; + if (!shape) { + shape = o3djs.primitives.createCylinder( + g.pack, g.materials[0], radius1, 40, 20, 1, + [[1, 0, 0, 0], + [0, 0, 1, 0], + [0, -1, 0, 0], + [offset1, 0, 0, 1]]); + shape2 = o3djs.primitives.createCylinder( + g.pack, g.materials[0], radius2, 40, 20, 1, + [[1, 0, 0, 0], + [0, 0, 1, 0], + [0, -1, 0, 0], + [offset2, 0, 0, 1]]); + shape2.elements[0].owner = shape; + g.pack.removeObject(shape2); + this.shapes[id] = shape; + } + return new O3DShape({shape: shape}); +}; + +/** + * Gets or creates a box shape. If a box of the same width and height already + * exists that one will be returned, otherwise a new one will be created. + * @param {number} width Width of box. + * @param {number} height Height of box. + * @return {o3d.Shape} The shape. + */ +O3DManager.prototype.createBox = function(width, height) { + var name = 'box-' + width + '-' + height; + var shape = this.shapes[name]; + if (!shape) { + shape = o3djs.primitives.createBox(g.pack, + g.materials[0], + width * 2, height * 2, 40); + this.shapes[name] = shape; + } + return new O3DShape({shape: shape}); +}; + +/** + * Gets or creates a wedge shape. If a wedge of the same parametrs already + * exists that one will be returned, otherwise a new one will be created. + * @param {Array} points Array of points in the format + * [[x1, y1], [x2, y2], [x3, y3]] that describe a 2d triangle. + * @return {o3d.Shape} The shape. + */ +O3DManager.prototype.createWedge = function(points) { + var name = 'wedge'; + for (var pp = 0; pp < points.length; ++pp) { + name += '-' + points[pp][0] + '-' + points[pp][1]; + } + var shape = this.shapes[name]; + if (!shape) { + shape = o3djs.primitives.createPrism(g.pack, + g.materials[0], + points, 40); + this.shapes[name] = shape; + } + return new O3DShape({shape: shape}); +}; + +/** + * Gets or creates a compound wedge shape (2 wedges). If a compound wedge of the + * same parametrs already exists that one will be returned, otherwise a new one + * will be created. + * @param {Array} points1 Array of points that describe a 2d triangle for the + * first wedge in the format [[x1, y1], [x2, y2], [x3, y3]] . + * @param {Object} position1 An object with x and y properties used to offset + * the first wedge. + * @param {number} rotation1 Rotation in radians to rotate the first wedge on + * the z axis. + * @param {Array} points2 Array of points that describe a 2d triangle for the + * second wedge in the format [[x1, y1], [x2, y2], [x3, y3]] . + * @param {Object} position2 An object with x and y properties used to offset + * the second wedge. + * @param {number} rotation2 Rotation in radians to rotate the second wedge on + * the z axis. + * @return {o3d.Shape} The shape. + */ +O3DManager.prototype.createCompoundWedge = function(points1, + position1, + rotation1, + points2, + position2, + rotation2) { + var name = 'compoundWedge'; + for (var pp = 0; pp < points1.length; ++pp) { + name += '-' + points1[pp][0] + '-' + points1[pp][1]; + } + name += '-' + position1.x + '-' + position1.y + '-' + rotation1; + for (var pp = 0; pp < points2.length; ++pp) { + name += '-' + points2[pp][0] + '-' + points2[pp][1]; + } + name += '-' + position2.x + '-' + position2.y + '-' + rotation2; + var shape = this.shapes[name]; + if (!shape) { + shape = o3djs.primitives.createPrism( + g.pack, + g.materials[0], + points1, + 40, + g.math.matrix4.mul( + g.math.matrix4.rotationZ(rotation1), + g.math.matrix4.translation([position1.x, position1.y, 0]))); + shape2 = o3djs.primitives.createPrism( + g.pack, + g.materials[0], + points2, + 40, + g.math.matrix4.mul( + g.math.matrix4.rotationZ(rotation2), + g.math.matrix4.translation([position2.x, position2.y, 0]))); + shape2.elements[0].owner = shape; + g.pack.removeObject(shape2); + this.shapes[name] = shape; + } + return new O3DShape({shape: shape}); +}; + +/** + * An O3DShape manages an O3D shape for the demo. + * @constructor + * @param {Object} spec An object that contains the fields needed to create the + * O3DShape. Currently only the field "shape" is needed. + */ +function O3DShape(spec) { + this.init(spec); +} + +/** + * Initializes an O3DShape + * @param {Object} spec An object that contains the fields needed to create the + * O3DShape. Currently only the field "shape" is needed. + */ +O3DShape.prototype.init = function(spec) { + this.transform = g.pack.createObject('Transform'); + this.transform.parent = g.root; + this.transform.addShape(spec.shape); + this.transform.createParam('colorMult', 'ParamFloat4').value = + [Math.random() * 0.8 + 0.2, + Math.random() * 0.8 + 0.2, + Math.random() * 0.8 + 0.2, + 1]; +}; + +/** + * Updates the position and orientation of an O3DShape. + * @param {Object} body A B2Body object from the Box2djs library. + */ +O3DShape.prototype.updateTransform = function(body) { + var transform = this.transform; + var position = body.GetOriginPosition(); + transform.identity(); + transform.translate(position.x, position.y, 0); + transform.rotateZ(body.GetRotation()); +}; + + diff --git a/o3d/samples/box2d-3d/demos/pendulum.js b/o3d/samples/box2d-3d/demos/pendulum.js new file mode 100644 index 0000000..730d039 --- /dev/null +++ b/o3d/samples/box2d-3d/demos/pendulum.js @@ -0,0 +1,25 @@ +// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi. +// The original version is available at http://box2d-js.sourceforge.net/ under the +// zlib/libpng license (see License.txt). +// This version has been modified to make it work with O3D. + +demos.pendulum = {}; +demos.pendulum.initWorld = function(world) { + var i; + var ground = world.GetGroundBody(); + var jointDef = new b2RevoluteJointDef(); + var L = 150; + for (i = 0; i < 4; i++) { + jointDef.anchorPoint.Set(250 + 40 * i, 200 - L); + jointDef.body1 = ground; + jointDef.body2 = createBall(world, 250 + 40 * i, 200); + world.CreateJoint(jointDef); + } + jointDef.anchorPoint.Set(250 - 40, 200 - L); + jointDef.body1 = ground; + jointDef.body2 = createBall(world, 250 - 40 - L, 200 - L); + world.CreateJoint(jointDef); +} +demos.InitWorlds.push(demos.pendulum.initWorld); + + diff --git a/o3d/samples/box2d-3d/demos/stack.js b/o3d/samples/box2d-3d/demos/stack.js new file mode 100644 index 0000000..ffee25b --- /dev/null +++ b/o3d/samples/box2d-3d/demos/stack.js @@ -0,0 +1,37 @@ +// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi. +// The original version is available at http://box2d-js.sourceforge.net/ under the +// zlib/libpng license (see License.txt). +// This version has been modified to make it work with O3D. + +demos.stack = {}; +demos.stack.initWorld = function(world) { + var sd = new b2BoxDef(); + var bd = new b2BodyDef(); + bd.AddShape(sd); + sd.density = 1.0; + sd.friction = 0.5; + sd.extents.Set(10, 10); + + var i; + for (i = 0; i < 8; i++) { + bd.position.Set(500/2-Math.random()*2-1, (250-5-i*22)); + // NOTE: Added the following line to create a 3d object to display. + bd.userData = g.mgr.createBox(10, 10); + world.CreateBody(bd); + } + for (i = 0; i < 8; i++) { + bd.position.Set(500/2-100-Math.random()*5+i, (250-5-i*22)); + // NOTE: Added the following line to create a 3d object to display. + bd.userData = g.mgr.createBox(10, 10); + world.CreateBody(bd); + } + for (i = 0; i < 8; i++) { + bd.position.Set(500/2+100+Math.random()*5-i, (250-5-i*22)); + // NOTE: Added the following line to create a 3d object to display. + bd.userData = g.mgr.createBox(10, 10); + world.CreateBody(bd); + } +} +demos.InitWorlds.push(demos.stack.initWorld); + + diff --git a/o3d/samples/box2d-3d/demos/top.js b/o3d/samples/box2d-3d/demos/top.js new file mode 100644 index 0000000..e569e7e --- /dev/null +++ b/o3d/samples/box2d-3d/demos/top.js @@ -0,0 +1,53 @@ +// This file comes from Box2D-JS, Copyright (c) 2008 ANDO Yasushi. +// The original version is available at http://box2d-js.sourceforge.net/ under the +// zlib/libpng license (see License.txt). +// This version has been modified to make it work with O3D. + +demos.top = {}; +demos.top.createBall = function(world, x, y, rad, fixed) { + var ballSd = new b2CircleDef(); + if (!fixed) ballSd.density = 1.0; + ballSd.radius = rad || 10; + ballSd.restitution = 0.2; + var ballBd = new b2BodyDef(); + ballBd.AddShape(ballSd); + ballBd.position.Set(x,y); + // NOTE: Added the following line to create a 3d object to display. + ballBd.userData = g.mgr.createCylinder(ballSd.radius); + return world.CreateBody(ballBd); +}; +demos.top.createPoly = function(world, x, y, points, fixed) { + var polySd = new b2PolyDef(); + if (!fixed) polySd.density = 1.0; + polySd.vertexCount = points.length; + for (var i = 0; i < points.length; i++) { + polySd.vertices[i].Set(points[i][0], points[i][1]); + } + var polyBd = new b2BodyDef(); + if (points.length == 3) { + // NOTE: Added the following line to create a 3d object to display. + polyBd.userData = g.mgr.createWedge(points); + } + polyBd.AddShape(polySd); + polyBd.position.Set(x,y); + return world.CreateBody(polyBd) +}; +demos.top.initWorld = function(world) { + demos.top.createBall(world, 350, 100, 50, true); + demos.top.createPoly(world, 100, 100, [[0, 0], [10, 30], [-10, 30]], true); + demos.top.createPoly(world, 150, 150, [[0, 0], [10, 30], [-10, 30]], true); + var pendulum = createBox(world, 150, 100, 20, 20, false); + var jointDef = new b2RevoluteJointDef(); + jointDef.body1 = pendulum; + jointDef.body2 = world.GetGroundBody(); + jointDef.anchorPoint = pendulum.GetCenterPosition(); + world.CreateJoint(jointDef); + + var seesaw = demos.top.createPoly(world, 300, 200, [[0, 0], [100, 30], [-100, 30]]); + jointDef.body1 = seesaw; + jointDef.anchorPoint = seesaw.GetCenterPosition(); + world.CreateJoint(jointDef); +}; +demos.InitWorlds.push(demos.top.initWorld); + + diff --git a/o3d/samples/box2d-3d/third_party/box2d/LICENSE.txt b/o3d/samples/box2d-3d/third_party/box2d/LICENSE.txt new file mode 100644 index 0000000..c224b40 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/LICENSE.txt @@ -0,0 +1,14 @@ +The zlib/libpng License + +Copyright (c) 2008 ANDO Yasushi + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + diff --git a/o3d/samples/box2d-3d/third_party/box2d/README.o3d b/o3d/samples/box2d-3d/third_party/box2d/README.o3d new file mode 100644 index 0000000..0f3fdc7 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/README.o3d @@ -0,0 +1,5 @@ +This directory contains a subset of Box2D-JS, available at +http://box2d-js.sourceforge.net/ under the zlib/libpng license (see +License.txt). +The software was not modified, but only the portions under js/box2d in the +original distribution are here. diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/ClipVertex.js b/o3d/samples/box2d-3d/third_party/box2d/collision/ClipVertex.js new file mode 100644 index 0000000..7c944dd --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/ClipVertex.js @@ -0,0 +1,35 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var ClipVertex = Class.create(); +ClipVertex.prototype = +{ + v: new b2Vec2(), + id: new b2ContactID(), + initialize: function() { + // initialize instance variables for references + this.v = new b2Vec2(); + this.id = new b2ContactID(); + // +}}; + + diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/Features.js b/o3d/samples/box2d-3d/third_party/box2d/collision/Features.js new file mode 100644 index 0000000..b0042a6 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/Features.js @@ -0,0 +1,61 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + +// We use contact ids to facilitate warm starting. +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() {}}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2AABB.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2AABB.js new file mode 100644 index 0000000..c22ccc3 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2AABB.js @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +// A manifold for two touching convex shapes. +var b2AABB = Class.create(); +b2AABB.prototype = +{ + IsValid: function(){ + //var d = b2Math.SubtractVV(this.maxVertex, this.minVertex); + 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() { + // initialize instance variables for references + this.minVertex = new b2Vec2(); + this.maxVertex = new b2Vec2(); + // +}}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2Bound.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Bound.js new file mode 100644 index 0000000..b203323 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Bound.js @@ -0,0 +1,43 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +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() {}} diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2BoundValues.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2BoundValues.js new file mode 100644 index 0000000..25294f2 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2BoundValues.js @@ -0,0 +1,31 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +var b2BoundValues = Class.create(); +b2BoundValues.prototype = { + lowerValues: [0,0], + upperValues: [0,0], + + initialize: function() { + // initialize instance variables for references + this.lowerValues = [0,0]; + this.upperValues = [0,0]; + // +}} diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2BroadPhase.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2BroadPhase.js new file mode 100644 index 0000000..f562329 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2BroadPhase.js @@ -0,0 +1,898 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +/* +This broad phase uses the Sweep and Prune algorithm in: +Collision Detection in Interactive 3D Environments by Gino van den Bergen +Also, some ideas, such integral values for fast compares comes from +Bullet (http:/www.bulletphysics.com). +*/ + + +// Notes: +// - we use bound arrays instead of linked lists for cache coherence. +// - we use quantized integral values for fast compares. +// - we use short indices rather than pointers to save memory. +// - we use a stabbing count for fast overlap queries (less than order N). +// - we also use a time stamp on each proxy to speed up the registration of +// overlap query results. +// - where possible, we compare bound indices instead of values to reduce +// cache misses (TODO_ERIN). +// - no broadphase is perfect and neither is this one: it is not great for huge +// worlds (use a multi-SAP instead), it is not great for large objects. + +var b2BroadPhase = Class.create(); +b2BroadPhase.prototype = +{ +//public: + initialize: function(worldAABB, callback){ + // initialize instance variables for references + 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(); + // + + //b2Settings.b2Assert(worldAABB.IsValid()); + var i = 0; + + this.m_pairManager.Initialize(this, callback); + + this.m_worldAABB = worldAABB; + + this.m_proxyCount = 0; + + // query results + for (i = 0; i < b2Settings.b2_maxProxies; i++){ + this.m_queryResults[i] = 0; + } + + // bounds array + 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 d = b2Math.SubtractVV(worldAABB.maxVertex, worldAABB.minVertex); + 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; + }, + //~b2BroadPhase(); + + // Use this to see if your proxy is in range. If it is not in range, + // it should be destroyed. Otherwise you may get O(m^2) pairs, where m + // is the number of proxies that are out of range. + InRange: function(aabb){ + //var d = b2Math.b2MaxV(b2Math.SubtractVV(aabb.minVertex, this.m_worldAABB.maxVertex), b2Math.SubtractVV(this.m_worldAABB.minVertex, aabb.maxVertex)); + 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; + }, + + // Get a single proxy. Returns NULL if the id is invalid. + GetProxy: function(proxyId){ + if (proxyId == b2Pair.b2_nullProxy || this.m_proxyPool[proxyId].IsValid() == false) + { + return null; + } + + return this.m_proxyPool[ proxyId ]; + }, + + // Create and destroy proxies. These call Flush first. + CreateProxy: function(aabb, userData){ + var index = 0; + var proxy; + + //b2Settings.b2Assert(this.m_proxyCount < b2_maxProxies); + //b2Settings.b2Assert(this.m_freeProxy != b2Pair.b2_nullProxy); + + 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]; + + // Replace memmove calls + //memmove(bounds + upperIndex + 2, bounds + upperIndex, (edgeCount - upperIndex) * sizeof(b2Bound)); + var tArr = new Array(); + var j = 0; + var tEnd = boundCount - upperIndex + var tBound1; + var tBound2; + // make temp array + 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; + } + // move temp array back in to bounds + tEnd = tArr.length; + var tIndex = upperIndex+2; + for (j = 0; j < tEnd; j++){ + //bounds[tIndex+j] = tArr[j]; + tBound2 = tArr[j]; + tBound1 = bounds[tIndex+j] + tBound1.value = tBound2.value; + tBound1.proxyId = tBound2.proxyId; + tBound1.stabbingCount = tBound2.stabbingCount; + } + //memmove(bounds + lowerIndex + 1, bounds + lowerIndex, (upperIndex - lowerIndex) * sizeof(b2Bound)); + // make temp array + 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; + } + // move temp array back in to bounds + tEnd = tArr.length; + tIndex = lowerIndex+1; + for (j = 0; j < tEnd; j++){ + //bounds[tIndex+j] = tArr[j]; + tBound2 = tArr[j]; + tBound1 = bounds[tIndex+j] + tBound1.value = tBound2.value; + tBound1.proxyId = tBound2.proxyId; + tBound1.stabbingCount = tBound2.stabbingCount; + } + + // The upper index has increased because of the lower bound insertion. + ++upperIndex; + + // Copy in the new bounds. + 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; + + // Adjust the stabbing count between the new bounds. + for (index = lowerIndex; index < upperIndex; ++index) + { + bounds[index].stabbingCount++; + } + + // Adjust the all the affected bound indices. + 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; + + //b2Settings.b2Assert(this.m_queryResultCount < b2Settings.b2_maxProxies); + + for (var i = 0; i < this.m_queryResultCount; ++i) + { + //b2Settings.b2Assert(this.m_queryResults[i] < b2_maxProxies); + //b2Settings.b2Assert(this.m_proxyPool[this.m_queryResults[i]].IsValid()); + + this.m_pairManager.AddBufferedPair(proxyId, this.m_queryResults[i]); + } + + this.m_pairManager.Commit(); + + // Prepare for next query. + this.m_queryResultCount = 0; + this.IncrementTimeStamp(); + + return proxyId; + }, + + DestroyProxy: function(proxyId){ + + //b2Settings.b2Assert(0 < this.m_proxyCount && this.m_proxyCount <= b2_maxProxies); + + var proxy = this.m_proxyPool[ proxyId ]; + //b2Settings.b2Assert(proxy.IsValid()); + + 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; + + // replace memmove calls + //memmove(bounds + lowerIndex, bounds + lowerIndex + 1, (upperIndex - lowerIndex - 1) * sizeof(b2Bound)); + var tArr = new Array(); + var j = 0; + var tEnd = upperIndex - lowerIndex - 1; + var tBound1; + var tBound2; + // make temp array + 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; + } + // move temp array back in to bounds + tEnd = tArr.length; + var tIndex = lowerIndex; + for (j = 0; j < tEnd; j++){ + //bounds[tIndex+j] = tArr[j]; + tBound2 = tArr[j]; + tBound1 = bounds[tIndex+j] + tBound1.value = tBound2.value; + tBound1.proxyId = tBound2.proxyId; + tBound1.stabbingCount = tBound2.stabbingCount; + } + //memmove(bounds + upperIndex-1, bounds + upperIndex + 1, (edgeCount - upperIndex - 1) * sizeof(b2Bound)); + // make temp array + 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; + } + // move temp array back in to bounds + tEnd = tArr.length; + tIndex = upperIndex-1; + for (j = 0; j < tEnd; j++){ + //bounds[tIndex+j] = tArr[j]; + tBound2 = tArr[j]; + tBound1 = bounds[tIndex+j] + tBound1.value = tBound2.value; + tBound1.proxyId = tBound2.proxyId; + tBound1.stabbingCount = tBound2.stabbingCount; + } + + // Fix bound indices. + 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; + } + } + + // Fix stabbing count. + tEnd = upperIndex - 1; + for (var index2 = lowerIndex; index2 < tEnd; ++index2) + { + bounds[index2].stabbingCount--; + } + + // this.Query for pairs to be removed. lowerIndex and upperIndex are not needed. + // make lowerIndex and upper output using an array and do this for others if compiler doesn't pick them up + this.Query([0], [0], lowerValue, upperValue, bounds, boundCount - 2, axis); + } + + //b2Settings.b2Assert(this.m_queryResultCount < b2Settings.b2_maxProxies); + + for (var i = 0; i < this.m_queryResultCount; ++i) + { + //b2Settings.b2Assert(this.m_proxyPool[this.m_queryResults[i]].IsValid()); + + this.m_pairManager.RemoveBufferedPair(proxyId, this.m_queryResults[i]); + } + + this.m_pairManager.Commit(); + + // Prepare for next query. + this.m_queryResultCount = 0; + this.IncrementTimeStamp(); + + // Return the proxy to the pool. + 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; + }, + + + // Call this.MoveProxy times like, then when you are done + // call this.Commit to finalized the proxy pairs (for your time step). + 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) + { + //b2Settings.b2Assert(false); + return; + } + + if (aabb.IsValid() == false) + { + //b2Settings.b2Assert(false); + return; + } + + var boundCount = 2 * this.m_proxyCount; + + var proxy = this.m_proxyPool[ proxyId ]; + // Get new bound values + var newValues = new b2BoundValues(); + this.ComputeBounds(newValues.lowerValues, newValues.upperValues, aabb); + + // Get old bound values + 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; + + // + // Expanding adds overlaps + // + + // Should we move the lower bound down? + 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]--; + + // swap + //var temp = bound; + //bound = prevEdge; + //prevEdge = temp; + bound.Swap(prevBound); + //b2Math.b2Swap(bound, prevEdge); + --index; + } + } + + // Should we move the upper bound up? + 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]++; + // swap + //var temp = bound; + //bound = nextEdge; + //nextEdge = temp; + bound.Swap(nextBound); + //b2Math.b2Swap(bound, nextEdge); + index++; + } + } + + // + // Shrinking removes overlaps + // + + // Should we move the lower bound up? + 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]++; + // swap + //var temp = bound; + //bound = nextEdge; + //nextEdge = temp; + bound.Swap(nextBound); + //b2Math.b2Swap(bound, nextEdge); + index++; + } + } + + // Should we move the upper bound down? + 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]--; + // swap + //var temp = bound; + //bound = prevEdge; + //prevEdge = temp; + bound.Swap(prevBound); + //b2Math.b2Swap(bound, prevEdge); + index--; + } + } + } + }, + + Commit: function(){ + this.m_pairManager.Commit(); + }, + + // this.Query an AABB for overlapping proxies, returns the user data and + // the count, up to the supplied maximum count. + 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); + + //b2Settings.b2Assert(this.m_queryResultCount < b2Settings.b2_maxProxies); + + var count = 0; + for (var i = 0; i < this.m_queryResultCount && count < maxCount; ++i, ++count) + { + //b2Settings.b2Assert(this.m_queryResults[i] < b2Settings.b2_maxProxies); + var proxy = this.m_proxyPool[ this.m_queryResults[i] ]; + //b2Settings.b2Assert(proxy.IsValid()); + userData[i] = proxy.userData; + } + + // Prepare for next query. + 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]; + //b2Settings.b2Assert(i == 0 || bounds[i-1].value <= bound->value); + //b2Settings.b2Assert(bound->proxyId != b2_nullProxy); + //b2Settings.b2Assert(this.m_proxyPool[bound->proxyId].IsValid()); + + if (bound.IsLower() == true) + { + //b2Settings.b2Assert(this.m_proxyPool[bound.proxyId].lowerBounds[axis] == i); + stabbingCount++; + } + else + { + //b2Settings.b2Assert(this.m_proxyPool[bound.proxyId].upperBounds[axis] == i); + stabbingCount--; + } + + //b2Settings.b2Assert(bound.stabbingCount == stabbingCount); + } + } + + }, + +//private: + ComputeBounds: function(lowerValues, upperValues, aabb) + { + //b2Settings.b2Assert(aabb.maxVertex.x > aabb.minVertex.x); + //b2Settings.b2Assert(aabb.maxVertex.y > aabb.minVertex.y); + + //var minVertex = b2Math.b2ClampV(aabb.minVertex, this.m_worldAABB.minVertex, this.m_worldAABB.maxVertex); + 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 maxVertex = b2Math.b2ClampV(aabb.maxVertex, this.m_worldAABB.minVertex, this.m_worldAABB.maxVertex); + 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); + + // Bump lower bounds downs and upper bounds up. This ensures correct sorting of + // lower/upper bounds that would have equal values. + // TODO_ERIN implement fast float to uint16 conversion. + lowerValues[0] = /*uint*/(this.m_quantizationFactor.x * (minVertexX - this.m_worldAABB.minVertex.x)) & (b2Settings.USHRT_MAX - 1); + upperValues[0] = (/*uint*/(this.m_quantizationFactor.x * (maxVertexX - this.m_worldAABB.minVertex.x))& 0x0000ffff) | 1; + + lowerValues[1] = /*uint*/(this.m_quantizationFactor.y * (minVertexY - this.m_worldAABB.minVertex.y)) & (b2Settings.USHRT_MAX - 1); + upperValues[1] = (/*uint*/(this.m_quantizationFactor.y * (maxVertexY - this.m_worldAABB.minVertex.y))& 0x0000ffff) | 1; + }, + + // This one is only used for validation. + TestOverlapValidate: function(p1, p2){ + + for (var axis = 0; axis < 2; ++axis) + { + var bounds = this.m_bounds[axis]; + + //b2Settings.b2Assert(p1.lowerBounds[axis] < 2 * this.m_proxyCount); + //b2Settings.b2Assert(p1.upperBounds[axis] < 2 * this.m_proxyCount); + //b2Settings.b2Assert(p2.lowerBounds[axis] < 2 * this.m_proxyCount); + //b2Settings.b2Assert(p2.upperBounds[axis] < 2 * this.m_proxyCount); + + 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]; + + //b2Settings.b2Assert(p.lowerBounds[axis] < 2 * this.m_proxyCount); + //b2Settings.b2Assert(p.upperBounds[axis] < 2 * this.m_proxyCount); + + 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); + + // Easy case: lowerQuery <= lowerIndex(i) < upperQuery + // Solution: search query range for min bounds. + for (var j = lowerQuery; j < upperQuery; ++j) + { + if (bounds[j].IsLower()) + { + this.IncrementOverlapCount(bounds[j].proxyId); + } + } + + // Hard case: lowerIndex(i) < lowerQuery < upperIndex(i) + // Solution: use the stabbing count to search down the bound array. + if (lowerQuery > 0) + { + var i = lowerQuery - 1; + var s = bounds[i].stabbingCount; + + // Find the s overlaps. + while (s) + { + //b2Settings.b2Assert(i >= 0); + + 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; + //b2Settings.b2Assert(this.m_queryResultCount < b2Settings.b2_maxProxies); + 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; + } + }, + +//public: + 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 /*uint*/(mid); + } + } + + return /*uint*/(low); + }; diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2BufferedPair.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2BufferedPair.js new file mode 100644 index 0000000..fe1419e --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2BufferedPair.js @@ -0,0 +1,26 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +var b2BufferedPair = Class.create(); +b2BufferedPair.prototype = { + proxyId1: 0, + proxyId2: 0, + + initialize: function() {}} diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2Collision.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Collision.js new file mode 100644 index 0000000..31719e3 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Collision.js @@ -0,0 +1,738 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + +var b2Collision = Class.create(); +b2Collision.prototype = { + + // Null feature + + + + + // Find the separation between poly1 and poly2 for a give edge normal on poly1. + + + + + // Find the max separation between poly1 and poly2 using edge normals + // from poly1. + + + + + + + + // Find edge normal of max separation on A - return if separating axis is found + // Find edge normal of max separation on B - return if separation axis is found + // Choose reference edge(minA, minB) + // Find incident edge + // Clip + // The normal points from 1 to 2 + + + + + + + + + + + + + + + + initialize: function() {}} +b2Collision.b2_nullFeature = 0x000000ff; +b2Collision.ClipSegmentToLine = function(vOut, vIn, normal, offset) + { + // Start with no output points + var numOut = 0; + + var vIn0 = vIn[0].v; + var vIn1 = vIn[1].v; + + // Calculate the distance of end points to the line + var distance0 = b2Math.b2Dot(normal, vIn[0].v) - offset; + var distance1 = b2Math.b2Dot(normal, vIn[1].v) - offset; + + // If the points are behind the plane + if (distance0 <= 0.0) vOut[numOut++] = vIn[0]; + if (distance1 <= 0.0) vOut[numOut++] = vIn[1]; + + // If the points are on different sides of the plane + if (distance0 * distance1 < 0.0) + { + // Find intersection point of edge and plane + var interp = distance0 / (distance0 - distance1); + // expanded for performance + 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; + + // Convert normal from into poly2's frame. + //b2Settings.b2Assert(edge1 < poly1.m_vertexCount); + + //var normal = b2Math.b2MulMV(poly1.m_R, poly1->m_normals[edge1]); + 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; + // ^^^^^^^ normal.MulM(poly1.m_R); + + //var normalLocal2 = b2Math.b2MulTMV(poly2.m_R, normal); + 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; + // ^^^^^ normalLocal2.MulTM(poly2.m_R); + + // Find support vertex on poly2 for -normal. + var vertexIndex2 = 0; + var minDot = Number.MAX_VALUE; + for (var i = 0; i < count2; ++i) + { + //var dot = b2Math.b2Dot(vert2s[i], normalLocal2); + var tVec = vert2s[i]; + var dot = tVec.x * normalLocal2X + tVec.y * normalLocal2Y; + if (dot < minDot) + { + minDot = dot; + vertexIndex2 = i; + } + } + + //b2Vec2 v1 = poly1->m_position + b2Mul(poly1->m_R, vert1s[edge1]); + 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) + + //b2Vec2 v2 = poly2->m_position + b2Mul(poly2->m_R, vert2s[vertexIndex2]); + 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) + + //var separation = b2Math.b2Dot( b2Math.SubtractVV( v2, v1 ) , normal); + v2X -= v1X; + v2Y -= v1Y; + //var separation = b2Math.b2Dot( v2 , normal); + var separation = v2X * normalX + v2Y * normalY; + return separation; + }; +b2Collision.FindMaxSeparation = function(edgeIndex /*int ptr*/, poly1, poly2, conservative) + { + var count1 = poly1.m_vertexCount; + + // Vector pointing from the origin of poly1 to the origin of poly2. + //var d = b2Math.SubtractVV( poly2.m_position, poly1.m_position ); + var dX = poly2.m_position.x - poly1.m_position.x; + var dY = poly2.m_position.y - poly1.m_position.y; + + //var dLocal1 = b2Math.b2MulTMV(poly1.m_R, d); + 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); + + // Get support vertex hint for our search + var edge = 0; + var maxDot = -Number.MAX_VALUE; + for (var i = 0; i < count1; ++i) + { + //var dot = b2Math.b2Dot(poly.m_normals[i], dLocal1); + var dot = (poly1.m_normals[i].x * dLocal1X + poly1.m_normals[i].y * dLocal1Y); + if (dot > maxDot) + { + maxDot = dot; + edge = i; + } + } + + // Get the separation for the edge normal. + var s = b2Collision.EdgeSeparation(poly1, edge, poly2); + if (s > 0.0 && conservative == false) + { + return s; + } + + // Check the separation for the neighboring edges. + 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; + } + + // Find the best edge and the search direction. + 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 + { + // pointer out + 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; + } + } + + // pointer out + 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; + + // Get the vertices associated with edge1. + var vertex11 = edge1; + var vertex12 = edge1 + 1 == count1 ? 0 : edge1 + 1; + + // Get the normal of edge1. + var tVec = vert1s[vertex12]; + //var normal1Local1 = b2Math.b2CrossVF( b2Math.SubtractVV( vert1s[vertex12], vert1s[vertex11] ), 1.0); + var normal1Local1X = tVec.x; + var normal1Local1Y = tVec.y; + tVec = vert1s[vertex11]; + normal1Local1X -= tVec.x; + normal1Local1Y -= tVec.y; + var tX = normal1Local1X; + normal1Local1X = normal1Local1Y; + normal1Local1Y = -tX; + // ^^^^ normal1Local1.CrossVF(1.0); + + var invLength = 1.0 / Math.sqrt(normal1Local1X*normal1Local1X + normal1Local1Y*normal1Local1Y); + normal1Local1X *= invLength; + normal1Local1Y *= invLength; + // ^^^^normal1Local1.Normalize(); + //var normal1 = b2Math.b2MulMV(poly1.m_R, normal1Local1); + 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; + // ^^^^ normal1.MulM(poly1.m_R); + + //var normal1Local2 = b2Math.b2MulTMV(poly2.m_R, normal1); + 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; + // ^^^^ normal1Local2.MulTM(poly2.m_R); + + // Find the incident edge on poly2. + 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; + + //var normal2Local2 = b2Math.b2CrossVF( b2Math.SubtractVV( vert2s[i2], vert2s[i1] ), 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; + // ^^^^ normal2Local2.CrossVF(1.0); + + invLength = 1.0 / Math.sqrt(normal2Local2X*normal2Local2X + normal2Local2Y*normal2Local2Y); + normal2Local2X *= invLength; + normal2Local2Y *= invLength; + // ^^^^ normal2Local2.Normalize(); + + //var dot = b2Math.b2Dot(normal2Local2, normal1Local2); + var dot = normal2Local2X * normal1Local2X + normal2Local2Y * normal1Local2Y; + if (dot < minDot) + { + minDot = dot; + vertex21 = i1; + vertex22 = i2; + } + } + + var tClip; + // Build the clip vertices for the incident edge. + tClip = c[0]; + //tClip.v = b2Math.AddVV(poly2.m_position, b2Math.b2MulMV(poly2.m_R, vert2s[vertex21])); + 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]; + //tClip.v = b2Math.AddVV(poly2.m_position, b2Math.b2MulMV(poly2.m_R, vert2s[vertex22])); + 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; + + // TODO_ERIN use "radius" of poly for absolute tolerance. + 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 dv = b2Math.SubtractVV(v12, v11); + var dvX = v12.x - v11.x; + var dvY = v12.y - v11.y; + + //var sideNormal = b2Math.b2MulMV(poly1.m_R, b2Math.SubtractVV(v12, v11)); + 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; + // ^^^^ sideNormal.MulM(poly1.m_R); + + var invLength = 1.0 / Math.sqrt(sideNormalX*sideNormalX + sideNormalY*sideNormalY); + sideNormalX *= invLength; + sideNormalY *= invLength; + // ^^^^ sideNormal.Normalize(); + + //var frontNormal = b2Math.b2CrossVF(sideNormal, 1.0); + var frontNormalX = sideNormalX; + var frontNormalY = sideNormalY; + tX = frontNormalX; + frontNormalX = frontNormalY; + frontNormalY = -tX; + // ^^^^ frontNormal.CrossVF(1.0); + + // Expanded for performance + //v11 = b2Math.AddVV(poly1.m_position, b2Math.b2MulMV(poly1.m_R, v11)); + 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; + // ^^^^ v11.MulM(poly1.m_R); + v11X += poly1.m_position.x; + v11Y += poly1.m_position.y; + //v12 = b2Math.AddVV(poly1.m_position, b2Math.b2MulMV(poly1.m_R, v12)); + 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; + // ^^^^ v12.MulM(poly1.m_R); + v12X += poly1.m_position.x; + v12Y += poly1.m_position.y; + + //var frontOffset = b2Math.b2Dot(frontNormal, v11); + var frontOffset = frontNormalX * v11X + frontNormalY * v11Y; + //var sideOffset1 = -b2Math.b2Dot(sideNormal, v11); + var sideOffset1 = -(sideNormalX * v11X + sideNormalY * v11Y); + //var sideOffset2 = b2Math.b2Dot(sideNormal, v12); + var sideOffset2 = sideNormalX * v12X + sideNormalY * v12Y; + + // Clip incident edge against extruded edge1 side edges. + var clipPoints1 = [new ClipVertex(), new ClipVertex()]; + var clipPoints2 = [new ClipVertex(), new ClipVertex()]; + + var np = 0; + + // Clip to box side 1 + b2Collision.b2CollidePolyTempVec.Set(-sideNormalX, -sideNormalY); + np = b2Collision.ClipSegmentToLine(clipPoints1, incidentEdge, b2Collision.b2CollidePolyTempVec, sideOffset1); + + if (np < 2) + return; + + // Clip to negative box side 1 + b2Collision.b2CollidePolyTempVec.Set(sideNormalX, sideNormalY); + np = b2Collision.ClipSegmentToLine(clipPoints2, clipPoints1, b2Collision.b2CollidePolyTempVec, sideOffset2); + + if (np < 2) + return; + + // Now clipPoints2 contains the clipped points. + if (flip){ + manifold.normal.Set(-frontNormalX, -frontNormalY); + } + else{ + manifold.normal.Set(frontNormalX, frontNormalY); + } + // ^^^^ manifold.normal = flip ? frontNormal.Negative() : frontNormal; + + var pointCount = 0; + for (var i = 0; i < b2Settings.b2_maxManifoldPoints; ++i) + { + //var separation = b2Math.b2Dot(frontNormal, clipPoints2[i].v) - frontOffset; + 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 d = b2Math.SubtractVV(circle2.m_position, circle1.m_position); + var dX = circle2.m_position.x - circle1.m_position.x; + var dY = circle2.m_position.y - circle1.m_position.y; + //var distSqr = b2Math.b2Dot(d, d); + 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 = b2Math.SubtractVV(circle2.m_position, b2Math.MulFV(circle2.m_radius, manifold.normal)); + 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; + + // Compute circle position in the frame of the polygon. + //var xLocal = b2Math.b2MulTMV(poly.m_R, b2Math.SubtractVV(circle.m_position, poly.m_position)); + 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; + + // Find the min separating edge. + var normalIndex = 0; + var separation = -Number.MAX_VALUE; + var radius = circle.m_radius; + for (var i = 0; i < poly.m_vertexCount; ++i) + { + //var s = b2Math.b2Dot(poly.m_normals[i], b2Math.SubtractVV(xLocal, poly.m_vertices[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) + { + // Early out. + return; + } + + if (s > separation) + { + separation = s; + normalIndex = i; + } + } + + // If the center is inside the polygon ... + if (separation < Number.MIN_VALUE) + { + manifold.pointCount = 1; + //manifold.normal = b2Math.b2MulMV(poly.m_R, poly.m_normals[normalIndex]); + 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.position = b2Math.SubtractVV(circle.m_position , b2Math.MulFV(radius , manifold.normal)); + tPoint.separation = separation - radius; + return; + } + + // Project the circle center onto the edge segment. + var vertIndex1 = normalIndex; + var vertIndex2 = vertIndex1 + 1 < poly.m_vertexCount ? vertIndex1 + 1 : 0; + //var e = b2Math.SubtractVV(poly.m_vertices[vertIndex2] , poly.m_vertices[vertIndex1]); + 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 = e.Normalize(); + var length = Math.sqrt(eX*eX + eY*eY); + eX /= length; + eY /= length; + + // If the edge length is zero ... + if (length < Number.MIN_VALUE) + { + //d = b2Math.SubtractVV(xLocal , poly.m_vertices[vertIndex1]); + dX = xLocalX - poly.m_vertices[vertIndex1].x; + dY = xLocalY - poly.m_vertices[vertIndex1].y; + //dist = d.Normalize(); + dist = Math.sqrt(dX*dX + dY*dY); + dX /= dist; + dY /= dist; + if (dist > radius) + { + return; + } + + manifold.pointCount = 1; + //manifold.normal = b2Math.b2MulMV(poly.m_R, d); + 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 = b2Math.SubtractVV(circle.m_position , b2Math.MulFV(radius , manifold.normal)); + 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; + } + + // Project the center onto the edge. + //var u = b2Math.b2Dot(b2Math.SubtractVV(xLocal , poly.m_vertices[vertIndex1]) , e); + 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 + { + //p = b2Math.AddVV(poly.m_vertices[vertIndex1] , b2Math.MulFV(u, e)); + pX = eX * u + poly.m_vertices[vertIndex1].x; + pY = eY * u + poly.m_vertices[vertIndex1].y; + tPoint.id.features.incidentEdge = vertIndex1; + } + + //d = b2Math.SubtractVV(xLocal , p); + dX = xLocalX - pX; + dY = xLocalY - pY; + //dist = d.Normalize(); + dist = Math.sqrt(dX*dX + dY*dY); + dX /= dist; + dY /= dist; + if (dist > radius) + { + return; + } + + manifold.pointCount = 1; + //manifold.normal = b2Math.b2MulMV(poly.m_R, d); + manifold.normal.Set(tMat.col1.x * dX + tMat.col2.x * dY, tMat.col1.y * dX + tMat.col2.y * dY); + //tPoint.position = b2Math.SubtractVV(circle.m_position , b2Math.MulFV(radius , manifold.normal)); + 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; + //d1 = b2Math.SubtractVV(b.minVertex, a.maxVertex); + var d1X = t1.x - t2.x; + var d1Y = t1.y - t2.y; + //d2 = b2Math.SubtractVV(a.minVertex, b.maxVertex); + 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; + }; diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactID.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactID.js new file mode 100644 index 0000000..f0962e9 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactID.js @@ -0,0 +1,52 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +// We use contact ids to facilitate warm starting. +var b2ContactID = Class.create(); +b2ContactID.prototype = +{ + initialize: function(){ + // initialize instance variables for references + 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}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactPoint.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactPoint.js new file mode 100644 index 0000000..2ba6359 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2ContactPoint.js @@ -0,0 +1,35 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +// We use contact ids to facilitate warm starting. +var b2ContactPoint = Class.create(); +b2ContactPoint.prototype = +{ + position: new b2Vec2(), + separation: null, + normalImpulse: null, + tangentImpulse: null, + id: new b2ContactID(), + initialize: function() { + // initialize instance variables for references + this.position = new b2Vec2(); + this.id = new b2ContactID(); + // +}}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2Distance.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Distance.js new file mode 100644 index 0000000..66bd84a --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Distance.js @@ -0,0 +1,333 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +var b2Distance = Class.create(); +b2Distance.prototype = +{ + + // GJK using Voronoi regions (Christer Ericson) and region selection + // optimizations (Casey Muratori). + + // The origin is either in the region of points[1] or in the edge region. The origin is + // not in region of points[0] because that is the old point. + + // Possible regions: + // - points[2] + // - edge points[0]-points[2] + // - edge points[1]-points[2] + // - inside the triangle + + + + + + + initialize: function() {}}; +b2Distance.ProcessTwo = function(p1Out, p2Out, p1s, p2s, points) + { + // If in point[1] region + //b2Vec2 r = -points[1]; + var rX = -points[1].x; + var rY = -points[1].y; + //b2Vec2 d = points[1] - points[0]; + var dX = points[0].x - points[1].x; + var dY = points[0].y - points[1].y; + //float32 length = d.Normalize(); + var length = Math.sqrt(dX*dX + dY*dY); + dX /= length; + dY /= length; + + //float32 lambda = b2Dot(r, d); + var lambda = rX * dX + rY * dY; + if (lambda <= 0.0 || length < Number.MIN_VALUE) + { + // The simplex is reduced to a point. + //*p1Out = p1s[1]; + p1Out.SetV(p1s[1]); + //*p2Out = p2s[1]; + p2Out.SetV(p2s[1]); + //p1s[0] = p1s[1]; + p1s[0].SetV(p1s[1]); + //p2s[0] = p2s[1]; + p2s[0].SetV(p2s[1]); + points[0].SetV(points[1]); + return 1; + } + + // Else in edge region + lambda /= length; + //*p1Out = p1s[1] + lambda * (p1s[0] - p1s[1]); + 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 = p2s[1] + lambda * (p2s[0] - p2s[1]); + 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) + { + //b2Vec2 a = points[0]; + var aX = points[0].x; + var aY = points[0].y; + //b2Vec2 b = points[1]; + var bX = points[1].x; + var bY = points[1].y; + //b2Vec2 c = points[2]; + var cX = points[2].x; + var cY = points[2].y; + + //b2Vec2 ab = b - a; + var abX = bX - aX; + var abY = bY - aY; + //b2Vec2 ac = c - a; + var acX = cX - aX; + var acY = cY - aY; + //b2Vec2 bc = c - b; + var bcX = cX - bX; + var bcY = cY - bY; + + //float32 sn = -b2Dot(a, ab), sd = b2Dot(b, ab); + var sn = -(aX * abX + aY * abY); + var sd = (bX * abX + bY * abY); + //float32 tn = -b2Dot(a, ac), td = b2Dot(c, ac); + var tn = -(aX * acX + aY * acY); + var td = (cX * acX + cY * acY); + //float32 un = -b2Dot(b, bc), ud = b2Dot(c, bc); + var un = -(bX * bcX + bY * bcY); + var ud = (cX * bcX + cY * bcY); + + // In vertex c region? + if (td <= 0.0 && ud <= 0.0) + { + // Single point + //*p1Out = p1s[2]; + p1Out.SetV(p1s[2]); + //*p2Out = p2s[2]; + p2Out.SetV(p2s[2]); + //p1s[0] = p1s[2]; + p1s[0].SetV(p1s[2]); + //p2s[0] = p2s[2]; + p2s[0].SetV(p2s[2]); + points[0].SetV(points[2]); + return 1; + } + + // Should not be in vertex a or b region. + //b2Settings.b2Assert(sn > 0.0 || tn > 0.0); + //b2Settings.b2Assert(sd > 0.0 || un > 0.0); + + //float32 n = b2Cross(ab, ac); + var n = abX * acY - abY * acX; + + // Should not be in edge ab region. + //float32 vc = n * b2Cross(a, b); + var vc = n * (aX * bY - aY * bX); + //b2Settings.b2Assert(vc > 0.0 || sn > 0.0 || sd > 0.0); + + // In edge bc region? + //float32 va = n * b2Cross(b, c); + var va = n * (bX * cY - bY * cX); + if (va <= 0.0 && un >= 0.0 && ud >= 0.0) + { + //b2Settings.b2Assert(un + ud > 0.0); + + //float32 lambda = un / (un + ud); + var lambda = un / (un + ud); + //*p1Out = p1s[1] + lambda * (p1s[2] - p1s[1]); + 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 = p2s[1] + lambda * (p2s[2] - p2s[1]); + 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] = p1s[2]; + p1s[0].SetV(p1s[2]); + //p2s[0] = p2s[2]; + p2s[0].SetV(p2s[2]); + //points[0] = points[2]; + points[0].SetV(points[2]); + return 2; + } + + // In edge ac region? + //float32 vb = n * b2Cross(c, a); + var vb = n * (cX * aY - cY * aX); + if (vb <= 0.0 && tn >= 0.0 && td >= 0.0) + { + //b2Settings.b2Assert(tn + td > 0.0); + + //float32 lambda = tn / (tn + td); + var lambda = tn / (tn + td); + //*p1Out = p1s[0] + lambda * (p1s[2] - p1s[0]); + 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 = p2s[0] + lambda * (p2s[2] - p2s[0]); + 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] = p1s[2]; + p1s[1].SetV(p1s[2]); + //p2s[1] = p2s[2]; + p2s[1].SetV(p2s[2]); + //points[1] = points[2]; + points[1].SetV(points[2]); + return 2; + } + + // Inside the triangle, compute barycentric coordinates + //float32 denom = va + vb + vc; + var denom = va + vb + vc; + //b2Settings.b2Assert(denom > 0.0); + denom = 1.0 / denom; + //float32 u = va * denom; + var u = va * denom; + //float32 v = vb * denom; + var v = vb * denom; + //float32 w = 1.0f - u - v; + var w = 1.0 - u - v; + //*p1Out = u * p1s[0] + v * p1s[1] + w * p1s[2]; + 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 = u * p2s[0] + v * p2s[1] + w * p2s[2]; + 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) + { + //b2Vec2 p1s[3], p2s[3]; + var p1s = new Array(3); + var p2s = new Array(3); + //b2Vec2 points[3]; + var points = new Array(3); + //int32 pointCount = 0; + var pointCount = 0; + + //*p1Out = shape1->m_position; + p1Out.SetV(shape1.m_position); + //*p2Out = shape2->m_position; + p2Out.SetV(shape2.m_position); + + var vSqr = 0.0; + var maxIterations = 20; + for (var iter = 0; iter < maxIterations; ++iter) + { + //b2Vec2 v = *p2Out - *p1Out; + var vX = p2Out.x - p1Out.x; + var vY = p2Out.y - p1Out.y; + //b2Vec2 w1 = shape1->Support(v); + var w1 = shape1.Support(vX, vY); + //b2Vec2 w2 = shape2->Support(-v); + var w2 = shape2.Support(-vX, -vY); + //float32 vSqr = b2Dot(v, v); + vSqr = (vX*vX + vY*vY); + //b2Vec2 w = w2 - w1; + var wX = w2.x - w1.x; + var wY = w2.y - w1.y; + //float32 vw = b2Dot(v, w); + var vw = (vX*wX + vY*wY); + //if (vSqr - b2Dot(v, w) <= 0.01f * vSqr) + if (vSqr - b2Dot(vX * wX + vY * wY) <= 0.01 * vSqr) + { + if (pointCount == 0) + { + //*p1Out = w1; + p1Out.SetV(w1); + //*p2Out = w2; + p2Out.SetV(w2); + } + b2Distance.g_GJK_Iterations = iter; + return Math.sqrt(vSqr); + } + + switch (pointCount) + { + case 0: + //p1s[0] = w1; + p1s[0].SetV(w1); + //p2s[0] = w2; + p2s[0].SetV(w2); + points[0] = w; + //*p1Out = p1s[0]; + p1Out.SetV(p1s[0]); + //*p2Out = p2s[0]; + p2Out.SetV(p2s[0]); + ++pointCount; + break; + + case 1: + //p1s[1] = w1; + p1s[1].SetV(w1); + //p2s[1] = w2; + p2s[1].SetV(w2); + //points[1] = w; + points[1].x = wX; + points[1].y = wY; + pointCount = b2Distance.ProcessTwo(p1Out, p2Out, p1s, p2s, points); + break; + + case 2: + //p1s[2] = w1; + p1s[2].SetV(w1); + //p2s[2] = w2; + p2s[2].SetV(w2); + //points[2] = w; + points[2].x = wX; + points[2].y = wY; + pointCount = b2Distance.ProcessThree(p1Out, p2Out, p1s, p2s, points); + break; + } + + // If we have three points, then the origin is in the corresponding triangle. + if (pointCount == 3) + { + b2Distance.g_GJK_Iterations = iter; + return 0.0; + } + + //float32 maxSqr = -FLT_MAX; + var maxSqr = -Number.MAX_VALUE; + for (var i = 0; i < pointCount; ++i) + { + //maxSqr = b2Math.b2Max(maxSqr, b2Dot(points[i], points[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; diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2Manifold.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Manifold.js new file mode 100644 index 0000000..14b0979 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Manifold.js @@ -0,0 +1,34 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +// A manifold for two touching convex shapes. +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}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2OBB.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2OBB.js new file mode 100644 index 0000000..0214be8e --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2OBB.js @@ -0,0 +1,34 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +// A manifold for two touching convex shapes. +var b2OBB = Class.create(); +b2OBB.prototype = +{ + R: new b2Mat22(), + center: new b2Vec2(), + extents: new b2Vec2(), + initialize: function() { + // initialize instance variables for references + this.R = new b2Mat22(); + this.center = new b2Vec2(); + this.extents = new b2Vec2(); + // +}}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2Pair.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Pair.js new file mode 100644 index 0000000..56e615b --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Pair.js @@ -0,0 +1,60 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +// The pair manager is used by the broad-phase to quickly add/remove/find pairs +// of overlapping proxies. It is based closely on code provided by Pierre Terdiman. +// http: + + + + + +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, + + // STATIC + + // enum + + 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; diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2PairCallback.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2PairCallback.js new file mode 100644 index 0000000..f68628c --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2PairCallback.js @@ -0,0 +1,34 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +var b2PairCallback = Class.create(); +b2PairCallback.prototype = +{ + //virtual ~b2PairCallback() {} + + // This returns the new pair user data. + PairAdded: function(proxyUserData1, proxyUserData2){return null}, + + // This should free the pair's user data. In extreme circumstances, it is possible + // this will be called with null pairUserData because the pair never existed. + PairRemoved: function(proxyUserData1, proxyUserData2, pairUserData){}, + initialize: function() {}}; + + diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2PairManager.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2PairManager.js new file mode 100644 index 0000000..88d4c90 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2PairManager.js @@ -0,0 +1,386 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +// The pair manager is used by the broad-phase to quickly add/remove/find pairs +// of overlapping proxies. It is based closely on code provided by Pierre Terdiman. +// http: + + + + + +var b2PairManager = Class.create(); +b2PairManager.prototype = +{ +//public: + initialize: function(){ + var i = 0; + //b2Settings.b2Assert(b2Math.b2IsPowerOfTwo(b2Pair.b2_tableCapacity) == true); + //b2Settings.b2Assert(b2Pair.b2_tableCapacity >= b2Settings.b2_maxPairs); + 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; + }, + //~b2PairManager(); + + Initialize: function(broadPhase, callback){ + this.m_broadPhase = broadPhase; + this.m_callback = callback; + }, + + /* + As proxies are created and moved, many pairs are created and destroyed. Even worse, the same + pair may be added and removed multiple times in a single time step of the physics engine. To reduce + traffic in the pair manager, we try to avoid destroying pairs in the pair manager until the + end of the physics step. This is done by buffering all the this.RemovePair requests. this.AddPair + requests are processed immediately because we need the hash table entry for quick lookup. + + All user user callbacks are delayed until the buffered pairs are confirmed in this.Commit. + This is very important because the user callbacks may be very expensive and client logic + may be harmed if pairs are added and removed within the same time step. + + Buffer a pair for addition. + We may add a pair that is not in the pair manager or pair buffer. + We may add a pair that is already in the pair manager and pair buffer. + If the added pair is not a new pair, then it must be in the pair buffer (because this.RemovePair was called). + */ + AddBufferedPair: function(proxyId1, proxyId2){ + //b2Settings.b2Assert(id1 != b2_nullProxy && id2 != b2_nullProxy); + //b2Settings.b2Assert(this.m_pairBufferCount < b2_maxPairs); + + var pair = this.AddPair(proxyId1, proxyId2); + + // If this pair is not in the pair buffer ... + if (pair.IsBuffered() == false) + { + // This must be a newly added pair. + //b2Settings.b2Assert(pair.IsFinal() == false); + + // Add it to the pair buffer. + pair.SetBuffered(); + this.m_pairBuffer[this.m_pairBufferCount].proxyId1 = pair.proxyId1; + this.m_pairBuffer[this.m_pairBufferCount].proxyId2 = pair.proxyId2; + ++this.m_pairBufferCount; + + //b2Settings.b2Assert(this.m_pairBufferCount <= this.m_pairCount); + } + + // Confirm this pair for the subsequent call to this.Commit. + pair.ClearRemoved(); + + if (b2BroadPhase.s_validate) + { + this.ValidateBuffer(); + } + }, + + // Buffer a pair for removal. + RemoveBufferedPair: function(proxyId1, proxyId2){ + //b2Settings.b2Assert(id1 != b2_nullProxy && id2 != b2_nullProxy); + //b2Settings.b2Assert(this.m_pairBufferCount < b2_maxPairs); + + var pair = this.Find(proxyId1, proxyId2); + + if (pair == null) + { + // The pair never existed. This is legal (due to collision filtering). + return; + } + + // If this pair is not in the pair buffer ... + if (pair.IsBuffered() == false) + { + // This must be an old pair. + //b2Settings.b2Assert(pair.IsFinal() == true); + + pair.SetBuffered(); + this.m_pairBuffer[this.m_pairBufferCount].proxyId1 = pair.proxyId1; + this.m_pairBuffer[this.m_pairBufferCount].proxyId2 = pair.proxyId2; + ++this.m_pairBufferCount; + + //b2Settings.b2Assert(this.m_pairBufferCount <= this.m_pairCount); + } + + 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); + //b2Settings.b2Assert(pair.IsBuffered()); + pair.ClearBuffered(); + + //b2Settings.b2Assert(pair.proxyId1 < b2Settings.b2_maxProxies && pair.proxyId2 < b2Settings.b2_maxProxies); + + var proxy1 = proxies[ pair.proxyId1 ]; + var proxy2 = proxies[ pair.proxyId2 ]; + + //b2Settings.b2Assert(proxy1.IsValid()); + //b2Settings.b2Assert(proxy2.IsValid()); + + if (pair.IsRemoved()) + { + // It is possible a pair was added then removed before a commit. Therefore, + // we should be careful not to tell the user the pair was removed when the + // the user didn't receive a matching add. + if (pair.IsFinal() == true) + { + this.m_callback.PairRemoved(proxy1.userData, proxy2.userData, pair.userData); + } + + // Store the ids so we can actually remove the pair below. + this.m_pairBuffer[removeCount].proxyId1 = pair.proxyId1; + this.m_pairBuffer[removeCount].proxyId2 = pair.proxyId2; + ++removeCount; + } + else + { + //b2Settings.b2Assert(this.m_broadPhase.TestOverlap(proxy1, proxy2) == true); + + 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(); + } + }, + +//private: + + // Add a pair and return the new pair. If the pair already exists, + // no new pair is created and the old one is returned. + AddPair: function(proxyId1, proxyId2){ + + if (proxyId1 > proxyId2){ + var temp = proxyId1; + proxyId1 = proxyId2; + proxyId2 = temp; + //b2Math.b2Swap(p1, p2); + } + + var hash = b2PairManager.Hash(proxyId1, proxyId2) & b2Pair.b2_tableMask; + + //var pairIndex = this.FindHash(proxyId1, proxyId2, hash); + var pair = pair = this.FindHash(proxyId1, proxyId2, hash); + + if (pair != null) + { + return pair; + } + + //b2Settings.b2Assert(this.m_pairCount < b2Settings.b2_maxPairs && this.m_freePair != b2_nullPair); + + 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; + }, + + // Remove a pair, return the pair's userData. + RemovePair: function(proxyId1, proxyId2){ + + //b2Settings.b2Assert(this.m_pairCount > 0); + + if (proxyId1 > proxyId2){ + var temp = proxyId1; + proxyId1 = proxyId2; + proxyId2 = temp; + //b2Math.b2Swap(proxyId1, proxyId2); + } + + 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; + + //*node = this.m_pairs[*node].next; + 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; + + // Scrub + 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 + { + //node = &this.m_pairs[*node].next; + pNode = this.m_pairs[node]; + node = pNode.next; + } + } + + //b2Settings.b2Assert(false); + return null; + }, + + Find: function(proxyId1, proxyId2){ + + if (proxyId1 > proxyId2){ + var temp = proxyId1; + proxyId1 = proxyId2; + proxyId2 = temp; + //b2Math.b2Swap(proxyId1, proxyId2); + } + + 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; + } + + //b2Settings.b2Assert(index < b2_maxPairs); + + return this.m_pairs[ index ]; + }, + + ValidateBuffer: function(){ + // DEBUG + }, + + ValidateTable: function(){ + // DEBUG + }, + +//public: + m_broadPhase: null, + m_callback: null, + m_pairs: null, + m_freePair: 0, + m_pairCount: 0, + + m_pairBuffer: null, + m_pairBufferCount: 0, + + m_hashTable: null + + +// static + // Thomas Wang's hash, see: http: + + + +}; +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; + }; diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/b2Proxy.js b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Proxy.js new file mode 100644 index 0000000..60726d0 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/b2Proxy.js @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +var b2Proxy = Class.create(); +b2Proxy.prototype = { + GetNext: function(){ return this.lowerBounds[0]; }, + SetNext: function(next) { this.lowerBounds[0] = next /*& 0x0000ffff*/; }, + + IsValid: function(){ return this.overlapCount != b2BroadPhase.b2_invalid; }, + + lowerBounds: [/*uint*/(0), /*uint*/(0)], + upperBounds: [/*uint*/(0), /*uint*/(0)], + overlapCount: 0, + timeStamp: 0, + + userData: null, + + initialize: function() { + // initialize instance variables for references + this.lowerBounds = [/*uint*/(0), /*uint*/(0)]; + this.upperBounds = [/*uint*/(0), /*uint*/(0)]; + // +}} diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2BoxDef.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2BoxDef.js new file mode 100644 index 0000000..9cc24f7 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2BoxDef.js @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + + +var b2BoxDef = Class.create(); +Object.extend(b2BoxDef.prototype, b2ShapeDef.prototype); +Object.extend(b2BoxDef.prototype, +{ + initialize: function() + { + // The constructor for b2ShapeDef + 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}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleDef.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleDef.js new file mode 100644 index 0000000..dd1c582 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleDef.js @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + + +var b2CircleDef = Class.create(); +Object.extend(b2CircleDef.prototype, b2ShapeDef.prototype); +Object.extend(b2CircleDef.prototype, +{ + initialize: function() + { + // The constructor for b2ShapeDef + 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}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleShape.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleShape.js new file mode 100644 index 0000000..4456168 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2CircleShape.js @@ -0,0 +1,198 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + + +var b2CircleShape = Class.create(); +Object.extend(b2CircleShape.prototype, b2Shape.prototype); +Object.extend(b2CircleShape.prototype, +{ + TestPoint: function(p){ + //var d = b2Math.SubtractVV(p, this.m_position); + var d = new b2Vec2(); + d.SetV(p); + d.Subtract(this.m_position); + return b2Math.b2Dot(d, d) <= this.m_radius * this.m_radius; + }, + + //--------------- Internals Below ------------------- + + initialize: function(def, body, localCenter){ + // initialize instance variables for references + this.m_R = new b2Mat22(); + this.m_position = new b2Vec2(); + // + + // The constructor for b2Shape + 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; + // + + // initialize instance variables for references + this.m_localPosition = new b2Vec2(); + // + + //super(def, body); + + //b2Settings.b2Assert(def.type == b2Shape.e_circleShape); + var circle = def; + + //this.m_localPosition = def.localPosition - localCenter; + 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); + //b2Vec2 r = b2Mul(this.m_body->this.m_R, this.m_localPosition); + 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 = this.m_body->this.m_position + r; + 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 = r.Length() + this.m_radius; + 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 = position2 + b2Mul(R2, this.m_localPosition); + 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; + } + + // Compute an AABB that covers the swept shape (may miss some rotation effect). + //b2Vec2 p1 = position1 + b2Mul(R1, this.m_localPosition); + 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); + //b2Vec2 lower = b2Min(p1, this.m_position); + var lowerX = Math.min(p1X, this.m_position.x); + var lowerY = Math.min(p1Y, this.m_position.y); + //b2Vec2 upper = b2Max(p1, this.m_position); + 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 = position + b2Mul(R, this.m_localPosition); + 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) + { + //b2Vec2 u = d; + //u.Normalize(); + var len = Math.sqrt(dX*dX + dY*dY); + dX /= len; + dY /= len; + //return this.m_position + this.m_radius * u; + out.Set( this.m_position.x + this.m_radius*dX, + this.m_position.y + this.m_radius*dY); + }, + + + // Local position in parent body + m_localPosition: new b2Vec2(), + m_radius: null}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2MassData.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2MassData.js new file mode 100644 index 0000000..2cd1af5 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2MassData.js @@ -0,0 +1,36 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + + +var b2MassData = Class.create(); +b2MassData.prototype = +{ + mass: 0.0, + center: new b2Vec2(0,0), + I: 0.0, + + initialize: function() { + // initialize instance variables for references + this.center = new b2Vec2(0,0); + // +}} diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyDef.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyDef.js new file mode 100644 index 0000000..6f13c28 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyDef.js @@ -0,0 +1,58 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + + +var b2PolyDef = Class.create(); +Object.extend(b2PolyDef.prototype, b2ShapeDef.prototype); +Object.extend(b2PolyDef.prototype, +{ + initialize: function() + { + // The constructor for b2ShapeDef + 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; + // + + // initialize instance variables for references + 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}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyShape.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyShape.js new file mode 100644 index 0000000..39ca4a6 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2PolyShape.js @@ -0,0 +1,492 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +// A convex polygon. The position of the polygon (m_position) is the +// position of the centroid. The vertices of the incoming polygon are pre-rotated +// according to the local rotation. The vertices are also shifted to be centered +// on the centroid. Since the local rotation is absorbed into the vertex +// coordinates, the polygon rotation is equal to the body rotation. However, +// the polygon position is centered on the polygon centroid. This simplifies +// some collision algorithms. + +var b2PolyShape = Class.create(); +Object.extend(b2PolyShape.prototype, b2Shape.prototype); +Object.extend(b2PolyShape.prototype, +{ + TestPoint: function(p){ + + //var pLocal = b2Math.b2MulTMV(this.m_R, b2Math.SubtractVV(p, this.m_position)); + 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 dot = b2Math.b2Dot(this.m_normals[i], b2Math.SubtractVV(pLocal, this.m_vertices[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; + }, + + //--------------- Internals Below ------------------- + // Temp vec for b2Shape.PolyCentroid + + initialize: function(def, body, newOrigin){ + // initialize instance variables for references + this.m_R = new b2Mat22(); + this.m_position = new b2Vec2(); + // + + // The constructor for b2Shape + 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; + // + + // initialize instance variables for references + this.syncAABB = new b2AABB(); + this.syncMat = new b2Mat22(); + this.m_localCentroid = new b2Vec2(); + this.m_localOBB = new b2OBB(); + // + + + //super(def, body); + + var i = 0; + + + var hX; + var hY; + + var tVec; + + var aabb = new b2AABB(); + + // Vertices + this.m_vertices = new Array(b2Settings.b2_maxPolyVertices); + this.m_coreVertices = new Array(b2Settings.b2_maxPolyVertices); + //for (i = 0; i < b2Settings.b2_maxPolyVertices; i++) + // this.m_vertices[i] = new b2Vec2(); + + // Normals + this.m_normals = new Array(b2Settings.b2_maxPolyVertices); + //for (i = 0; i < b2Settings.b2_maxPolyVertices; i++) + // this.m_normals[i] = new b2Vec2(); + + //b2Settings.b2Assert(def.type == b2Shape.e_boxShape || def.type == b2Shape.e_polyShape); + this.m_type = b2Shape.e_polyShape; + + var localR = new b2Mat22(def.localRotation); + + // Get the vertices transformed into the body frame. + if (def.type == b2Shape.e_boxShape) + { + //this.m_localCentroid = def.localPosition - newOrigin; + 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; + + //hc.x = b2Max(0.0f, h.x - 2.0f * b2_linearSlop); + var hcX = Math.max(0.0, hX - 2.0 * b2Settings.b2_linearSlop); + //hc.y = b2Max(0.0f, h.y - 2.0f * b2_linearSlop); + var hcY = Math.max(0.0, hY - 2.0 * b2Settings.b2_linearSlop); + + //this.m_vertices[0] = b2Mul(localR, b2Vec2(h.x, h.y)); + 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; + //this.m_vertices[1] = b2Mul(localR, b2Vec2(-h.x, h.y)); + 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; + //this.m_vertices[2] = b2Mul(localR, b2Vec2(-h.x, -h.y)); + 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; + //this.m_vertices[3] = b2Mul(localR, b2Vec2(h.x, -h.y)); + 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; + + //this.m_coreVertices[0] = b2Mul(localR, b2Vec2(hc.x, hc.y)); + 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; + //this.m_coreVertices[1] = b2Mul(localR, b2Vec2(-hc.x, hc.y)); + 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; + //this.m_coreVertices[2] = b2Mul(localR, b2Vec2(-hc.x, -hc.y)); + 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; + //this.m_coreVertices[3] = b2Mul(localR, b2Vec2(hc.x, -hc.y)); + 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; + //b2Settings.b2Assert(3 <= this.m_vertexCount && this.m_vertexCount <= b2Settings.b2_maxPolyVertices); + //b2Vec2 centroid = b2Shape.PolyCentroid(poly->vertices, poly->vertexCount); + b2Shape.PolyCentroid(poly.vertices, poly.vertexCount, b2PolyShape.tempVec); + var centroidX = b2PolyShape.tempVec.x; + var centroidY = b2PolyShape.tempVec.y; + //this.m_localCentroid = def->localPosition + b2Mul(localR, centroid) - newOrigin; + 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(); + + //this.m_vertices[i] = b2Mul(localR, poly->vertices[i] - centroid); + 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; + + //b2Vec2 u = this.m_vertices[i]; + var uX = this.m_vertices[i].x; + var uY = this.m_vertices[i].y; + //float32 length = u.Length(); + 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] = this.m_vertices[i] - 2.0f * b2_linearSlop * u; + 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; + } + + } + + // Compute bounding box. TODO_ERIN optimize OBB + //var minVertex = new b2Vec2(Number.MAX_VALUE, Number.MAX_VALUE); + 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]; + //minVertex = b2Math.b2MinV(minVertex, this.m_vertices[i]); + minVertexX = Math.min(minVertexX, v.x); + minVertexY = Math.min(minVertexY, v.y); + //maxVertex = b2Math.b2MaxV(maxVertex, this.m_vertices[i]); + maxVertexX = Math.max(maxVertexX, v.x); + maxVertexY = Math.max(maxVertexY, v.y); + //this.m_maxRadius = b2Max(this.m_maxRadius, v.Length()); + this.m_maxRadius = Math.max(this.m_maxRadius, v.Length()); + } + + this.m_localOBB.R.SetIdentity(); + //this.m_localOBB.center = 0.5 * (minVertex + maxVertex); + this.m_localOBB.center.Set((minVertexX + maxVertexX) * 0.5, (minVertexY + maxVertexY) * 0.5); + //this.m_localOBB.extents = 0.5 * (maxVertex - minVertex); + this.m_localOBB.extents.Set((maxVertexX - minVertexX) * 0.5, (maxVertexY - minVertexY) * 0.5); + + // Compute the edge normals and next index map. + 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; + //b2Vec2 edge = this.m_vertices[i2] - this.m_vertices[i1]; + //var edgeX = this.m_vertices[i2].x - this.m_vertices[i1].x; + //var edgeY = this.m_vertices[i2].y - this.m_vertices[i1].y; + //this.m_normals[i] = b2Cross(edge, 1.0f); + 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(); + } + + // Ensure the polygon in convex. TODO_ERIN compute convex hull. + for (i = 0; i < this.m_vertexCount; ++i) + { + i1 = i; + i2 = i + 1 < this.m_vertexCount ? i + 1 : 0; + + //b2Settings.b2Assert(b2Math.b2CrossVV(this.m_normals[i1], this.m_normals[i2]) > Number.MIN_VALUE); + } + + this.m_R.SetM(this.m_body.m_R); + //this.m_position.SetV( this.m_body.m_position + b2Mul(this.m_body->this.m_R, this.m_localCentroid) ); + 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); + + //var R = b2Math.b2MulMM(this.m_R, this.m_localOBB.R); + //R.col1 = b2MulMV(this.m_R, this.m_localOBB.R.col1); + 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; + //R.col2 = b2MulMV(this.m_R, this.m_localOBB.R.col2) + 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; + //var absR = b2Math.b2AbsM(R); + b2PolyShape.tAbsR.Abs() + + //h = b2Math.b2MulMV(b2PolyShape.tAbsR, this.m_localOBB.extents); + 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 position = this.m_position + b2Mul(this.m_R, this.m_localOBB.center); + 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 = b2Math.SubtractVV(this.m_position, h); + aabb.minVertex.x = positionX - hX; + aabb.minVertex.y = positionY - hY; + //aabb.maxVertex = b2Math.AddVV(this.m_position, h); + 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(); + } + }, + + // Temp AABB for Synch function + syncAABB: new b2AABB(), + syncMat: new b2Mat22(), + Synchronize: function(position1, R1, position2, R2){ + // The body transform is copied for convenience. + this.m_R.SetM(R2); + //this.m_position = this.m_body->this.m_position + b2Mul(this.m_body->this.m_R, this.m_localCentroid) + 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; + } + + //b2AABB aabb1, aabb2; + var hX; + var hY; + + //b2Mat22 obbR = b2Mul(R1, this.m_localOBB.R); + 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 = b2MulMV(R1, this.m_localOBB.R.col1); + 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 = b2MulMV(R1, this.m_localOBB.R.col2); + this.syncMat.col2.x = v1.x * v4.x + v2.x * v4.y; + this.syncMat.col2.y = v1.y * v4.x + v2.y * v4.y; + //b2Mat22 absR = b2Abs(obbR); + this.syncMat.Abs(); + //b2Vec2 center = position1 + b2Mul(R1, this.m_localCentroid + this.m_localOBB.center); + 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); + //b2Vec2 h = b2Mul(this.syncMat, this.m_localOBB.extents); + 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; + //aabb1.minVertex = center - h; + this.syncAABB.minVertex.x = centerX - hX; + this.syncAABB.minVertex.y = centerY - hY; + //aabb1.maxVertex = center + h; + this.syncAABB.maxVertex.x = centerX + hX; + this.syncAABB.maxVertex.y = centerY + hY; + + //b2Mat22 obbR = b2Mul(R2, this.m_localOBB.R); + v1 = R2.col1; + v2 = R2.col2; + v3 = this.m_localOBB.R.col1; + v4 = this.m_localOBB.R.col2; + //this.syncMat.col1 = b2MulMV(R1, this.m_localOBB.R.col1); + 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 = b2MulMV(R1, this.m_localOBB.R.col2); + this.syncMat.col2.x = v1.x * v4.x + v2.x * v4.y; + this.syncMat.col2.y = v1.y * v4.x + v2.y * v4.y; + //b2Mat22 absR = b2Abs(obbR); + this.syncMat.Abs(); + //b2Vec2 center = position2 + b2Mul(R2, this.m_localCentroid + this.m_localOBB.center); + 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); + //b2Vec2 h = b2Mul(absR, this.m_localOBB.extents); + 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; + //aabb2.minVertex = center - h; + //aabb2.maxVertex = center + h; + + //aabb.minVertex = b2Min(aabb1.minVertex, aabb2.minVertex); + this.syncAABB.minVertex.x = Math.min(this.syncAABB.minVertex.x, centerX - hX); + this.syncAABB.minVertex.y = Math.min(this.syncAABB.minVertex.y, centerY - hY); + //aabb.maxVertex = b2Max(aabb1.maxVertex, aabb2.maxVertex); + 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 = R; + this.m_R.SetM(R); + //this.m_position = position + b2Mul(R, this.m_localCentroid); + 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 = this.m_position + b2Mul(this.m_R, this.m_localOBB.center); + var position = b2Math.b2MulMV(this.m_R, this.m_localOBB.center); + position.Add(this.m_position); + + var aabb = new b2AABB(); + //aabb.minVertex = position - h; + aabb.minVertex.SetV(position); + aabb.minVertex.Subtract(h); + //aabb.maxVertex = position + 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) + { + //b2Vec2 dLocal = b2MulT(this.m_R, d); + 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; + //float32 bestValue = b2Dot(this.m_vertices[0], dLocal); + var bestValue = (this.m_coreVertices[0].x * dLocalX + this.m_coreVertices[0].y * dLocalY); + for (var i = 1; i < this.m_vertexCount; ++i) + { + //float32 value = b2Dot(this.m_vertices[i], dLocal); + var value = (this.m_coreVertices[i].x * dLocalX + this.m_coreVertices[i].y * dLocalY); + if (value > bestValue) + { + bestIndex = i; + bestValue = value; + } + } + + //return this.m_position + b2Mul(this.m_R, this.m_vertices[bestIndex]); + 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)); + + }, + + + // Local position of the shape centroid in parent body frame. + m_localCentroid: new b2Vec2(), + + // Local position oriented bounding box. The OBB center is relative to + // shape centroid. + m_localOBB: new b2OBB(), + m_vertices: null, + m_coreVertices: null, + m_vertexCount: 0, + m_normals: null}); + +b2PolyShape.tempVec = new b2Vec2(); +b2PolyShape.tAbsR = new b2Mat22(); diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2Shape.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2Shape.js new file mode 100644 index 0000000..17ebf8f --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2Shape.js @@ -0,0 +1,339 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + + + +// Shapes are created automatically when a body is created. +// Client code does not normally interact with shapes. +var b2Shape = Class.create(); +b2Shape.prototype = +{ + TestPoint: function(p){return false}, + + GetUserData: function(){return this.m_userData;}, + + GetType: function(){ + return this.m_type; + }, + + // Get the parent body of this shape. + GetBody: function(){ + return this.m_body; + }, + + GetPosition: function(){ + return this.m_position; + }, + GetRotationMatrix: function(){ + return this.m_R; + }, + + // Remove and then add proxy from the broad-phase. + // This is used to refresh the collision filters. + ResetProxy: function(broadPhase){}, + + // Get the next shape in the parent body's shape list. + GetNext: function(){ + return this.m_next; + }, + + //--------------- Internals Below ------------------- + + + + + initialize: function(def, body){ + // initialize instance variables for references + 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; + }, + + // Internal use only. Do not call. + //b2Shape::~b2Shape() + //{ + // this.m_body->m_world->m_broadPhase->this.DestroyProxy(this.m_proxyId); + //} + + + 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; + } + }, + + + // Internal use only. Do not call. + 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 + + + + // b2ShapeType + + + + + + + + + + + + +}; + + +b2Shape.Create = function(def, body, center){ + switch (def.type) + { + case b2Shape.e_circleShape: + { + //void* mem = body->m_world->m_blockAllocator.Allocate(sizeof(b2CircleShape)); + return new b2CircleShape(def, body, center); + } + + case b2Shape.e_boxShape: + case b2Shape.e_polyShape: + { + //void* mem = body->m_world->m_blockAllocator.Allocate(sizeof(b2PolyShape)); + return new b2PolyShape(def, body, center); + } + } + + //b2Settings.b2Assert(false); + return null; + }; +b2Shape.Destroy = function(shape) + { + /*b2BlockAllocator& allocator = shape->m_body->m_world->m_blockAllocator; + + switch (shape.m_type) + { + case b2Shape.e_circleShape: + shape->~b2Shape(); + allocator.Free(shape, sizeof(b2CircleShape)); + break; + + case b2Shape.e_polyShape: + shape->~b2Shape(); + allocator.Free(shape, sizeof(b2PolyShape)); + break; + + default: + b2Assert(false); + } + + shape = NULL;*/ + + // FROM DESTRUCTOR + 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) + { + //b2Settings.b2Assert(count >= 3); + + //var center = new b2Vec2(0.0, 0.0); + var center = new b2Vec2(); + center.SetZero(); + + var area = 0.0; + var I = 0.0; + + // pRef is the reference point for forming triangles. + // It's location doesn't change the result (except for rounding error). + var pRef = new b2Vec2(0.0, 0.0); + + var inv3 = 1.0 / 3.0; + + for (var i = 0; i < count; ++i) + { + // Triangle vertices. + 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; + + // Area weighted centroid + // center += triangleArea * inv3 * (p1 + p2 + p3); + 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); + } + + // Total mass + massData.mass = rho * area; + + // Center of mass + //b2Settings.b2Assert(area > Number.MIN_VALUE); + center.Multiply( 1.0 / area ); + massData.center = center; + + // Inertia tensor relative to the center. + I = rho * (I - area * b2Math.b2Dot(center, center)); + massData.I = I; + }; +b2Shape.PolyCentroid = function(vs, count, out) + { + //b2Settings.b2Assert(count >= 3); + + //b2Vec2 c; c.Set(0.0f, 0.0f); + var cX = 0.0; + var cY = 0.0; + //float32 area = 0.0f; + var area = 0.0; + + // pRef is the reference point for forming triangles. + // It's location doesn't change the result (except for rounding error). + //b2Vec2 pRef(0.0f, 0.0f); + var pRefX = 0.0; + var pRefY = 0.0; + /* + // This code would put the reference point inside the polygon. + for (var i = 0; i < count; ++i) + { + //pRef += vs[i]; + pRef.x += vs[i].x; + pRef.y += vs[i].y; + } + pRef.x *= 1.0 / count; + pRef.y *= 1.0 / count; + */ + + //const float32 inv3 = 1.0f / 3.0f; + var inv3 = 1.0 / 3.0; + + for (var i = 0; i < count; ++i) + { + // Triangle vertices. + //b2Vec2 p1 = pRef; + var p1X = pRefX; + var p1Y = pRefY; + //b2Vec2 p2 = vs[i]; + var p2X = vs[i].x; + var p2Y = vs[i].y; + //b2Vec2 p3 = i + 1 < count ? vs[i+1] : vs[0]; + var p3X = i + 1 < count ? vs[i+1].x : vs[0].x; + var p3Y = i + 1 < count ? vs[i+1].y : vs[0].y; + + //b2Vec2 e1 = p2 - p1; + var e1X = p2X - p1X; + var e1Y = p2Y - p1Y; + //b2Vec2 e2 = p3 - p1; + var e2X = p3X - p1X; + var e2Y = p3Y - p1Y; + + //float32 D = b2Cross(e1, e2); + var D = (e1X * e2Y - e1Y * e2X); + + //float32 triangleArea = 0.5f * D; + var triangleArea = 0.5 * D; + area += triangleArea; + + // Area weighted centroid + //c += triangleArea * inv3 * (p1 + p2 + p3); + cX += triangleArea * inv3 * (p1X + p2X + p3X); + cY += triangleArea * inv3 * (p1Y + p2Y + p3Y); + } + + // Centroid + //b2Settings.b2Assert(area > Number.MIN_VALUE); + cX *= 1.0 / area; + cY *= 1.0 / area; + + // Replace return with 'out' vector + //return c; + out.Set(cX, cY); + }; diff --git a/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2ShapeDef.js b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2ShapeDef.js new file mode 100644 index 0000000..8f48f07 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/collision/shapes/b2ShapeDef.js @@ -0,0 +1,109 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + + +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; + }, + + //virtual ~b2ShapeDef() {} + + 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, + + // The collision category bits. Normally you would just set one bit. + categoryBits: 0, + + // The collision mask bits. This states the categories that this + // shape would accept for collision. + maskBits: 0, + + // Collision groups allow a certain group of objects to never collide (negative) + // or always collide (positive). Zero means no collision group. Non-zero group + // filtering always wins against the mask bits. + groupIndex: 0}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/common/b2Settings.js b/o3d/samples/box2d-3d/third_party/box2d/common/b2Settings.js new file mode 100644 index 0000000..890fde2 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/common/b2Settings.js @@ -0,0 +1,72 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2Settings = Class.create(); +b2Settings.prototype = { + + + + // Define your unit system here. The default system is + // meters-kilograms-seconds. For the tuning to work well, + // your dynamic objects should be bigger than a pebble and smaller + // than a house. + //static public const b2Settings.b2_lengthUnitsPerMeter = 1.0; + + // Use this for pixels: + + // Global tuning constants based on MKS units. + + // Collision + + // Dynamics + + // Sleep + + // assert + + 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++; + } + }; diff --git a/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Mat22.js b/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Mat22.js new file mode 100644 index 0000000..d5fb1fc --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Mat22.js @@ -0,0 +1,130 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2Mat22 = Class.create(); +b2Mat22.prototype = +{ + initialize: function(angle, c1, c2) + { + if (angle==null) angle = 0; + // initialize instance variables for references + 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 B = new b2Mat22(); + var det = a * d - b * c; + //b2Settings.b2Assert(det != 0.0); + 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; + }, + + // this.Solve A * x = b + Solve: function(out, bX, bY) + { + //float32 a11 = this.col1.x, a12 = this.col2.x, a21 = this.col1.y, a22 = this.col2.y; + var a11 = this.col1.x; + var a12 = this.col2.x; + var a21 = this.col1.y; + var a22 = this.col2.y; + //float32 det = a11 * a22 - a12 * a21; + var det = a11 * a22 - a12 * a21; + //b2Settings.b2Assert(det != 0.0); + 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()}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Math.js b/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Math.js new file mode 100644 index 0000000..85185bf --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Math.js @@ -0,0 +1,218 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +var b2Math = Class.create(); +b2Math.prototype = { + + + /*static public function b2InvSqrt(x) + { + float32 xhalf = 0.5f * x; + int32 i = *(int32*)&x; + i = 0x5f3759df - (i >> 1); + x = *(float32*)&i; + x = x * (1.5f - xhalf * x * x); + return x; + }*/ + + + + + + + + + + + + // A * B + + // A^T * B + + + + + + + + + + + + // b2Math.b2Random number in range [-1,1] + + /*inline float32 b2Math.b2Random(float32 lo, float32 hi) + { + float32 r = (float32)rand(); + r /= RAND_MAX; + r = (hi - lo) * r + lo; + return r; + }*/ + + // "Next Largest Power of 2 + // Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm + // that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with + // the same most significant 1, but all 1's below it. Adding 1 to that value yields the next + // largest power of 2. For a 32-bit value:" + + + + // Temp vector functions to reduce calls to 'new' + /*static public var tempVec = new b2Vec2(); + + + static public var tempAABB = new b2AABB(); */ + + + + 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(); diff --git a/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Vec2.js b/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Vec2.js new file mode 100644 index 0000000..24acd20 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/common/math/b2Vec2.js @@ -0,0 +1,131 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +// b2Vec2 has no constructor so that it +// can be placed in a union. +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_); + }; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Body.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Body.js new file mode 100644 index 0000000..6716d2f --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Body.js @@ -0,0 +1,469 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + +// A rigid body. Internal computation are done in terms +// of the center of mass position. The center of mass may +// be offset from the body's origin. +var b2Body = Class.create(); +b2Body.prototype = +{ + // Set the position of the body's origin and rotation (radians). + // This breaks any contacts and wakes the other bodies. + 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(); + }, + + // Get the position of the body's origin. The body's origin does not + // necessarily coincide with the center of mass. It depends on how the + // shapes are created. + GetOriginPosition: function(){ + return b2Math.SubtractVV(this.m_position, b2Math.b2MulMV(this.m_R, this.m_center)); + }, + + // Set the position of the body's center of mass and rotation (radians). + // This breaks any contacts and wakes the other bodies. + 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(); + }, + + // Get the position of the body's center of mass. The body's center of mass + // does not necessarily coincide with the body's origin. It depends on how the + // shapes are created. + GetCenterPosition: function(){ + return this.m_position; + }, + + // Get the rotation in radians. + GetRotation: function(){ + return this.m_rotation; + }, + + GetRotationMatrix: function(){ + return this.m_R; + }, + + // Set/Get the linear velocity of the center of mass. + SetLinearVelocity: function(v){ + this.m_linearVelocity.SetV(v); + }, + GetLinearVelocity: function(){ + return this.m_linearVelocity; + }, + + // Set/Get the angular velocity. + SetAngularVelocity: function(w){ + this.m_angularVelocity = w; + }, + GetAngularVelocity: function(){ + return this.m_angularVelocity; + }, + + // Apply a force at a world point. Additive. + 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); + } + }, + + // Apply a torque. Additive. + ApplyTorque: function(torque) + { + if (this.IsSleeping() == false) + { + this.m_torque += torque; + } + }, + + // Apply an impulse at a point. This immediately modifies the velocity. + 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; + }, + + // Get the world coordinates of a point give the local coordinates + // relative to the body's center of mass. + GetWorldPoint: function(localPoint){ + return b2Math.AddVV(this.m_position , b2Math.b2MulMV(this.m_R, localPoint)); + }, + + // Get the world coordinates of a vector given the local coordinates. + GetWorldVector: function(localVector){ + return b2Math.b2MulMV(this.m_R, localVector); + }, + + // Returns a local point relative to the center of mass given a world point. + GetLocalPoint: function(worldPoint){ + return b2Math.b2MulTMV(this.m_R, b2Math.SubtractVV(worldPoint, this.m_position)); + }, + + // Returns a local vector given a world vector. + GetLocalVector: function(worldVector){ + return b2Math.b2MulTMV(this.m_R, worldVector); + }, + + // Is this body static (immovable)? + IsStatic: function(){ + return (this.m_flags & b2Body.e_staticFlag) == b2Body.e_staticFlag; + }, + + IsFrozen: function() + { + return (this.m_flags & b2Body.e_frozenFlag) == b2Body.e_frozenFlag; + }, + + // Is this body sleeping (not simulating). + IsSleeping: function(){ + return (this.m_flags & b2Body.e_sleepFlag) == b2Body.e_sleepFlag; + }, + + // You can disable sleeping on this particular body. + AllowSleeping: function(flag) + { + if (flag) + { + this.m_flags |= b2Body.e_allowSleepFlag; + } + else + { + this.m_flags &= ~b2Body.e_allowSleepFlag; + this.WakeUp(); + } + }, + + // Wake up this body so it will begin simulating. + WakeUp: function(){ + this.m_flags &= ~b2Body.e_sleepFlag; + this.m_sleepTime = 0.0; + }, + + // Get the list of all shapes attached to this body. + GetShapeList: function(){ + return this.m_shapeList; + }, + + GetContactList: function() + { + return this.m_contactList; + }, + + GetJointList: function() + { + return this.m_jointList; + }, + + // Get the next body in the world's body list. + GetNext: function(){ + return this.m_next; + }, + + GetUserData: function(){ + return this.m_userData; + }, + + //--------------- Internals Below ------------------- + + initialize: function(bd, world){ + // initialize instance variables for references + 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(); + } + + // Compute the shape mass properties, the bodies total mass and COM. + 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 += massData->mass * (sd->localPosition + massData->center); + 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; + } + + // Compute center of mass, and shift the origin to the COM. + 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; + } + + // Compute the moment of inertia. + 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; + } + + // Compute the center of mass velocity. + 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; + + // Create the shapes. + 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; + }, + // does not support destructors + /*~b2Body(){ + b2Shape* s = this.m_shapeList; + while (s) + { + b2Shape* s0 = s; + s = s->this.m_next; + + b2Shape::this.Destroy(s0); + } + }*/ + + Destroy: function(){ + var s = this.m_shapeList; + while (s) + { + var s0 = s; + s = s.m_next; + + b2Shape.Destroy(s0); + } + }, + + // Temp mat + sMat0: new b2Mat22(), + SynchronizeShapes: function(){ + //b2Mat22 R0(this.m_rotation0); + 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); + } + }, + + // This is used to prevent connected bodies from colliding. + // It may lie, depending on the collideConnected flag. + 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), + + // Conservative advancement data. + 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; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2BodyDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2BodyDef.js new file mode 100644 index 0000000..c586b37 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2BodyDef.js @@ -0,0 +1,69 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2BodyDef = Class.create(); +b2BodyDef.prototype = +{ + initialize: function() + { + // initialize instance variables for references + 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; + } + } + }}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2CollisionFilter.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2CollisionFilter.js new file mode 100644 index 0000000..6724f25 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2CollisionFilter.js @@ -0,0 +1,42 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + + +var b2CollisionFilter = Class.create(); +b2CollisionFilter.prototype = +{ + + // Return true if contact calculations should be performed between these two shapes. + 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; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2ContactManager.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2ContactManager.js new file mode 100644 index 0000000..cfb2df7 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2ContactManager.js @@ -0,0 +1,337 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2ContactManager = Class.create(); +Object.extend(b2ContactManager.prototype, b2PairCallback.prototype); +Object.extend(b2ContactManager.prototype, +{ + initialize: function(){ + // The constructor for b2PairCallback + // + + // initialize instance variables for references + this.m_nullContact = new b2NullContact(); + // + + this.m_world = null; + this.m_destroyImmediate = false; + }, + + // This is a callback from the broadphase when two AABB proxies begin + // to overlap. We create a b2Contact to manage the narrow phase. + 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; + } + + // Ensure that body2 is dynamic (body1 is static or dynamic). + if (body2.m_invMass == 0.0) + { + var tempShape = shape1; + shape1 = shape2; + shape2 = tempShape; + //b2Math.b2Swap(shape1, shape2); + var tempBody = body1; + body1 = body2; + body2 = tempBody; + //b2Math.b2Swap(body1, body2); + } + + // Call the factory. + var contact = b2Contact.Create(shape1, shape2, this.m_world.m_blockAllocator); + + if (contact == null) + { + return this.m_nullContact; + } + else + { + // Insert into the world. + 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; + }, + + // This is a callback from the broadphase when two AABB proxies cease + // to overlap. We destroy the b2Contact. + PairRemoved: function(proxyUserData1, proxyUserData2, pairUserData){ + + if (pairUserData == null) + { + return; + } + + var c = pairUserData; + if (c != this.m_nullContact) + { + //b2Settings.b2Assert(this.m_world.m_contactCount > 0); + if (this.m_destroyImmediate == true) + { + this.DestroyContact(c); + c = null; + } + else + { + c.m_flags |= b2Contact.e_destroyFlag; + } + } + }, + + DestroyContact: function(c) + { + + //b2Settings.b2Assert(this.m_world.m_contactCount > 0); + + // Remove from the world. + 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 there are contact points, then disconnect from the island graph. + 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; + + // Wake up touching bodies. + body1.WakeUp(); + body2.WakeUp(); + + // Remove from body 1 + 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; + + // Remove from body 2 + 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; + } + + // Call the factory. + b2Contact.Destroy(c, this.m_world.m_blockAllocator); + --this.m_world.m_contactCount; + }, + + + // Destroy any contacts marked for deferred destruction. + 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; + } + } + }, + + + // This is the top level collision call for the time step. Here + // all the narrow phase collision is processed for the world + // contact list. + 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) + { + //b2Settings.b2Assert(c.GetManifolds().pointCount > 0); + + // Connect to island graph. + body1 = c.m_shape1.m_body; + body2 = c.m_shape2.m_body; + node1 = c.m_node1; + node2 = c.m_node2; + + // Connect to body 1 + 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; + + // Connect to body 2 + 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) + { + // Disconnect from island graph. + body1 = c.m_shape1.m_body; + body2 = c.m_shape2.m_body; + node1 = c.m_node1; + node2 = c.m_node2; + + // Remove from body 1 + 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; + + // Remove from body 2 + 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, + + // This lets us provide broadphase proxy pair user data for + // contacts that shouldn't exist. + m_nullContact: new b2NullContact(), + m_destroyImmediate: null}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Island.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Island.js new file mode 100644 index 0000000..ee15d71 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2Island.js @@ -0,0 +1,331 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +/* +Position Correction Notes +========================= +I tried the several algorithms for position correction of the 2D revolute joint. +I looked at these systems: +- simple pendulum (1m diameter sphere on massless 5m stick) with initial angular velocity of 100 rad/s. +- suspension bridge with 30 1m long planks of length 1m. +- multi-link chain with 30 1m long links. + +Here are the algorithms: + +Baumgarte - A fraction of the position error is added to the velocity error. There is no +separate position solver. + +Pseudo Velocities - After the velocity solver and position integration, +the position error, Jacobian, and effective mass are recomputed. Then +the velocity constraints are solved with pseudo velocities and a fraction +of the position error is added to the pseudo velocity error. The pseudo +velocities are initialized to zero and there is no warm-starting. After +the position solver, the pseudo velocities are added to the positions. +This is also called the First Order World method or the Position LCP method. + +Modified Nonlinear Gauss-Seidel (NGS) - Like Pseudo Velocities except the +position error is re-computed for each constraint and the positions are updated +after the constraint is solved. The radius vectors (aka Jacobians) are +re-computed too (otherwise the algorithm has horrible instability). The pseudo +velocity states are not needed because they are effectively zero at the beginning +of each iteration. Since we have the current position error, we allow the +iterations to terminate early if the error becomes smaller than b2_linearSlop. + +Full NGS or just NGS - Like Modified NGS except the effective mass are re-computed +each time a constraint is solved. + +Here are the results: +Baumgarte - this is the cheapest algorithm but it has some stability problems, +especially with the bridge. The chain links separate easily close to the root +and they jitter struggle to pull together. This is one of the most common +methods in the field. The big drawback is that the position correction artificially +affects the momentum, thus leading to instabilities and false bounce. I used a +bias factor of 0.2. A larger bias factor makes the bridge less stable, a smaller +factor makes joints and contacts more spongy. + +Pseudo Velocities - the is more stable than the Baumgarte method. The bridge is +stable. However, joints still separate with large angular velocities. Drag the +simple pendulum in a circle quickly and the joint will separate. The chain separates +easily and does not recover. I used a bias factor of 0.2. A larger value lead to +the bridge collapsing when a heavy cube drops on it. + +Modified NGS - this algorithm is better in some ways than Baumgarte and Pseudo +Velocities, but in other ways it is worse. The bridge and chain are much more +stable, but the simple pendulum goes unstable at high angular velocities. + +Full NGS - stable in all tests. The joints display good stiffness. The bridge +still sags, but this is better than infinite forces. + +Recommendations +Pseudo Velocities are not really worthwhile because the bridge and chain cannot +recover from joint separation. In other cases the benefit over Baumgarte is small. + +Modified NGS is not a robust method for the revolute joint due to the violent +instability seen in the simple pendulum. Perhaps it is viable with other constraint +types, especially scalar constraints where the effective mass is a scalar. + +This leaves Baumgarte and Full NGS. Baumgarte has small, but manageable instabilities +and is very fast. I don't think we can escape Baumgarte, especially in highly +demanding cases where high constraint fidelity is not needed. + +Full NGS is robust and easy on the eyes. I recommend this option for +higher fidelity simulation and certainly for suspension bridges and long chains. +Full NGS might be a good choice for ragdolls, especially motorized ragdolls where +joint separation can be problematic. The number of NGS iterations can be reduced +for better performance without harming robustness much. + +Each joint in a can be handled differently in the position solver. So I recommend +a system where the user can select the algorithm on a per joint basis. I would +probably default to the slower Full NGS and let the user select the faster +Baumgarte method in performance critical scenarios. +*/ + + +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 = (b2Body**)allocator->Allocate(bodyCapacity * sizeof(b2Body*)); + this.m_bodies = new Array(bodyCapacity); + for (i = 0; i < bodyCapacity; i++) + this.m_bodies[i] = null; + + //this.m_contacts = (b2Contact**)allocator->Allocate(contactCapacity * sizeof(b2Contact*)); + this.m_contacts = new Array(contactCapacity); + for (i = 0; i < contactCapacity; i++) + this.m_contacts[i] = null; + + //this.m_joints = (b2Joint**)allocator->Allocate(jointCapacity * sizeof(b2Joint*)); + this.m_joints = new Array(jointCapacity); + for (i = 0; i < jointCapacity; i++) + this.m_joints[i] = null; + + this.m_allocator = allocator; + }, + //~b2Island(); + + 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 *= b.m_linearDamping; + b.m_linearVelocity.Multiply(b.m_linearDamping); + b.m_angularVelocity *= b.m_angularDamping; + + // Store positions for conservative advancement. + 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); + + // Pre-solve + contactSolver.PreSolve(); + + for (i = 0; i < this.m_jointCount; ++i) + { + this.m_joints[i].PrepareVelocitySolver(); + } + + // this.Solve velocity constraints. + for (i = 0; i < step.iterations; ++i) + { + contactSolver.SolveVelocityConstraints(); + + for (var j = 0; j < this.m_jointCount; ++j) + { + this.m_joints[j].SolveVelocityConstraints(step); + } + } + + // Integrate positions. + for (i = 0; i < this.m_bodyCount; ++i) + { + b = this.m_bodies[i]; + + if (b.m_invMass == 0.0) + continue; + + //b.m_position.Add( b2Math.MulFV (step.dt, b.m_linearVelocity) ); + 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(); + } + + // this.Solve position constraints. + 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; + } + } + } + + // Post-solve. + contactSolver.PostSolve(); + + // Synchronize shapes and reset forces. + 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) + { + //b2Settings.b2Assert(this.m_bodyCount < this.m_bodyCapacity); + this.m_bodies[this.m_bodyCount++] = body; + }, + + AddContact: function(contact) + { + //b2Settings.b2Assert(this.m_contactCount < this.m_contactCapacity); + this.m_contacts[this.m_contactCount++] = contact; + }, + + AddJoint: function(joint) + { + //b2Settings.b2Assert(this.m_jointCount < this.m_jointCapacity); + 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; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2TimeStep.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2TimeStep.js new file mode 100644 index 0000000..599390ed --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2TimeStep.js @@ -0,0 +1,27 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +var b2TimeStep = Class.create(); +b2TimeStep.prototype = +{ + dt: null, + inv_dt: null, + iterations: 0, + initialize: function() {}}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2World.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2World.js new file mode 100644 index 0000000..b90ae43 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2World.js @@ -0,0 +1,522 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + +var b2World = Class.create(); +b2World.prototype = +{ + initialize: function(worldAABB, gravity, doSleep){ + // initialize instance variables for references + 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); + }, + //~b2World(){ + // this.DestroyBody(this.m_groundBody); + // delete this.m_broadPhase; + //} + + // Set a callback to notify you when a joint is implicitly destroyed + // when an attached body is destroyed. + SetListener: function(listener){ + this.m_listener = listener; + }, + + // Register a collision filter to provide specific control over collision. + // Otherwise the default filter is used (b2CollisionFilter). + SetFilter: function(filter){ + this.m_filter = filter; + }, + + // Create and destroy rigid bodies. Destruction is deferred until the + // the next call to this.Step. This is done so that bodies may be destroyed + // while you iterate through the contact list. + CreateBody: function(def){ + //void* mem = this.m_blockAllocator.Allocate(sizeof(b2Body)); + 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; + }, + // Body destruction is deferred to make contact processing more robust. + DestroyBody: function(b) + { + + if (b.m_flags & b2Body.e_destroyFlag) + { + return; + } + + // Remove from normal body list. + 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; + //b2Settings.b2Assert(this.m_bodyCount > 0); + --this.m_bodyCount; + + //b->~b2Body(); + //b.Destroy(); + // Add to the deferred destruction list. + 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) + { + //b2Settings.b2Assert((b.m_flags & b2Body.e_destroyFlag) != 0); + + // Preserve the next pointer. + var b0 = b; + b = b.m_next; + + // Delete the attached joints + 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_blockAllocator.Free(b0, sizeof(b2Body)); + } + + // Reset the list. + this.m_bodyDestroyList = null; + + this.m_contactManager.m_destroyImmediate = false; + }, + + CreateJoint: function(def){ + var j = b2Joint.Create(def, this.m_blockAllocator); + + // Connect to the world list. + 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; + + // Connect to the bodies + 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 the joint prevents collisions, then reset collision filtering. + if (def.collideConnected == false) + { + // Reset the proxies on the body with the minimum number of shapes. + 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; + + // Remove from the world. + 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; + } + + // Disconnect from island graph. + var body1 = j.m_body1; + var body2 = j.m_body2; + + // Wake up touching bodies. + body1.WakeUp(); + body2.WakeUp(); + + // Remove from body 1 + 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; + + // Remove from body 2 + 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); + + //b2Settings.b2Assert(this.m_jointCount > 0); + --this.m_jointCount; + + // If the joint prevents collisions, then reset collision filtering. + if (collideConnected == false) + { + // Reset the proxies on the body with the minimum number of shapes. + 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); + } + } + }, + + // The world provides a single ground body with no collision shapes. You + // can use this to simplify the creation of joints. + GetGroundBody: function(){ + return this.m_groundBody; + }, + + + step: new b2TimeStep(), + // this.Step + 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; + + // Handle deferred contact destruction. + this.m_contactManager.CleanContactList(); + + // Handle deferred body destruction. + this.CleanBodyList(); + + // Update contacts. + this.m_contactManager.Collide(); + + // Size the island for the worst case. + var island = new b2Island(this.m_bodyCount, this.m_contactCount, this.m_jointCount, this.m_stackAllocator); + + // Clear all the island flags. + 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; + } + + // Build and simulate all awake islands. + var stackSize = this.m_bodyCount; + //var stack = (b2Body**)this.m_stackAllocator.Allocate(stackSize * sizeof(b2Body*)); + 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; + } + + // Reset island and stack. + island.Clear(); + var stackCount = 0; + stack[stackCount++] = seed; + seed.m_flags |= b2Body.e_islandFlag;; + + // Perform a depth first search (DFS) on the constraint graph. + while (stackCount > 0) + { + // Grab the next body off the stack and add it to the island. + b = stack[--stackCount]; + island.AddBody(b); + + // Make sure the body is awake. + b.m_flags &= ~b2Body.e_sleepFlag; + + // To keep islands, we don't + // propagate islands across static bodies. + if (b.m_flags & b2Body.e_staticFlag) + { + continue; + } + + // Search all contacts connected to this body. + 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; + } + + //b2Settings.b2Assert(stackCount < stackSize); + stack[stackCount++] = other; + other.m_flags |= b2Body.e_islandFlag; + } + + // Search all joints connect to this body. + 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; + } + + //b2Settings.b2Assert(stackCount < stackSize); + 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); + } + + // Post solve cleanup. + for (var i = 0; i < island.m_bodyCount; ++i) + { + // Allow static bodies to participate in other islands. + b = island.m_bodies[i]; + if (b.m_flags & b2Body.e_staticFlag) + { + b.m_flags &= ~b2Body.e_islandFlag; + } + + // Handle newly frozen bodies. + 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(); + + //this.m_stackAllocator.Free(stack); + }, + + // this.Query the world for all shapes that potentially overlap the + // provided AABB. You provide a shape pointer buffer of specified + // size. The number of shapes found is returned. + Query: function(aabb, shapes, maxCount){ + + //void** results = (void**)this.m_stackAllocator.Allocate(maxCount * sizeof(void*)); + var results = new Array(); + var count = this.m_broadPhase.QueryAABB(aabb, results, maxCount); + + for (var i = 0; i < count; ++i) + { + shapes[i] = results[i]; + } + + //this.m_stackAllocator.Free(results); + return count; + }, + + // You can use these to iterate over all the bodies, joints, and contacts. + GetBodyList: function(){ + return this.m_bodyList; + }, + GetJointList: function(){ + return this.m_jointList; + }, + GetContactList: function(){ + return this.m_contactList; + }, + + //--------------- Internals Below ------------------- + + 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, + + // These bodies will be destroyed at the next time this.step. + 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; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2WorldListener.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2WorldListener.js new file mode 100644 index 0000000..9167570 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/b2WorldListener.js @@ -0,0 +1,52 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + + +var b2WorldListener = Class.create(); +b2WorldListener.prototype = +{ + + // If a body is destroyed, then any joints attached to it are also destroyed. + // This prevents memory leaks, but you may unexpectedly be left with an + // orphaned joint pointer. + // Box2D will notify you when a joint is implicitly destroyed. + // It is NOT called if you directly destroy a joint. + // Implement this abstract class and provide it to b2World via + // b2World::SetListener(). + // DO NOT modify the Box2D world inside this callback. + NotifyJointDestroyed: function(joint){}, + + // This is called when a body's shape passes outside of the world boundary. If you + // override this and pass back e_destroyBody, you must nullify your copies of the + // body pointer. + NotifyBoundaryViolated: function(body) + { + //NOT_USED(body); + return b2WorldListener.b2_freezeBody; + }, + + + + initialize: function() {}}; +b2WorldListener.b2_freezeBody = 0; +b2WorldListener.b2_destroyBody = 1; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2CircleContact.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2CircleContact.js new file mode 100644 index 0000000..dc8df84 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2CircleContact.js @@ -0,0 +1,102 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + +var b2CircleContact = Class.create(); +Object.extend(b2CircleContact.prototype, b2Contact.prototype); +Object.extend(b2CircleContact.prototype, +{ + + initialize: function(s1, s2) { + // The constructor for b2Contact + // initialize instance variables for references + 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; + // + + // initialize instance variables for references + this.m_manifold = [new b2Manifold()]; + // + + //super(shape1, shape2); + + //b2Settings.b2Assert(this.m_shape1.m_type == b2Shape.e_circleShape); + //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; + }, + //~b2CircleContact() {} + + 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){ + // + }; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Conservative.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Conservative.js new file mode 100644 index 0000000..fe1cd4b --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Conservative.js @@ -0,0 +1,228 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2Conservative = Class.create(); +b2Conservative.prototype = { + + // Temp vars + + + + 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(); + + //b2Vec2 v1 = body1->m_position - body1->m_position0; + var v1X = body1.m_position.x - body1.m_position0.x; + var v1Y = body1.m_position.y - body1.m_position0.y; + //float32 omega1 = body1->m_rotation - body1->m_rotation0; + var omega1 = body1.m_rotation - body1.m_rotation0; + //b2Vec2 v2 = body2->m_position - body2->m_position0; + var v2X = body2.m_position.x - body2.m_position0.x; + var v2Y = body2.m_position.y - body2.m_position0.y; + //float32 omega2 = body2->m_rotation - body2->m_rotation0; + var omega2 = body2.m_rotation - body2.m_rotation0; + + //float32 r1 = shape1->GetMaxRadius(); + var r1 = shape1.GetMaxRadius(); + //float32 r2 = shape2->GetMaxRadius(); + var r2 = shape2.GetMaxRadius(); + + //b2Vec2 p1Start = body1->m_position0; + var p1StartX = body1.m_position0.x; + var p1StartY = body1.m_position0.y; + //float32 a1Start = body1->m_rotation0; + var a1Start = body1.m_rotation0; + + //b2Vec2 p2Start = body2->m_position0; + var p2StartX = body2.m_position0.x; + var p2StartY = body2.m_position0.y; + //float32 a2Start = body2->m_rotation0; + var a2Start = body2.m_rotation0; + + //b2Vec2 p1 = p1Start; + var p1X = p1StartX; + var p1Y = p1StartY; + //float32 a1 = a1Start; + var a1 = a1Start; + //b2Vec2 p2 = p2Start; + var p2X = p2StartX; + var p2Y = p2StartY; + //float32 a2 = a2Start; + var a2 = a2Start; + + //b2Mat22 b2Conservative.R1(a1), b2Conservative.R2(a2); + b2Conservative.R1.Set(a1); + b2Conservative.R2.Set(a2); + + //shape1->QuickSync(p1, b2Conservative.R1); + shape1.QuickSync(p1, b2Conservative.R1); + //shape2->QuickSync(p2, b2Conservative.R2); + shape2.QuickSync(p2, b2Conservative.R2); + + //float32 s1 = 0.0f; + var s1 = 0.0; + //const int32 maxIterations = 10; + var maxIterations = 10; + //b2Vec2 d; + var dX; + var dY; + //float32 invRelativeVelocity = 0.0f; + var invRelativeVelocity = 0.0; + //bool hit = true; + var hit = true; + //b2Vec2 b2Conservative.x1, b2Conservative.x2; moved to static var + for (var iter = 0; iter < maxIterations; ++iter) + { + // Get the accurate distance between shapes. + //float32 distance = b2Distance.Distance(&b2Conservative.x1, &b2Conservative.x2, shape1, shape2); + 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) + { + //b2Vec2 d = b2Conservative.x2 - b2Conservative.x1; + dX = b2Conservative.x2.x - b2Conservative.x1.x; + dY = b2Conservative.x2.y - b2Conservative.x1.y; + //d.Normalize(); + var dLen = Math.sqrt(dX*dX + dY*dY); + //float32 relativeVelocity = b2Dot(d, v1 - v2) + b2Abs(omega1) * r1 + b2Abs(omega2) * r2; + 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; + } + + // Get the conservative movement. + //float32 ds = distance * invRelativeVelocity; + var ds = distance * invRelativeVelocity; + //float32 s2 = s1 + ds; + 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; + + // Move forward conservatively. + //p1 = p1Start + s1 * v1; + p1X = p1StartX + s1 * v1.x; + p1Y = p1StartY + s1 * v1.y; + //a1 = a1Start + s1 * omega1; + a1 = a1Start + s1 * omega1; + //p2 = p2Start + s1 * v2; + p2X = p2StartX + s1 * v2.x; + p2Y = p2StartY + s1 * v2.y; + //a2 = a2Start + s1 * omega2; + a2 = a2Start + s1 * omega2; + + b2Conservative.R1.Set(a1); + b2Conservative.R2.Set(a2); + shape1.QuickSync(p1, b2Conservative.R1); + shape2.QuickSync(p2, b2Conservative.R2); + } + + if (hit) + { + // Hit, move bodies to safe position and re-sync shapes. + //b2Vec2 d = b2Conservative.x2 - b2Conservative.x1; + dX = b2Conservative.x2.x - b2Conservative.x1.x; + dY = b2Conservative.x2.y - b2Conservative.x1.y; + //float32 length = d.Length(); + var length = Math.sqrt(dX*dX + dY*dY); + if (length > FLT_EPSILON) + { + d *= b2_linearSlop / length; + } + + if (body1.IsStatic()) + { + //body1.m_position = p1; + body1.m_position.x = p1X; + body1.m_position.y = p1Y; + } + else + { + //body1.m_position = p1 - d; + 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 = p2; + body2.m_position.x = p2X; + body2.m_position.y = p2Y; + } + else + { + //body2->m_position = p2 + d; + body2.m_position.x = p2X + dX; + body2.m_position.y = p2Y + dY; + } + //body2.m_position = p2 + d; + 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; + } + + // No hit, restore shapes. + shape1.QuickSync(body1.m_position, body1.m_R); + shape2.QuickSync(body2.m_position, body2.m_R); + return false; + }; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Contact.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Contact.js new file mode 100644 index 0000000..283feaf --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2Contact.js @@ -0,0 +1,201 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +//typedef b2Contact* b2ContactCreateFcn(b2Shape* shape1, b2Shape* shape2, b2BlockAllocator* allocator); +//typedef void b2ContactDestroyFcn(b2Contact* contact, b2BlockAllocator* allocator); + + + +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; + }, + + //--------------- Internals Below ------------------- + + // this.m_flags + // enum + + + initialize: function(s1, s2) + { + // initialize instance variables for references + 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; + }, + + //virtual ~b2Contact() {} + + Evaluate: function(){}, + + m_flags: 0, + + // World pool and list pointers. + m_prev: null, + m_next: null, + + // Nodes for connecting bodies. + m_node1: new b2ContactNode(), + m_node2: new b2ContactNode(), + + m_shape1: null, + m_shape2: null, + + m_manifoldCount: 0, + + // Combined friction + m_friction: null, + m_restitution: null}; +b2Contact.e_islandFlag = 0x0001; +b2Contact.e_destroyFlag = 0x0002; +b2Contact.AddType = function(createFcn, destroyFcn, type1, type2) + { + //b2Settings.b2Assert(b2Shape.e_unknownShape < type1 && type1 < b2Shape.e_shapeTypeCount); + //b2Settings.b2Assert(b2Shape.e_unknownShape < type2 && type2 < b2Shape.e_shapeTypeCount); + + 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; + + //b2Settings.b2Assert(b2Shape.e_unknownShape < type1 && type1 < b2Shape.e_shapeTypeCount); + //b2Settings.b2Assert(b2Shape.e_unknownShape < type2 && type2 < b2Shape.e_shapeTypeCount); + + 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){ + //b2Settings.b2Assert(b2Contact.s_initialized == true); + + 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; + + //b2Settings.b2Assert(b2Shape.e_unknownShape < type1 && type1 < b2Shape.e_shapeTypeCount); + //b2Settings.b2Assert(b2Shape.e_unknownShape < type2 && type2 < b2Shape.e_shapeTypeCount); + + var destroyFcn = b2Contact.s_registers[type1][type2].destroyFcn; + destroyFcn(contact, allocator); + }; +b2Contact.s_registers = null; +b2Contact.s_initialized = false; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraint.js new file mode 100644 index 0000000..79b82e1 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraint.js @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2ContactConstraint = Class.create(); +b2ContactConstraint.prototype = +{ + initialize: function(){ + // initialize instance variables for references + 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}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraintPoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraintPoint.js new file mode 100644 index 0000000..bc3dca6 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactConstraintPoint.js @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +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() { + // initialize instance variables for references + this.localAnchor1 = new b2Vec2(); + this.localAnchor2 = new b2Vec2(); + // +}}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactNode.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactNode.js new file mode 100644 index 0000000..c10e482 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactNode.js @@ -0,0 +1,33 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2ContactNode = Class.create(); +b2ContactNode.prototype = +{ + other: null, + contact: null, + prev: null, + next: null, + initialize: function() {}}; + + + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactRegister.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactRegister.js new file mode 100644 index 0000000..524c352 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactRegister.js @@ -0,0 +1,30 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +var b2ContactRegister = Class.create(); +b2ContactRegister.prototype = +{ + createFcn: null, + destroyFcn: null, + primary: null, + initialize: function() {}}; + + + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactSolver.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactSolver.js new file mode 100644 index 0000000..0b0d3f4 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2ContactSolver.js @@ -0,0 +1,537 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2ContactSolver = Class.create(); +b2ContactSolver.prototype = +{ + initialize: function(contacts, contactCount, allocator){ + // initialize instance variables for references + 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(); + } + + // fill array + 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 v1 = b1.m_linearVelocity.Copy(); + var v1X = b1.m_linearVelocity.x; + var v1Y = b1.m_linearVelocity.y; + //var v2 = b2.m_linearVelocity.Copy(); + 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 ]; + + //b2Settings.b2Assert(manifold.pointCount > 0); + + //var normal = manifold.normal.Copy(); + var normalX = manifold.normal.x; + var normalY = manifold.normal.y; + + //b2Settings.b2Assert(count < this.m_constraintCount); + var c = this.m_constraints[ count ]; + c.body1 = b1; + c.body2 = b2; + c.manifold = manifold; + //c.normal = normal; + 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 r1 = b2Math.SubtractVV( cp.position, b1.m_position ); + var r1X = cp.position.x - b1.m_position.x; + var r1Y = cp.position.y - b1.m_position.y; + //var r2 = b2Math.SubtractVV( cp.position, b2.m_position ); + var r2X = cp.position.x - b2.m_position.x; + var r2Y = cp.position.y - b2.m_position.y; + + //ccp.localAnchor1 = b2Math.b2MulTMV(b1.m_R, r1); + 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; + + //ccp.localAnchor2 = b2Math.b2MulTMV(b2.m_R, r2); + 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 = b2Math.b2Dot(r1, normal); + var rn1 = r1X*normalX + r1Y*normalY; + //var rn2 = b2Math.b2Dot(r2, normal); + 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); + //b2Settings.b2Assert(kNormal > Number.MIN_VALUE); + ccp.normalMass = 1.0 / kNormal; + + //var tangent = b2Math.b2CrossVF(normal, 1.0); + var tangentX = normalY + var tangentY = -normalX; + + //var rt1 = b2Math.b2Dot(r1, tangent); + var rt1 = r1X*tangentX + r1Y*tangentY; + //var rt2 = b2Math.b2Dot(r2, tangent); + 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); + //b2Settings.b2Assert(kTangent > Number.MIN_VALUE); + ccp.tangentMass = 1.0 / kTangent; + + // Setup a velocity bias for restitution. + ccp.velocityBias = 0.0; + if (ccp.separation > 0.0) + { + ccp.velocityBias = -60.0 * ccp.separation; + } + //var vRel = b2Math.b2Dot(c.normal, b2Math.SubtractVV( b2Math.SubtractVV( b2Math.AddVV( v2, b2Math.b2CrossFV(w2, r2)), v1 ), b2Math.b2CrossFV(w1, r1))); + var tX = v2X + (-w2*r2Y) - v1X - (-w1*r1Y); + var tY = v2Y + (w2*r2X) - v1Y - (w1*r1X); + //var vRel = b2Dot(c.normal, tX/Y); + var vRel = c.normal.x*tX + c.normal.y*tY; + if (vRel < -b2Settings.b2_velocityThreshold) + { + ccp.velocityBias += -c.restitution * vRel; + } + } + + ++count; + } + } + + //b2Settings.b2Assert(count == this.m_constraintCount); + }, + //~b2ContactSolver(); + + PreSolve: function(){ + var tVec; + var tVec2; + var tMat; + + // Warm start. + 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 normal = new b2Vec2(c.normal.x, c.normal.y); + var normalX = c.normal.x; + var normalY = c.normal.y; + //var tangent = b2Math.b2CrossVF(normal, 1.0); + 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 P = b2Math.AddVV( b2Math.MulFV(ccp.normalImpulse, normal), b2Math.MulFV(ccp.tangentImpulse, tangent)); + var PX = ccp.normalImpulse*normalX + ccp.tangentImpulse*tangentX; + var PY = ccp.normalImpulse*normalY + ccp.tangentImpulse*tangentY; + + //var r1 = b2Math.b2MulMV(b1.m_R, ccp.localAnchor1); + 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; + + //var r2 = b2Math.b2MulMV(b2.m_R, ccp.localAnchor2); + 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 * b2Math.b2CrossVV(r1, P); + b1.m_angularVelocity -= invI1 * (r1X * PY - r1Y * PX); + //b1.m_linearVelocity.Subtract( b2Math.MulFV(invMass1, P) ); + b1.m_linearVelocity.x -= invMass1 * PX; + b1.m_linearVelocity.y -= invMass1 * PY; + //b2.m_angularVelocity += invI2 * b2Math.b2CrossVV(r2, P); + b2.m_angularVelocity += invI2 * (r2X * PY - r2Y * PX); + //b2.m_linearVelocity.Add( b2Math.MulFV(invMass2, P) ); + 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 normal = new b2Vec2(c.normal.x, c.normal.y); + var normalX = c.normal.x; + var normalY = c.normal.y; + //var tangent = b2Math.b2CrossVF(normal, 1.0); + var tangentX = normalY; + var tangentY = -normalX; + + // Solver normal constraints + var tCount = c.pointCount; + for (j = 0; j < tCount; ++j) + { + ccp = c.points[ j ]; + + //r1 = b2Math.b2MulMV(b1.m_R, ccp.localAnchor1); + 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 + //r2 = b2Math.b2MulMV(b2.m_R, ccp.localAnchor2); + 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 + + // Relative velocity at contact + //var dv = b2Math.SubtractVV( b2Math.AddVV( b2.m_linearVelocity, b2Math.b2CrossFV(b2.m_angularVelocity, r2)), b2Math.SubtractVV(b1.m_linearVelocity, b2Math.b2CrossFV(b1.m_angularVelocity, r1))); + //dv = b2Math.SubtractVV(b2Math.SubtractVV( b2Math.AddVV( b2.m_linearVelocity, b2Math.b2CrossFV(b2.m_angularVelocity, r2)), b1.m_linearVelocity), b2Math.b2CrossFV(b1.m_angularVelocity, r1)); + 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); + + // Compute normal impulse + //var vn = b2Math.b2Dot(dv, normal); + var vn = dvX * normalX + dvY * normalY; + lambda = -ccp.normalMass * (vn - ccp.velocityBias); + + // b2Clamp the accumulated impulse + newImpulse = b2Math.b2Max(ccp.normalImpulse + lambda, 0.0); + lambda = newImpulse - ccp.normalImpulse; + + // Apply contact impulse + //P = b2Math.MulFV(lambda, normal); + PX = lambda * normalX; + PY = lambda * normalY; + + //b1.m_linearVelocity.Subtract( b2Math.MulFV( invMass1, P ) ); + b1_linearVelocity.x -= invMass1 * PX; + b1_linearVelocity.y -= invMass1 * PY; + b1_angularVelocity -= invI1 * (r1X * PY - r1Y * PX); + + //b2.m_linearVelocity.Add( b2Math.MulFV( invMass2, P ) ); + b2_linearVelocity.x += invMass2 * PX; + b2_linearVelocity.y += invMass2 * PY; + b2_angularVelocity += invI2 * (r2X * PY - r2Y * PX); + + ccp.normalImpulse = newImpulse; + + + + // MOVED FROM BELOW + // Relative velocity at contact + //var dv = b2.m_linearVelocity + b2Cross(b2.m_angularVelocity, r2) - b1.m_linearVelocity - b2Cross(b1.m_angularVelocity, r1); + //dv = b2Math.SubtractVV(b2Math.SubtractVV(b2Math.AddVV(b2.m_linearVelocity, b2Math.b2CrossFV(b2.m_angularVelocity, r2)), b1.m_linearVelocity), b2Math.b2CrossFV(b1.m_angularVelocity, r1)); + 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); + + // Compute tangent impulse + var vt = dvX*tangentX + dvY*tangentY; + lambda = ccp.tangentMass * (-vt); + + // b2Clamp the accumulated impulse + var maxFriction = c.friction * ccp.normalImpulse; + newImpulse = b2Math.b2Clamp(ccp.tangentImpulse + lambda, -maxFriction, maxFriction); + lambda = newImpulse - ccp.tangentImpulse; + + // Apply contact impulse + //P = b2Math.MulFV(lambda, tangent); + PX = lambda * tangentX; + PY = lambda * tangentY; + + //b1.m_linearVelocity.Subtract( b2Math.MulFV( invMass1, P ) ); + b1_linearVelocity.x -= invMass1 * PX; + b1_linearVelocity.y -= invMass1 * PY; + b1_angularVelocity -= invI1 * (r1X * PY - r1Y * PX); + + //b2.m_linearVelocity.Add( b2Math.MulFV( invMass2, P ) ); + b2_linearVelocity.x += invMass2 * PX; + b2_linearVelocity.y += invMass2 * PY; + b2_angularVelocity += invI2 * (r2X * PY - r2Y * PX); + + ccp.tangentImpulse = newImpulse; + } + + + + // Solver tangent constraints + // MOVED ABOVE FOR EFFICIENCY + /*for (j = 0; j < tCount; ++j) + { + ccp = c.points[ j ]; + + //r1 = b2Math.b2MulMV(b1.m_R, ccp.localAnchor1); + 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 + //r2 = b2Math.b2MulMV(b2.m_R, ccp.localAnchor2); + 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 + + // Relative velocity at contact + //var dv = b2.m_linearVelocity + b2Cross(b2.m_angularVelocity, r2) - b1.m_linearVelocity - b2Cross(b1.m_angularVelocity, r1); + //dv = b2Math.SubtractVV(b2Math.SubtractVV(b2Math.AddVV(b2.m_linearVelocity, b2Math.b2CrossFV(b2.m_angularVelocity, r2)), b1.m_linearVelocity), b2Math.b2CrossFV(b1.m_angularVelocity, r1)); + 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); + + // Compute tangent impulse + var vt = dvX*tangentX + dvY*tangentY; + lambda = ccp.tangentMass * (-vt); + + // b2Clamp the accumulated impulse + var maxFriction = c.friction * ccp.normalImpulse; + newImpulse = b2Math.b2Clamp(ccp.tangentImpulse + lambda, -maxFriction, maxFriction); + lambda = newImpulse - ccp.tangentImpulse; + + // Apply contact impulse + //P = b2Math.MulFV(lambda, tangent); + PX = lambda * tangentX; + PY = lambda * tangentY; + + //b1.m_linearVelocity.Subtract( b2Math.MulFV( invMass1, P ) ); + b1_linearVelocity.x -= invMass1 * PX; + b1_linearVelocity.y -= invMass1 * PY; + b1_angularVelocity -= invI1 * (r1X * PY - r1Y * PX); + + //b2.m_linearVelocity.Add( b2Math.MulFV( invMass2, P ) ); + b2_linearVelocity.x += invMass2 * PX; + b2_linearVelocity.y += invMass2 * PY; + b2_angularVelocity += invI2 * (r2X * PY - r2Y * PX); + + ccp.tangentImpulse = newImpulse; + }*/ + + // Update angular velocity + 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 normal = new b2Vec2(c.normal.x, c.normal.y); + var normalX = c.normal.x; + var normalY = c.normal.y; + //var tangent = b2Math.b2CrossVF(normal, 1.0); + var tangentX = normalY; + var tangentY = -normalX; + + // Solver normal constraints + var tCount = c.pointCount; + for (var j = 0; j < tCount; ++j) + { + var ccp = c.points[ j ]; + + //r1 = b2Math.b2MulMV(b1.m_R, ccp.localAnchor1); + 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 + //r2 = b2Math.b2MulMV(b2.m_R, ccp.localAnchor2); + 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 p1 = b2Math.AddVV(b1.m_position, r1); + var p1X = b1_position.x + r1X; + var p1Y = b1_position.y + r1Y; + + //var p2 = b2Math.AddVV(b2.m_position, r2); + var p2X = b2_position.x + r2X; + var p2Y = b2_position.y + r2Y; + + //var dp = b2Math.SubtractVV(p2, p1); + var dpX = p2X - p1X; + var dpY = p2Y - p1Y; + + // Approximate the current separation. + //var separation = b2Math.b2Dot(dp, normal) + ccp.separation; + var separation = (dpX*normalX + dpY*normalY) + ccp.separation; + + // Track max constraint error. + minSeparation = b2Math.b2Min(minSeparation, separation); + + // Prevent large corrections and allow slop. + var C = beta * b2Math.b2Clamp(separation + b2Settings.b2_linearSlop, -b2Settings.b2_maxLinearCorrection, 0.0); + + // Compute normal impulse + var dImpulse = -ccp.normalMass * C; + + // b2Clamp the accumulated impulse + var impulse0 = ccp.positionImpulse; + ccp.positionImpulse = b2Math.b2Max(impulse0 + dImpulse, 0.0); + dImpulse = ccp.positionImpulse - impulse0; + + //var impulse = b2Math.MulFV( dImpulse, normal ); + var impulseX = dImpulse * normalX; + var impulseY = dImpulse * normalY; + + //b1.m_position.Subtract( b2Math.MulFV( invMass1, impulse ) ); + b1_position.x -= invMass1 * impulseX; + b1_position.y -= invMass1 * impulseY; + b1_rotation -= invI1 * (r1X * impulseY - r1Y * impulseX); + b1.m_R.Set(b1_rotation); + + //b2.m_position.Add( b2Math.MulFV( invMass2, impulse ) ); + b2_position.x += invMass2 * impulseX; + b2_position.y += invMass2 * impulseY; + b2_rotation += invI2 * (r2X * impulseY - r2Y * impulseX); + b2.m_R.Set(b2_rotation); + } + // Update body rotations + 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}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2NullContact.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2NullContact.js new file mode 100644 index 0000000..faa2bb4 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2NullContact.js @@ -0,0 +1,65 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2NullContact = Class.create(); +Object.extend(b2NullContact.prototype, b2Contact.prototype); +Object.extend(b2NullContact.prototype, +{ + initialize: function(s1, s2) { + // The constructor for b2Contact + // initialize instance variables for references + 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; }}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyAndCircleContact.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyAndCircleContact.js new file mode 100644 index 0000000..1f0a7e3 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyAndCircleContact.js @@ -0,0 +1,103 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2PolyAndCircleContact = Class.create(); +Object.extend(b2PolyAndCircleContact.prototype, b2Contact.prototype); +Object.extend(b2PolyAndCircleContact.prototype, { + + + initialize: function(s1, s2) { + // The constructor for b2Contact + // initialize instance variables for references + 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; + // + + // initialize instance variables for references + this.m_manifold = [new b2Manifold()]; + // + + //super(shape1, shape2); + + 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; + }, + //~b2PolyAndCircleContact() {} + + 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){ + // + }; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyContact.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyContact.js new file mode 100644 index 0000000..e11bf40 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/contacts/b2PolyContact.js @@ -0,0 +1,163 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2PolyContact = Class.create(); +Object.extend(b2PolyContact.prototype, b2Contact.prototype); +Object.extend(b2PolyContact.prototype, +{ + + initialize: function(s1, s2) { + // The constructor for b2Contact + // initialize instance variables for references + 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; + // + + // initialize instance variables for references + this.m0 = new b2Manifold(); + this.m_manifold = [new b2Manifold()]; + // + + //super(shape1, shape2); + //b2Settings.b2Assert(this.m_shape1.m_type == b2Shape.e_polyShape); + //b2Settings.b2Assert(this.m_shape2.m_type == b2Shape.e_polyShape); + this.m_manifold[0].pointCount = 0; + }, + //~b2PolyContact() {} + + // store temp manifold to reduce calls to new + m0: new b2Manifold(), + + Evaluate: function(){ + var tMani = this.m_manifold[0]; + // replace memcpy + // memcpy(&this.m0, &this.m_manifold, sizeof(b2Manifold)); + //this.m0.points = new Array(tMani.pointCount); + var tPoints = this.m0.points; + + for (var k = 0; k < tMani.pointCount; k++){ + var tPoint = tPoints[k]; + var tPoint0 = tMani.points[k]; + //tPoint.separation = tPoint0.separation; + tPoint.normalImpulse = tPoint0.normalImpulse; + tPoint.tangentImpulse = tPoint0.tangentImpulse; + //tPoint.position.SetV( tPoint0.position ); + + tPoint.id = tPoint0.id.Copy(); + + /*this.m0.points[k].id.features = new Features(); + this.m0.points[k].id.features.referenceFace = this.m_manifold[0].points[k].id.features.referenceFace; + this.m0.points[k].id.features.incidentEdge = this.m_manifold[0].points[k].id.features.incidentEdge; + this.m0.points[k].id.features.incidentVertex = this.m_manifold[0].points[k].id.features.incidentVertex; + this.m0.points[k].id.features.flip = this.m_manifold[0].points[k].id.features.flip;*/ + } + //this.m0.normal.SetV( tMani.normal ); + this.m0.pointCount = tMani.pointCount; + + b2Collision.b2CollidePoly(tMani, this.m_shape1, this.m_shape2, false); + + // Match contact ids to facilitate warm starting. + if (tMani.pointCount > 0) + { + var match = [false, false]; + + // Match old contact ids to new contact ids and copy the + // stored impulses to warm start the solver. + 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){ + //void* mem = allocator->Allocate(sizeof(b2PolyContact)); + return new b2PolyContact(shape1, shape2); + }; +b2PolyContact.Destroy = function(contact, allocator){ + //((b2PolyContact*)contact)->~b2PolyContact(); + //allocator->Free(contact, sizeof(b2PolyContact)); + }; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJoint.js new file mode 100644 index 0000000..b91e4779 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJoint.js @@ -0,0 +1,264 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + +// C = norm(p2 - p1) - L +// u = (p2 - p1) / norm(p2 - p1) +// Cdot = dot(u, v2 + cross(w2, r2) - v1 - cross(w1, r1)) +// J = [-u -cross(r1, u) u cross(r2, u)] +// K = J * invM * JT +// = invMass1 + invI1 * cross(r1, u)^2 + invMass2 + invI2 * cross(r2, u)^2 + +var b2DistanceJoint = Class.create(); +Object.extend(b2DistanceJoint.prototype, b2Joint.prototype); +Object.extend(b2DistanceJoint.prototype, +{ + //--------------- Internals Below ------------------- + + initialize: function(def){ + // The constructor for b2Joint + // initialize instance variables for references + 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; + // + + // initialize instance variables for references + this.m_localAnchor1 = new b2Vec2(); + this.m_localAnchor2 = new b2Vec2(); + this.m_u = new b2Vec2(); + // + + //super(def); + + var tMat; + var tX; + var tY; + //this.m_localAnchor1 = b2MulT(this.m_body1->m_R, def->anchorPoint1 - this.m_body1->m_position); + 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; + //this.m_localAnchor2 = b2MulT(this.m_body2->m_R, def->anchorPoint2 - this.m_body2->m_position); + 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; + + //b2Vec2 d = def->anchorPoint2 - def->anchorPoint1; + tX = def.anchorPoint2.x - def.anchorPoint1.x; + tY = def.anchorPoint2.y - def.anchorPoint1.y; + //this.m_length = d.Length(); + this.m_length = Math.sqrt(tX*tX + tY*tY); + this.m_impulse = 0.0; + }, + + PrepareVelocitySolver: function(){ + + var tMat; + + // Compute the effective mass matrix. + //b2Vec2 r1 = b2Mul(this.m_body1->m_R, this.m_localAnchor1); + 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; + //b2Vec2 r2 = b2Mul(this.m_body2->m_R, this.m_localAnchor2); + 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 = this.m_body2->m_position + r2 - this.m_body1->m_position - r1; + 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; + + // Handle singularity. + //float32 length = this.m_u.Length(); + 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 *= 1.0 / length; + this.m_u.Multiply( 1.0 / length ); + } + else + { + this.m_u.SetZero(); + } + + //float32 cr1u = b2Cross(r1, this.m_u); + var cr1u = (r1X * this.m_u.y - r1Y * this.m_u.x); + //float32 cr2u = b2Cross(r2, this.m_u); + 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 = this.m_body1.m_invMass + this.m_body1.m_invI * cr1u * cr1u + this.m_body2.m_invMass + this.m_body2.m_invI * cr2u * cr2u; + //b2Settings.b2Assert(this.m_mass > Number.MIN_VALUE); + this.m_mass = 1.0 / this.m_mass; + + if (b2World.s_enableWarmStarting) + { + //b2Vec2 P = this.m_impulse * this.m_u; + var PX = this.m_impulse * this.m_u.x; + var PY = this.m_impulse * this.m_u.y; + //this.m_body1.m_linearVelocity -= this.m_body1.m_invMass * P; + 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 * b2Cross(r1, P); + this.m_body1.m_angularVelocity -= this.m_body1.m_invI * (r1X * PY - r1Y * PX); + //this.m_body2.m_linearVelocity += this.m_body2.m_invMass * P; + 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 * b2Cross(r2, P); + 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; + + //b2Vec2 r1 = b2Mul(this.m_body1->m_R, this.m_localAnchor1); + 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; + //b2Vec2 r2 = b2Mul(this.m_body2->m_R, this.m_localAnchor2); + 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; + + // Cdot = dot(u, v + cross(w, r)) + //b2Vec2 v1 = this.m_body1->m_linearVelocity + b2Cross(this.m_body1->m_angularVelocity, r1); + 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); + //b2Vec2 v2 = this.m_body2->m_linearVelocity + b2Cross(this.m_body2->m_angularVelocity, r2); + 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); + //float32 Cdot = b2Dot(this.m_u, v2 - v1); + var Cdot = (this.m_u.x * (v2X - v1X) + this.m_u.y * (v2Y - v1Y)); + //float32 impulse = -this.m_mass * Cdot; + var impulse = -this.m_mass * Cdot; + this.m_impulse += impulse; + + //b2Vec2 P = impulse * this.m_u; + var PX = impulse * this.m_u.x; + var PY = impulse * this.m_u.y; + //this.m_body1->m_linearVelocity -= this.m_body1->m_invMass * P; + 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 * b2Cross(r1, P); + this.m_body1.m_angularVelocity -= this.m_body1.m_invI * (r1X * PY - r1Y * PX); + //this.m_body2->m_linearVelocity += this.m_body2->m_invMass * P; + 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 * b2Cross(r2, P); + this.m_body2.m_angularVelocity += this.m_body2.m_invI * (r2X * PY - r2Y * PX); + }, + + SolvePositionConstraints: function(){ + + var tMat; + + //b2Vec2 r1 = b2Mul(this.m_body1->m_R, this.m_localAnchor1); + 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; + //b2Vec2 r2 = b2Mul(this.m_body2->m_R, this.m_localAnchor2); + 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; + //b2Vec2 d = this.m_body2->m_position + r2 - this.m_body1->m_position - r1; + 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; + + //float32 length = d.Normalize(); + var length = Math.sqrt(dX*dX + dY*dY); + dX /= length; + dY /= length; + //float32 C = length - this.m_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 = d; + this.m_u.Set(dX, dY); + //b2Vec2 P = impulse * this.m_u; + var PX = impulse * this.m_u.x; + var PY = impulse * this.m_u.y; + + //this.m_body1->m_position -= this.m_body1->m_invMass * P; + 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 * b2Cross(r1, P); + this.m_body1.m_rotation -= this.m_body1.m_invI * (r1X * PY - r1Y * PX); + //this.m_body2->m_position += this.m_body2->m_invMass * P; + 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 * b2Cross(r2, P); + 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 = (this.m_impulse * invTimeStep) * this.m_u; + var F = new b2Vec2(); + F.SetV(this.m_u); + F.Multiply(this.m_impulse * invTimeStep); + return F; + }, + + GetReactionTorque: function(invTimeStep) + { + //NOT_USED(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}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJointDef.js new file mode 100644 index 0000000..0c89b7a --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2DistanceJointDef.js @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2DistanceJointDef = Class.create(); +Object.extend(b2DistanceJointDef.prototype, b2JointDef.prototype); +Object.extend(b2DistanceJointDef.prototype, +{ + initialize: function() + { + // The constructor for b2JointDef + this.type = b2Joint.e_unknownJoint; + this.userData = null; + this.body1 = null; + this.body2 = null; + this.collideConnected = false; + // + + // initialize instance variables for references + this.anchorPoint1 = new b2Vec2(); + this.anchorPoint2 = new b2Vec2(); + // + + this.type = b2Joint.e_distanceJoint; + //this.anchorPoint1.Set(0.0, 0.0); + //this.anchorPoint2.Set(0.0, 0.0); + }, + + anchorPoint1: new b2Vec2(), + anchorPoint2: new b2Vec2()}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJoint.js new file mode 100644 index 0000000..daace0b --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJoint.js @@ -0,0 +1,307 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + +var b2GearJoint = Class.create(); +Object.extend(b2GearJoint.prototype, b2Joint.prototype); +Object.extend(b2GearJoint.prototype, +{ + GetAnchor1: function(){ + //return this.m_body1.m_position + b2MulMV(this.m_body1.m_R, this.m_localAnchor1); + 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(){ + //return this.m_body2->m_position + b2Mul(this.m_body2->m_R, this.m_localAnchor2); + 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){ + //b2Vec2 F(0.0f, 0.0f); + return new b2Vec2(); + }, + GetReactionTorque: function(invTimeStep){ + return 0.0; + }, + + GetRatio: function(){ + return this.m_ratio; + }, + + //--------------- Internals Below ------------------- + + initialize: function(def){ + // The constructor for b2Joint + // initialize instance variables for references + 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; + // + + // initialize instance variables for references + 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(); + // + + // parent constructor + //super(def); + + //b2Settings.b2Assert(def.joint1.m_type == b2Joint.e_revoluteJoint || def.joint1.m_type == b2Joint.e_prismaticJoint); + //b2Settings.b2Assert(def.joint2.m_type == b2Joint.e_revoluteJoint || def.joint2.m_type == b2Joint.e_prismaticJoint); + //b2Settings.b2Assert(def.joint1.m_body1.IsStatic()); + //b2Settings.b2Assert(def.joint2.m_body1.IsStatic()); + + 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; + + // temp vars + 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 + { + //b2Vec2 ug = b2MulMV(g1->m_R, this.m_prismatic1->m_localXAxis1); + 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; + //b2Vec2 r = b2MulMV(b1->m_R, this.m_localAnchor1); + 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; + + //var crug = b2Cross(r, ug); + crug = rX * ugY - rY * ugX; + //this.m_J.linear1 = -ug; + 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 + { + //b2Vec2 ug = b2Mul(g2->m_R, this.m_prismatic2->m_localXAxis1); + 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; + //b2Vec2 r = b2Mul(b2->m_R, this.m_localAnchor2); + 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; + //float32 crug = b2Cross(r, ug); + crug = rX * ugY - rY * ugX; + //this.m_J.linear2 = -this.m_ratio * ug; + 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); + } + + // Compute effective mass. + //b2Settings.b2Assert(K > 0.0); + this.m_mass = 1.0 / K; + + // Warm starting. + //b1.m_linearVelocity += b1.m_invMass * this.m_impulse * this.m_J.linear1; + 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 += b2.m_invMass * this.m_impulse * this.m_J.linear2; + 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, + + // One of these is NULL. + m_revolute1: null, + m_prismatic1: null, + + // One of these is 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, + + // Effective mass + m_mass: null, + + // Impulse for accumulation/warm starting. + m_impulse: null}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJointDef.js new file mode 100644 index 0000000..aaf30a3 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2GearJointDef.js @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + +// A gear joint is used to connect two joints together. Either joint +// can be a revolute or prismatic joint. You specify a gear ratio +// to bind the motions together: +// coordinate1 + ratio * coordinate2 = constant +// The ratio can be negative or positive. If one joint is a revolute joint +// and the other joint is a prismatic joint, then the ratio will have units +// of length or units of 1/length. +// +// RESTRICITON: The revolute and prismatic joints must be attached to +// a fixed body (which must be body1 on those joints). + +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}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Jacobian.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Jacobian.js new file mode 100644 index 0000000..f67a22a --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Jacobian.js @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +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 b2Math.b2Dot(this.linear1, x1) + this.angular1 * a1 + b2Math.b2Dot(this.linear2, x2) + this.angular2 * 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() { + // initialize instance variables for references + this.linear1 = new b2Vec2(); + this.linear2 = new b2Vec2(); + // +}}; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Joint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Joint.js new file mode 100644 index 0000000..88a5766 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2Joint.js @@ -0,0 +1,200 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +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; + }, + + //--------------- Internals Below ------------------- + + + + initialize: function(def){ + // initialize instance variables for references + 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; + }, + //virtual ~b2Joint() {} + + PrepareVelocitySolver: function(){}, + SolveVelocityConstraints: function(step){}, + + // This returns true if the position errors are within tolerance. + 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 + + + // ENUMS + + // enum b2JointType + + // enum b2LimitState + +}; +b2Joint.Create = function(def, allocator){ + var joint = null; + + switch (def.type) + { + case b2Joint.e_distanceJoint: + { + //void* mem = allocator->Allocate(sizeof(b2DistanceJoint)); + joint = new b2DistanceJoint(def); + } + break; + + case b2Joint.e_mouseJoint: + { + //void* mem = allocator->Allocate(sizeof(b2MouseJoint)); + joint = new b2MouseJoint(def); + } + break; + + case b2Joint.e_prismaticJoint: + { + //void* mem = allocator->Allocate(sizeof(b2PrismaticJoint)); + joint = new b2PrismaticJoint(def); + } + break; + + case b2Joint.e_revoluteJoint: + { + //void* mem = allocator->Allocate(sizeof(b2RevoluteJoint)); + joint = new b2RevoluteJoint(def); + } + break; + + case b2Joint.e_pulleyJoint: + { + //void* mem = allocator->Allocate(sizeof(b2PulleyJoint)); + joint = new b2PulleyJoint(def); + } + break; + + case b2Joint.e_gearJoint: + { + //void* mem = allocator->Allocate(sizeof(b2GearJoint)); + joint = new b2GearJoint(def); + } + break; + + default: + //b2Settings.b2Assert(false); + break; + } + + return joint; + }; +b2Joint.Destroy = function(joint, allocator){ + /*joint->~b2Joint(); + switch (joint.m_type) + { + case b2Joint.e_distanceJoint: + allocator->Free(joint, sizeof(b2DistanceJoint)); + break; + + case b2Joint.e_mouseJoint: + allocator->Free(joint, sizeof(b2MouseJoint)); + break; + + case b2Joint.e_prismaticJoint: + allocator->Free(joint, sizeof(b2PrismaticJoint)); + break; + + case b2Joint.e_revoluteJoint: + allocator->Free(joint, sizeof(b2RevoluteJoint)); + break; + + case b2Joint.e_pulleyJoint: + allocator->Free(joint, sizeof(b2PulleyJoint)); + break; + + case b2Joint.e_gearJoint: + allocator->Free(joint, sizeof(b2GearJoint)); + break; + + default: + b2Assert(false); + break; + }*/ + }; +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; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointDef.js new file mode 100644 index 0000000..6b949e6 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointDef.js @@ -0,0 +1,40 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +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} diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointNode.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointNode.js new file mode 100644 index 0000000..b1c837b --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2JointNode.js @@ -0,0 +1,33 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2JointNode = Class.create(); +b2JointNode.prototype = +{ + + other: null, + joint: null, + prev: null, + next: null, + + + initialize: function() {}} diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJoint.js new file mode 100644 index 0000000..cf933a9 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJoint.js @@ -0,0 +1,234 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +// p = attached point, m = mouse point +// C = p - m +// Cdot = v +// = v + cross(w, r) +// J = [I r_skew] +// Identity used: +// w k % (rx i + ry j) = w * (-ry i + rx j) + +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) + { + //b2Vec2 F = invTimeStep * this.m_impulse; + var F = new b2Vec2(); + F.SetV(this.m_impulse); + F.Multiply(invTimeStep); + return F; + }, + + GetReactionTorque: function(invTimeStep) + { + //NOT_USED(invTimeStep); + return 0.0; + }, + + SetTarget: function(target){ + this.m_body2.WakeUp(); + this.m_target = target; + }, + + //--------------- Internals Below ------------------- + + initialize: function(def){ + // The constructor for b2Joint + // initialize instance variables for references + 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; + // + + // initialize instance variables for references + 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(); + // + + //super(def); + + this.m_target.SetV(def.target); + //this.m_localAnchor = b2Math.b2MulTMV(this.m_body2.m_R, b2Math.SubtractVV( this.m_target, this.m_body2.m_position ) ); + 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; + + // Frequency + var omega = 2.0 * b2Settings.b2_pi * def.frequencyHz; + + // Damping coefficient + var d = 2.0 * mass * def.dampingRatio * omega; + + // Spring stiffness + var k = mass * omega * omega; + + // magic formulas + this.m_gamma = 1.0 / (d + def.timeStep * k); + this.m_beta = def.timeStep * k / (d + def.timeStep * k); + }, + + // Presolve vars + K: new b2Mat22(), + K1: new b2Mat22(), + K2: new b2Mat22(), + PrepareVelocitySolver: function(){ + var b = this.m_body2; + + var tMat; + + // Compute the effective mass matrix. + //b2Vec2 r = b2Mul(b.m_R, this.m_localAnchor); + 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; + + // this.K = [(1/m1 + 1/m2) * eye(2) - skew(r1) * invI1 * skew(r1) - skew(r2) * invI2 * skew(r2)] + // = [1/m1+1/m2 0 ] + invI1 * [r1.y*r1.y -r1.x*r1.y] + invI2 * [r1.y*r1.y -r1.x*r1.y] + // [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x] + var invMass = b.m_invMass; + var invI = b.m_invI; + + //b2Mat22 this.K1; + this.K1.col1.x = invMass; this.K1.col2.x = 0.0; + this.K1.col1.y = 0.0; this.K1.col2.y = invMass; + + //b2Mat22 this.K2; + 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; + + //b2Mat22 this.K = this.K1 + this.K2; + 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.m_ptpMass = this.K.Invert(); + this.K.Invert(this.m_ptpMass); + + //this.m_C = b.m_position + r - this.m_target; + 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; + + // Cheat with some damping + b.m_angularVelocity *= 0.98; + + // Warm starting. + //b2Vec2 P = this.m_impulse; + var PX = this.m_impulse.x; + var PY = this.m_impulse.y; + //b.m_linearVelocity += invMass * P; + b.m_linearVelocity.x += invMass * PX; + b.m_linearVelocity.y += invMass * PY; + //b.m_angularVelocity += invI * b2Cross(r, P); + b.m_angularVelocity += invI * (rX * PY - rY * PX); + }, + + + SolveVelocityConstraints: function(step){ + var body = this.m_body2; + + var tMat; + + // Compute the effective mass matrix. + //b2Vec2 r = b2Mul(body.m_R, this.m_localAnchor); + 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; + + // Cdot = v + cross(w, r) + //b2Vec2 Cdot = body->m_linearVelocity + b2Cross(body->m_angularVelocity, r); + var CdotX = body.m_linearVelocity.x + (-body.m_angularVelocity * rY); + var CdotY = body.m_linearVelocity.y + (body.m_angularVelocity * rX); + //b2Vec2 impulse = -b2Mul(this.m_ptpMass, Cdot + (this.m_beta * step->inv_dt) * this.m_C + this.m_gamma * this.m_impulse); + 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 += impulse; + 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 *= step.dt * this.m_maxForce / length; + this.m_impulse.Multiply(step.dt * this.m_maxForce / length); + } + //impulse = this.m_impulse - oldImpulse; + impulseX = this.m_impulse.x - oldImpulseX; + impulseY = this.m_impulse.y - oldImpulseY; + + //body.m_linearVelocity += body->m_invMass * impulse; + body.m_linearVelocity.x += body.m_invMass * impulseX; + body.m_linearVelocity.y += body.m_invMass * impulseY; + //body.m_angularVelocity += body->m_invI * b2Cross(r, impulse); + 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}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJointDef.js new file mode 100644 index 0000000..b9a3b5c --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2MouseJointDef.js @@ -0,0 +1,53 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2MouseJointDef = Class.create(); +Object.extend(b2MouseJointDef.prototype, b2JointDef.prototype); +Object.extend(b2MouseJointDef.prototype, +{ + initialize: function() + { + // The constructor for b2JointDef + this.type = b2Joint.e_unknownJoint; + this.userData = null; + this.body1 = null; + this.body2 = null; + this.collideConnected = false; + // + + // initialize instance variables for references + 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}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJoint.js new file mode 100644 index 0000000..94bfcf8 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJoint.js @@ -0,0 +1,676 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +// Linear constraint (point-to-line) +// d = p2 - p1 = x2 + r2 - x1 - r1 +// C = dot(ay1, d) +// Cdot = dot(d, cross(w1, ay1)) + dot(ay1, v2 + cross(w2, r2) - v1 - cross(w1, r1)) +// = -dot(ay1, v1) - dot(cross(d + r1, ay1), w1) + dot(ay1, v2) + dot(cross(r2, ay1), v2) +// J = [-ay1 -cross(d+r1,ay1) ay1 cross(r2,ay1)] +// +// Angular constraint +// C = a2 - a1 + a_initial +// Cdot = w2 - w1 +// J = [0 0 -1 0 0 1] + +// Motor/Limit linear constraint +// C = dot(ax1, d) +// Cdot = = -dot(ax1, v1) - dot(cross(d + r1, ax1), w1) + dot(ax1, v2) + dot(cross(r2, ax1), v2) +// J = [-ax1 -cross(d+r1,ax1) ax1 cross(r2,ax1)] + + +var b2PrismaticJoint = Class.create(); +Object.extend(b2PrismaticJoint.prototype, b2Joint.prototype); +Object.extend(b2PrismaticJoint.prototype, +{ + GetAnchor1: function(){ + var b1 = this.m_body1; + //return b2Math.AddVV(b1.m_position, b2Math.b2MulMV(b1.m_R, this.m_localAnchor1)); + 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; + //return b2Math.AddVV(b2.m_position, b2Math.b2MulMV(b2.m_R, this.m_localAnchor2)); + 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; + + //var r1 = b2Math.b2MulMV(b1.m_R, this.m_localAnchor1); + 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; + //var r2 = b2Math.b2MulMV(b2.m_R, this.m_localAnchor2); + 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 p1 = b2Math.AddVV(b1.m_position , r1); + var p1X = b1.m_position.x + r1X; + var p1Y = b1.m_position.y + r1Y; + //var p2 = b2Math.AddVV(b2.m_position , r2); + var p2X = b2.m_position.x + r2X; + var p2Y = b2.m_position.y + r2Y; + //var d = b2Math.SubtractVV(p2, p1); + var dX = p2X - p1X; + var dY = p2Y - p1Y; + //var ax1 = b2Math.b2MulMV(b1.m_R, this.m_localXAxis1); + 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 = b2Math.b2Dot(ax1, d); + var translation = ax1X*dX + ax1Y*dY; + return translation; + }, + GetJointSpeed: function(){ + var b1 = this.m_body1; + var b2 = this.m_body2; + + var tMat; + + //var r1 = b2Math.b2MulMV(b1.m_R, this.m_localAnchor1); + 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; + //var r2 = b2Math.b2MulMV(b2.m_R, this.m_localAnchor2); + 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 p1 = b2Math.AddVV(b1.m_position , r1); + var p1X = b1.m_position.x + r1X; + var p1Y = b1.m_position.y + r1Y; + //var p2 = b2Math.AddVV(b2.m_position , r2); + var p2X = b2.m_position.x + r2X; + var p2Y = b2.m_position.y + r2Y; + //var d = b2Math.SubtractVV(p2, p1); + var dX = p2X - p1X; + var dY = p2Y - p1Y; + //var ax1 = b2Math.b2MulMV(b1.m_R, this.m_localXAxis1); + 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 = b2Math.b2Dot(d, b2Math.b2CrossFV(w1, ax1)) + b2Math.b2Dot(ax1, b2Math.SubtractVV( b2Math.SubtractVV( b2Math.AddVV( v2 , b2Math.b2CrossFV(w2, r2)) , v1) , b2Math.b2CrossFV(w1, r1))); + //var b2D = (dX*(-w1 * ax1Y) + dY*(w1 * ax1X)); + //var b2D2 = (ax1X * ((( v2.x + (-w2 * r2Y)) - v1.x) - (-w1 * r1Y)) + ax1Y * ((( v2.y + (w2 * r2X)) - v1.y) - (w1 * r1X))); + 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; + + //var ax1 = b2Math.b2MulMV(this.m_body1.m_R, this.m_localXAxis1); + 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 ay1 = b2Math.b2MulMV(this.m_body1.m_R, this.m_localYAxis1); + 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 (invTimeStep * this.m_limitImpulse) * ax1 + (invTimeStep * this.m_linearImpulse) * ay1; + + return new b2Vec2(ax1X+ay1X, ax1Y+ay1Y); + }, + + GetReactionTorque: function(invTimeStep) + { + return invTimeStep * this.m_angularImpulse; + }, + + + //--------------- Internals Below ------------------- + + initialize: function(def){ + // The constructor for b2Joint + // initialize instance variables for references + 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; + // + + // initialize instance variables for references + 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(); + // + + //super(def); + + var tMat; + var tX; + var tY; + + //this.m_localAnchor1 = b2Math.b2MulTMV(this.m_body1.m_R, b2Math.SubtractVV(def.anchorPoint , this.m_body1.m_position)); + 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)); + + //this.m_localAnchor2 = b2Math.b2MulTMV(this.m_body2.m_R, b2Math.SubtractVV(def.anchorPoint , this.m_body2.m_position)); + 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)); + + //this.m_localXAxis1 = b2Math.b2MulTMV(this.m_body1.m_R, def.axis); + 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 = b2Math.b2CrossFV(1.0, this.m_localXAxis1); + 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; + + // Compute the effective masses. + //b2Vec2 r1 = b2Mul(b1->m_R, this.m_localAnchor1); + 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; + //b2Vec2 r2 = b2Mul(b2->m_R, this.m_localAnchor2); + 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; + + //float32 invMass1 = b1->m_invMass, invMass2 = b2->m_invMass; + var invMass1 = b1.m_invMass; + var invMass2 = b2.m_invMass; + //float32 invI1 = b1->m_invI, invI2 = b2->m_invI; + var invI1 = b1.m_invI; + var invI2 = b2.m_invI; + + // Compute point to line constraint effective mass. + // J = [-ay1 -cross(d+r1,ay1) ay1 cross(r2,ay1)] + //b2Vec2 ay1 = b2Mul(b1->m_R, this.m_localYAxis1); + 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; + //b2Vec2 e = b2->m_position + r2 - b1->m_position; + 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.Set(-ay1, -b2Math.b2Cross(e, ay1), ay1, b2Math.b2Cross(r2, ay1)); + 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; + //b2Settings.b2Assert(this.m_linearMass > Number.MIN_VALUE); + this.m_linearMass = 1.0 / this.m_linearMass; + + // Compute angular constraint effective mass. + this.m_angularMass = 1.0 / (invI1 + invI2); + + // Compute motor and limit terms. + if (this.m_enableLimit || this.m_enableMotor) + { + // The motor and limit share a Jacobian and effective mass. + //b2Vec2 ax1 = b2Mul(b1->m_R, this.m_localXAxis1); + 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.Set(-ax1, -b2Cross(e, ax1), ax1, b2Cross(r2, ax1)); + 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; + //b2Settings.b2Assert(this.m_motorMass > Number.MIN_VALUE); + this.m_motorMass = 1.0 / this.m_motorMass; + + if (this.m_enableLimit) + { + //b2Vec2 d = e - r1; + var dX = eX - r1X; + var dY = eY - r1Y; + //float32 jointTranslation = b2Dot(ax1, d); + 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) + { + //b2Vec2 P1 = this.m_linearImpulse * this.m_linearJacobian.linear1 + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.linear1; + 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; + //b2Vec2 P2 = this.m_linearImpulse * this.m_linearJacobian.linear2 + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.linear2; + 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; + //float32 L1 = this.m_linearImpulse * this.m_linearJacobian.angular1 - this.m_angularImpulse + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.angular1; + var L1 = this.m_linearImpulse * this.m_linearJacobian.angular1 - this.m_angularImpulse + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.angular1; + //float32 L2 = this.m_linearImpulse * this.m_linearJacobian.angular2 + this.m_angularImpulse + (this.m_motorImpulse + this.m_limitImpulse) * this.m_motorJacobian.angular2; + 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 += invMass1 * P1; + b1.m_linearVelocity.x += invMass1 * P1X; + b1.m_linearVelocity.y += invMass1 * P1Y; + //b1->m_angularVelocity += invI1 * L1; + b1.m_angularVelocity += invI1 * L1; + + //b2->m_linearVelocity += invMass2 * P2; + b2.m_linearVelocity.x += invMass2 * P2X; + b2.m_linearVelocity.y += invMass2 * P2Y; + //b2->m_angularVelocity += invI2 * L2; + 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; + + // Solve linear constraint. + 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 += (invMass1 * linearImpulse) * this.m_linearJacobian.linear1; + 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; + b1.m_angularVelocity += invI1 * linearImpulse * this.m_linearJacobian.angular1; + + //b2->m_linearVelocity += (invMass2 * linearImpulse) * this.m_linearJacobian.linear2; + 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; + b2.m_angularVelocity += invI2 * linearImpulse * this.m_linearJacobian.angular2; + + // Solve angular constraint. + 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; + + // Solve linear motor constraint. + 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 += (invMass1 * motorImpulse) * this.m_motorJacobian.linear1; + 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; + b1.m_angularVelocity += invI1 * motorImpulse * this.m_motorJacobian.angular1; + + //b2->m_linearVelocity += (invMass2 * motorImpulse) * this.m_motorJacobian.linear2; + 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; + b2.m_angularVelocity += invI2 * motorImpulse * this.m_motorJacobian.angular2; + } + + // Solve linear limit constraint. + 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 += (invMass1 * limitImpulse) * this.m_motorJacobian.linear1; + 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; + b1.m_angularVelocity += invI1 * limitImpulse * this.m_motorJacobian.angular1; + + //b2->m_linearVelocity += (invMass2 * limitImpulse) * this.m_motorJacobian.linear2; + 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; + 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; + + //b2Vec2 r1 = b2Mul(b1->m_R, this.m_localAnchor1); + 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; + //b2Vec2 r2 = b2Mul(b2->m_R, this.m_localAnchor2); + 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; + //b2Vec2 p1 = b1->m_position + r1; + var p1X = b1.m_position.x + r1X; + var p1Y = b1.m_position.y + r1Y; + //b2Vec2 p2 = b2->m_position + r2; + var p2X = b2.m_position.x + r2X; + var p2Y = b2.m_position.y + r2Y; + //b2Vec2 d = p2 - p1; + var dX = p2X - p1X; + var dY = p2Y - p1Y; + //b2Vec2 ay1 = b2Mul(b1->m_R, this.m_localYAxis1); + 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; + + // Solve linear (point-to-line) constraint. + //float32 linearC = b2Dot(ay1, d); + var linearC = ay1X*dX + ay1Y*dY; + // Prevent overly large corrections. + linearC = b2Math.b2Clamp(linearC, -b2Settings.b2_maxLinearCorrection, b2Settings.b2_maxLinearCorrection); + var linearImpulse = -this.m_linearMass * linearC; + + //b1->m_position += (invMass1 * linearImpulse) * this.m_linearJacobian.linear1; + 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; + b1.m_rotation += invI1 * linearImpulse * this.m_linearJacobian.angular1; + //b1->m_R.Set(b1->m_rotation); + //b2->m_position += (invMass2 * linearImpulse) * this.m_linearJacobian.linear2; + 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; + //b2->m_R.Set(b2->m_rotation); + + var positionError = b2Math.b2Abs(linearC); + + // Solve angular constraint. + var angularC = b2.m_rotation - b1.m_rotation - this.m_initialAngle; + // Prevent overly large corrections. + 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); + + // Solve linear limit constraint. + if (this.m_enableLimit && this.m_limitState != b2Joint.e_inactiveLimit) + { + + //b2Vec2 r1 = b2Mul(b1->m_R, this.m_localAnchor1); + 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; + //b2Vec2 r2 = b2Mul(b2->m_R, this.m_localAnchor2); + 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; + //b2Vec2 p1 = b1->m_position + r1; + p1X = b1.m_position.x + r1X; + p1Y = b1.m_position.y + r1Y; + //b2Vec2 p2 = b2->m_position + r2; + p2X = b2.m_position.x + r2X; + p2Y = b2.m_position.y + r2Y; + //b2Vec2 d = p2 - p1; + dX = p2X - p1X; + dY = p2Y - p1Y; + //b2Vec2 ax1 = b2Mul(b1->m_R, this.m_localXAxis1); + 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; + + //float32 translation = b2Dot(ax1, d); + var translation = (ax1X*dX + ax1Y*dY); + var limitImpulse = 0.0; + + if (this.m_limitState == b2Joint.e_equalLimits) + { + // Prevent large angular corrections + 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); + + // Prevent large linear corrections and allow some slop. + 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); + + // Prevent large linear corrections and allow some slop. + 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 += (invMass1 * limitImpulse) * this.m_motorJacobian.linear1; + 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_rotation += invI1 * limitImpulse * this.m_motorJacobian.angular1; + b1.m_R.Set(b1.m_rotation); + //b2->m_position += (invMass2 * limitImpulse) * this.m_motorJacobian.linear2; + 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_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}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJointDef.js new file mode 100644 index 0000000..42d5743 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PrismaticJointDef.js @@ -0,0 +1,56 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +var b2PrismaticJointDef = Class.create(); +Object.extend(b2PrismaticJointDef.prototype, b2JointDef.prototype); +Object.extend(b2PrismaticJointDef.prototype, +{ + initialize: function() + { + // The constructor for b2JointDef + 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}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJoint.js new file mode 100644 index 0000000..8b8506d --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJoint.js @@ -0,0 +1,618 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + + +var b2PulleyJoint = Class.create(); +Object.extend(b2PulleyJoint.prototype, b2Joint.prototype); +Object.extend(b2PulleyJoint.prototype, +{ + GetAnchor1: function(){ + //return this.m_body1->m_position + b2Mul(this.m_body1->m_R, this.m_localAnchor1); + 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(){ + //return this.m_body2->m_position + b2Mul(this.m_body2->m_R, this.m_localAnchor2); + 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 this.m_ground->m_position + this.m_groundAnchor1; + 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){ + //b2Vec2 F(0.0f, 0.0f); + return new b2Vec2(); + }, + GetReactionTorque: function(invTimeStep){ + return 0.0; + }, + + GetLength1: function(){ + var tMat; + //b2Vec2 p = this.m_body1->m_position + b2Mul(this.m_body1->m_R, this.m_localAnchor1); + 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); + //b2Vec2 s = this.m_ground->m_position + this.m_groundAnchor1; + //b2Vec2 d = p - s; + 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; + //b2Vec2 p = this.m_body2->m_position + b2Mul(this.m_body2->m_R, this.m_localAnchor2); + 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); + //b2Vec2 s = this.m_ground->m_position + this.m_groundAnchor2; + //b2Vec2 d = p - s; + 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; + }, + + //--------------- Internals Below ------------------- + + initialize: function(def){ + // The constructor for b2Joint + // initialize instance variables for references + 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; + // + + // initialize instance variables for references + 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(); + // + + + // parent + //super(def); + + var tMat; + var tX; + var tY; + + this.m_ground = this.m_body1.m_world.m_groundBody; + //this.m_groundAnchor1 = def.groundPoint1 - this.m_ground.m_position; + 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 = def.groundPoint2 - this.m_ground.m_position; + 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; + //this.m_localAnchor1 = b2MulT(this.m_body1.m_R, def.anchorPoint1 - this.m_body1.m_position); + 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; + //this.m_localAnchor2 = b2MulT(this.m_body2.m_R, def.anchorPoint2 - this.m_body2.m_position); + 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; + + //var d1 = def.groundPoint1 - def.anchorPoint1; + tX = def.groundPoint1.x - def.anchorPoint1.x; + tY = def.groundPoint1.y - def.anchorPoint1.y; + var d1Len = Math.sqrt(tX*tX + tY*tY); + //var d2 = def.groundPoint2 - def.anchorPoint2; + 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; + + //b2Vec2 r1 = b2Mul(b1->m_R, this.m_localAnchor1); + 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; + //b2Vec2 r2 = b2Mul(b2->m_R, this.m_localAnchor2); + 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; + + //b2Vec2 p1 = b1->m_position + r1; + var p1X = b1.m_position.x + r1X; + var p1Y = b1.m_position.y + r1Y; + //b2Vec2 p2 = b2->m_position + r2; + var p2X = b2.m_position.x + r2X; + var p2Y = b2.m_position.y + r2Y; + + //b2Vec2 s1 = this.m_ground->m_position + this.m_groundAnchor1; + var s1X = this.m_ground.m_position.x + this.m_groundAnchor1.x; + var s1Y = this.m_ground.m_position.y + this.m_groundAnchor1.y; + //b2Vec2 s2 = this.m_ground->m_position + this.m_groundAnchor2; + var s2X = this.m_ground.m_position.x + this.m_groundAnchor2.x; + var s2Y = this.m_ground.m_position.y + this.m_groundAnchor2.y; + + // Get the pulley axes. + //this.m_u1 = p1 - s1; + this.m_u1.Set(p1X - s1X, p1Y - s1Y); + //this.m_u2 = p2 - s2; + 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 *= 1.0f / length1; + this.m_u1.Multiply(1.0 / length1); + } + else + { + this.m_u1.SetZero(); + } + + if (length2 > b2Settings.b2_linearSlop) + { + //this.m_u2 *= 1.0f / length2; + 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; + } + + // Compute effective mass. + //var cr1u1 = b2Cross(r1, this.m_u1); + var cr1u1 = r1X * this.m_u1.y - r1Y * this.m_u1.x; + //var cr2u2 = b2Cross(r2, this.m_u2); + 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; + //b2Settings.b2Assert(this.m_limitMass1 > Number.MIN_VALUE); + //b2Settings.b2Assert(this.m_limitMass2 > Number.MIN_VALUE); + //b2Settings.b2Assert(this.m_pulleyMass > Number.MIN_VALUE); + this.m_limitMass1 = 1.0 / this.m_limitMass1; + this.m_limitMass2 = 1.0 / this.m_limitMass2; + this.m_pulleyMass = 1.0 / this.m_pulleyMass; + + // Warm starting. + //b2Vec2 P1 = (-this.m_pulleyImpulse - this.m_limitImpulse1) * this.m_u1; + var P1X = (-this.m_pulleyImpulse - this.m_limitImpulse1) * this.m_u1.x; + var P1Y = (-this.m_pulleyImpulse - this.m_limitImpulse1) * this.m_u1.y; + //b2Vec2 P2 = (-this.m_ratio * this.m_pulleyImpulse - this.m_limitImpulse2) * this.m_u2; + 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 += b1.m_invMass * P1; + b1.m_linearVelocity.x += b1.m_invMass * P1X; + b1.m_linearVelocity.y += b1.m_invMass * P1Y; + //b1.m_angularVelocity += b1.m_invI * b2Cross(r1, P1); + b1.m_angularVelocity += b1.m_invI * (r1X * P1Y - r1Y * P1X); + //b2.m_linearVelocity += b2.m_invMass * P2; + b2.m_linearVelocity.x += b2.m_invMass * P2X; + b2.m_linearVelocity.y += b2.m_invMass * P2Y; + //b2.m_angularVelocity += b2.m_invI * b2Cross(r2, P2); + 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; + + //var r1 = b2Mul(b1.m_R, this.m_localAnchor1); + 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; + //var r2 = b2Mul(b2.m_R, this.m_localAnchor2); + 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; + + // temp vars + var v1X; + var v1Y; + var v2X; + var v2Y; + var P1X; + var P1Y; + var P2X; + var P2Y; + var Cdot; + var impulse; + var oldLimitImpulse; + + //{ + //b2Vec2 v1 = b1->m_linearVelocity + b2Cross(b1->m_angularVelocity, r1); + v1X = b1.m_linearVelocity.x + (-b1.m_angularVelocity * r1Y); + v1Y = b1.m_linearVelocity.y + (b1.m_angularVelocity * r1X); + //b2Vec2 v2 = b2->m_linearVelocity + b2Cross(b2->m_angularVelocity, r2); + v2X = b2.m_linearVelocity.x + (-b2.m_angularVelocity * r2Y); + v2Y = b2.m_linearVelocity.y + (b2.m_angularVelocity * r2X); + + //Cdot = -b2Dot(this.m_u1, v1) - this.m_ratio * b2Dot(this.m_u2, v2); + 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; + + //b2Vec2 P1 = -impulse * this.m_u1; + P1X = -impulse * this.m_u1.x; + P1Y = -impulse * this.m_u1.y; + //b2Vec2 P2 = -this.m_ratio * impulse * this.m_u2; + P2X = -this.m_ratio * impulse * this.m_u2.x; + P2Y = -this.m_ratio * impulse * this.m_u2.y; + //b1.m_linearVelocity += b1.m_invMass * P1; + b1.m_linearVelocity.x += b1.m_invMass * P1X; + b1.m_linearVelocity.y += b1.m_invMass * P1Y; + //b1.m_angularVelocity += b1.m_invI * b2Cross(r1, P1); + b1.m_angularVelocity += b1.m_invI * (r1X * P1Y - r1Y * P1X); + //b2.m_linearVelocity += b2.m_invMass * P2; + b2.m_linearVelocity.x += b2.m_invMass * P2X; + b2.m_linearVelocity.y += b2.m_invMass * P2Y; + //b2.m_angularVelocity += b2.m_invI * b2Cross(r2, P2); + b2.m_angularVelocity += b2.m_invI * (r2X * P2Y - r2Y * P2X); + //} + + if (this.m_limitState1 == b2Joint.e_atUpperLimit) + { + //b2Vec2 v1 = b1->m_linearVelocity + b2Cross(b1->m_angularVelocity, r1); + v1X = b1.m_linearVelocity.x + (-b1.m_angularVelocity * r1Y); + v1Y = b1.m_linearVelocity.y + (b1.m_angularVelocity * r1X); + + //float32 Cdot = -b2Dot(this.m_u1, v1); + 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; + //b2Vec2 P1 = -impulse * this.m_u1; + P1X = -impulse * this.m_u1.x; + P1Y = -impulse * this.m_u1.y; + //b1.m_linearVelocity += b1->m_invMass * P1; + b1.m_linearVelocity.x += b1.m_invMass * P1X; + b1.m_linearVelocity.y += b1.m_invMass * P1Y; + //b1.m_angularVelocity += b1->m_invI * b2Cross(r1, P1); + b1.m_angularVelocity += b1.m_invI * (r1X * P1Y - r1Y * P1X); + } + + if (this.m_limitState2 == b2Joint.e_atUpperLimit) + { + //b2Vec2 v2 = b2->m_linearVelocity + b2Cross(b2->m_angularVelocity, r2); + v2X = b2.m_linearVelocity.x + (-b2.m_angularVelocity * r2Y); + v2Y = b2.m_linearVelocity.y + (b2.m_angularVelocity * r2X); + + //float32 Cdot = -b2Dot(this.m_u2, v2); + 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; + //b2Vec2 P2 = -impulse * this.m_u2; + P2X = -impulse * this.m_u2.x; + P2Y = -impulse * this.m_u2.y; + //b2->m_linearVelocity += b2->m_invMass * P2; + b2.m_linearVelocity.x += b2.m_invMass * P2X; + b2.m_linearVelocity.y += b2.m_invMass * P2Y; + //b2->m_angularVelocity += b2->m_invI * b2Cross(r2, P2); + b2.m_angularVelocity += b2.m_invI * (r2X * P2Y - r2Y * P2X); + } + }, + + + + SolvePositionConstraints: function(){ + var b1 = this.m_body1; + var b2 = this.m_body2; + + var tMat; + + //b2Vec2 s1 = this.m_ground->m_position + this.m_groundAnchor1; + var s1X = this.m_ground.m_position.x + this.m_groundAnchor1.x; + var s1Y = this.m_ground.m_position.y + this.m_groundAnchor1.y; + //b2Vec2 s2 = this.m_ground->m_position + this.m_groundAnchor2; + var s2X = this.m_ground.m_position.x + this.m_groundAnchor2.x; + var s2Y = this.m_ground.m_position.y + this.m_groundAnchor2.y; + + // temp vars + 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; + + { + //var r1 = b2Mul(b1.m_R, this.m_localAnchor1); + 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; + //var r2 = b2Mul(b2.m_R, this.m_localAnchor2); + 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; + + //b2Vec2 p1 = b1->m_position + r1; + p1X = b1.m_position.x + r1X; + p1Y = b1.m_position.y + r1Y; + //b2Vec2 p2 = b2->m_position + r2; + p2X = b2.m_position.x + r2X; + p2Y = b2.m_position.y + r2Y; + + // Get the pulley axes. + //this.m_u1 = p1 - s1; + this.m_u1.Set(p1X - s1X, p1Y - s1Y); + //this.m_u2 = p2 - s2; + 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 *= 1.0f / length1; + this.m_u1.Multiply( 1.0 / length1 ); + } + else + { + this.m_u1.SetZero(); + } + + if (length2 > b2Settings.b2_linearSlop) + { + //this.m_u2 *= 1.0f / length2; + 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) + { + //b2Vec2 r1 = b2Mul(b1->m_R, this.m_localAnchor1); + 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; + //b2Vec2 p1 = b1->m_position + r1; + p1X = b1.m_position.x + r1X; + p1Y = b1.m_position.y + r1Y; + + //this.m_u1 = p1 - s1; + this.m_u1.Set(p1X - s1X, p1Y - s1Y); + + length1 = this.m_u1.Length(); + + if (length1 > b2Settings.b2_linearSlop) + { + //this.m_u1 *= 1.0 / length1; + 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; + + //P1 = -impulse * this.m_u1; + 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 * b2Cross(r1, P1); + b1.m_rotation += b1.m_invI * (r1X * p1Y - r1Y * p1X); + b1.m_R.Set(b1.m_rotation); + } + + if (this.m_limitState2 == b2Joint.e_atUpperLimit) + { + //b2Vec2 r2 = b2Mul(b2->m_R, this.m_localAnchor2); + 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; + //b2Vec2 p2 = b2->m_position + r2; + p2X = b2.m_position.x + r2X; + p2Y = b2.m_position.y + r2Y; + + //this.m_u2 = p2 - s2; + this.m_u2.Set(p2X - s2X, p2Y - s2Y); + + length2 = this.m_u2.Length(); + + if (length2 > b2Settings.b2_linearSlop) + { + //this.m_u2 *= 1.0 / length2; + 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; + + //P2 = -impulse * this.m_u2; + p2X = -impulse * this.m_u2.x; + p2Y = -impulse * this.m_u2.y; + + //b2.m_position += b2.m_invMass * P2; + b2.m_position.x += b2.m_invMass * p2X; + b2.m_position.y += b2.m_invMass * p2Y; + //b2.m_rotation += b2.m_invI * b2Cross(r2, P2); + 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, + + // Effective masses + m_pulleyMass: null, + m_limitMass1: null, + m_limitMass2: null, + + // Impulses for accumulation/warm starting. + m_pulleyImpulse: null, + m_limitImpulse1: null, + m_limitImpulse2: null, + + // Position impulses for accumulation. + m_limitPositionImpulse1: null, + m_limitPositionImpulse2: null, + + m_limitState1: 0, + m_limitState2: 0 + + // static +}); + + + +b2PulleyJoint.b2_minPulleyLength = b2Settings.b2_lengthUnitsPerMeter; diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJointDef.js new file mode 100644 index 0000000..28d0acb --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2PulleyJointDef.js @@ -0,0 +1,70 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + +// The pulley joint is connected to two bodies and two fixed ground points. +// The pulley supports a ratio such that: +// length1 + ratio * length2 = constant +// Yes, the force transmitted is scaled by the ratio. +// The pulley also enforces a maximum length limit on both sides. This is +// useful to prevent one side of the pulley hitting the top. + +var b2PulleyJointDef = Class.create(); +Object.extend(b2PulleyJointDef.prototype, b2JointDef.prototype); +Object.extend(b2PulleyJointDef.prototype, +{ + initialize: function() + { + // The constructor for b2JointDef + this.type = b2Joint.e_unknownJoint; + this.userData = null; + this.body1 = null; + this.body2 = null; + this.collideConnected = false; + // + + // initialize instance variables for references + 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}); + diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJoint.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJoint.js new file mode 100644 index 0000000..db5738b --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJoint.js @@ -0,0 +1,491 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + +// Point-to-point constraint +// C = p2 - p1 +// Cdot = v2 - v1 +// = v2 + cross(w2, r2) - v1 - cross(w1, r1) +// J = [-I -r1_skew I r2_skew ] +// Identity used: +// w k % (rx i + ry j) = w * (-ry i + rx j) + +// Motor constraint +// Cdot = w2 - w1 +// J = [0 0 -1 0 0 1] +// K = invI1 + invI2 + +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 invTimeStep * this.m_ptpImpulse; + return tVec; + }, + + GetReactionTorque: function(invTimeStep) + { + return invTimeStep * this.m_limitImpulse; + }, + + //--------------- Internals Below ------------------- + + initialize: function(def){ + // The constructor for b2Joint + // initialize instance variables for references + 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; + // + + // initialize instance variables for references + 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(); + // + + //super(def); + + var tMat; + var tX; + var tY; + + //this.m_localAnchor1 = b2Math.b2MulTMV(this.m_body1.m_R, b2Math.SubtractVV( def.anchorPoint, this.m_body1.m_position)); + 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; + //this.m_localAnchor2 = b2Math.b2MulTMV(this.m_body2.m_R, b2Math.SubtractVV( def.anchorPoint, this.m_body2.m_position)); + 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; + }, + + // internal vars + 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; + + // Compute the effective mass matrix. + //b2Vec2 r1 = b2Mul(b1->m_R, this.m_localAnchor1); + 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; + //b2Vec2 r2 = b2Mul(b2->m_R, this.m_localAnchor2); + 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; + + // this.K = [(1/m1 + 1/m2) * eye(2) - skew(r1) * invI1 * skew(r1) - skew(r2) * invI2 * skew(r2)] + // = [1/m1+1/m2 0 ] + invI1 * [r1.y*r1.y -r1.x*r1.y] + invI2 * [r1.y*r1.y -r1.x*r1.y] + // [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x] + var invMass1 = b1.m_invMass; + var invMass2 = b2.m_invMass; + var invI1 = b1.m_invI; + var invI2 = b2.m_invI; + + //var this.K1 = new b2Mat22(); + this.K1.col1.x = invMass1 + invMass2; this.K1.col2.x = 0.0; + this.K1.col1.y = 0.0; this.K1.col2.y = invMass1 + invMass2; + + //var this.K2 = new b2Mat22(); + 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; + + //var this.K3 = new b2Mat22(); + 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; + + //var this.K = b2Math.AddMM(b2Math.AddMM(this.K1, this.K2), this.K3); + this.K.SetM(this.K1); + this.K.AddM(this.K2); + this.K.AddM(this.K3); + + //this.m_ptpMass = this.K.Invert(); + 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; + } + + // Warm starting. + if (b2World.s_enableWarmStarting) + { + //b1.m_linearVelocity.Subtract( b2Math.MulFV( invMass1, this.m_ptpImpulse) ); + b1.m_linearVelocity.x -= invMass1 * this.m_ptpImpulse.x; + b1.m_linearVelocity.y -= invMass1 * this.m_ptpImpulse.y; + //b1.m_angularVelocity -= invI1 * (b2Math.b2CrossVV(r1, this.m_ptpImpulse) + this.m_motorImpulse + this.m_limitImpulse); + b1.m_angularVelocity -= invI1 * ((r1X * this.m_ptpImpulse.y - r1Y * this.m_ptpImpulse.x) + this.m_motorImpulse + this.m_limitImpulse); + + //b2.m_linearVelocity.Add( b2Math.MulFV( invMass2 , this.m_ptpImpulse )); + b2.m_linearVelocity.x += invMass2 * this.m_ptpImpulse.x; + b2.m_linearVelocity.y += invMass2 * this.m_ptpImpulse.y; + //b2.m_angularVelocity += invI2 * (b2Math.b2CrossVV(r2, this.m_ptpImpulse) + this.m_motorImpulse + this.m_limitImpulse); + 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; + + //var r1 = b2Math.b2MulMV(b1.m_R, this.m_localAnchor1); + 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; + //var r2 = b2Math.b2MulMV(b2.m_R, this.m_localAnchor2); + 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; + + // Solve point-to-point constraint + //b2Vec2 ptpCdot = b2.m_linearVelocity + b2Cross(b2.m_angularVelocity, r2) - b1.m_linearVelocity - b2Cross(b1.m_angularVelocity, r1); + 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); + + //b2Vec2 ptpImpulse = -b2Mul(this.m_ptpMass, ptpCdot); + 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 -= b1->m_invMass * ptpImpulse; + b1.m_linearVelocity.x -= b1.m_invMass * ptpImpulseX; + b1.m_linearVelocity.y -= b1.m_invMass * ptpImpulseY; + //b1->m_angularVelocity -= b1->m_invI * b2Cross(r1, ptpImpulse); + b1.m_angularVelocity -= b1.m_invI * (r1X * ptpImpulseY - r1Y * ptpImpulseX); + + //b2->m_linearVelocity += b2->m_invMass * ptpImpulse; + b2.m_linearVelocity.x += b2.m_invMass * ptpImpulseX; + b2.m_linearVelocity.y += b2.m_invMass * ptpImpulseY; + //b2->m_angularVelocity += b2->m_invI * b2Cross(r2, ptpImpulse); + 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; + + // Solve point-to-point position error. + //var r1 = b2Math.b2MulMV(b1.m_R, this.m_localAnchor1); + 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; + //var r2 = b2Math.b2MulMV(b2.m_R, this.m_localAnchor2); + 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; + + //b2Vec2 p1 = b1->m_position + r1; + var p1X = b1.m_position.x + r1X; + var p1Y = b1.m_position.y + r1Y; + //b2Vec2 p2 = b2->m_position + r2; + var p2X = b2.m_position.x + r2X; + var p2Y = b2.m_position.y + r2Y; + + //b2Vec2 ptpC = p2 - p1; + var ptpCX = p2X - p1X; + var ptpCY = p2Y - p1Y; + + //float32 positionError = ptpC.Length(); + positionError = Math.sqrt(ptpCX*ptpCX + ptpCY*ptpCY); + + // Prevent overly large corrections. + //b2Vec2 dpMax(b2_maxLinearCorrection, b2_maxLinearCorrection); + //ptpC = b2Clamp(ptpC, -dpMax, dpMax); + + //float32 invMass1 = b1->m_invMass, invMass2 = b2->m_invMass; + var invMass1 = b1.m_invMass; + var invMass2 = b2.m_invMass; + //float32 invI1 = b1->m_invI, invI2 = b2->m_invI; + var invI1 = b1.m_invI; + var invI2 = b2.m_invI; + + //b2Mat22 this.K1; + this.K1.col1.x = invMass1 + invMass2; this.K1.col2.x = 0.0; + this.K1.col1.y = 0.0; this.K1.col2.y = invMass1 + invMass2; + + //b2Mat22 this.K2; + 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; + + //b2Mat22 this.K3; + 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; + + //b2Mat22 this.K = this.K1 + this.K2 + this.K3; + this.K.SetM(this.K1); + this.K.AddM(this.K2); + this.K.AddM(this.K3); + //b2Vec2 impulse = this.K.Solve(-ptpC); + this.K.Solve(b2RevoluteJoint.tImpulse, -ptpCX, -ptpCY); + var impulseX = b2RevoluteJoint.tImpulse.x; + var impulseY = b2RevoluteJoint.tImpulse.y; + + //b1.m_position -= b1.m_invMass * impulse; + b1.m_position.x -= b1.m_invMass * impulseX; + b1.m_position.y -= b1.m_invMass * impulseY; + //b1.m_rotation -= b1.m_invI * b2Cross(r1, impulse); + b1.m_rotation -= b1.m_invI * (r1X * impulseY - r1Y * impulseX); + b1.m_R.Set(b1.m_rotation); + + //b2.m_position += b2.m_invMass * impulse; + b2.m_position.x += b2.m_invMass * impulseX; + b2.m_position.y += b2.m_invMass * impulseY; + //b2.m_rotation += b2.m_invI * b2Cross(r2, impulse); + b2.m_rotation += b2.m_invI * (r2X * impulseY - r2Y * impulseX); + b2.m_R.Set(b2.m_rotation); + + + // Handle limits. + 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) + { + // Prevent large angular corrections + 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); + + // Prevent large angular corrections and allow some slop. + 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); + + // Prevent large angular corrections and allow some slop. + 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(); diff --git a/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJointDef.js b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJointDef.js new file mode 100644 index 0000000..4466785 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/box2d/dynamics/joints/b2RevoluteJointDef.js @@ -0,0 +1,55 @@ +/* +* Copyright (c) 2006-2007 Erin Catto http: +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked, and must not be +* misrepresented the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + + + + + + +var b2RevoluteJointDef = Class.create(); +Object.extend(b2RevoluteJointDef.prototype, b2JointDef.prototype); +Object.extend(b2RevoluteJointDef.prototype, +{ + initialize: function() + { + // The constructor for b2JointDef + 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/box2d-3d/third_party/prototype-1.6.0.2.js b/o3d/samples/box2d-3d/third_party/prototype-1.6.0.2.js new file mode 100644 index 0000000..6385503 --- /dev/null +++ b/o3d/samples/box2d-3d/third_party/prototype-1.6.0.2.js @@ -0,0 +1,4221 @@ +/* Prototype JavaScript framework, version 1.6.0.2 + * (c) 2005-2008 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://www.prototypejs.org/ + * + *--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.6.0.2', + + Browser: { + IE: !!(window.attachEvent && !window.opera), + Opera: !!window.opera, + WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, + Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1, + MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) + }, + + BrowserFeatures: { + XPath: !!document.evaluate, + ElementExtensions: !!window.HTMLElement, + SpecificElementExtensions: + document.createElement('div').__proto__ && + document.createElement('div').__proto__ !== + document.createElement('form').__proto__ + }, + + ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>', + JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, + + emptyFunction: function() { }, + K: function(x) { return x } +}; + +if (Prototype.Browser.MobileSafari) + Prototype.BrowserFeatures.SpecificElementExtensions = false; + + +/* Based on Alex Arnell's inheritance implementation. */ +var Class = { + create: function() { + var parent = null, properties = $A(arguments); + if (Object.isFunction(properties[0])) + parent = properties.shift(); + + function klass() { + this.initialize.apply(this, arguments); + } + + Object.extend(klass, Class.Methods); + klass.superclass = parent; + klass.subclasses = []; + + if (parent) { + var subclass = function() { }; + subclass.prototype = parent.prototype; + klass.prototype = new subclass; + parent.subclasses.push(klass); + } + + for (var i = 0; i < properties.length; i++) + klass.addMethods(properties[i]); + + if (!klass.prototype.initialize) + klass.prototype.initialize = Prototype.emptyFunction; + + klass.prototype.constructor = klass; + + return klass; + } +}; + +Class.Methods = { + addMethods: function(source) { + var ancestor = this.superclass && this.superclass.prototype; + var properties = Object.keys(source); + + if (!Object.keys({ toString: true }).length) + properties.push("toString", "valueOf"); + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && Object.isFunction(value) && + value.argumentNames().first() == "$super") { + var method = value, value = Object.extend((function(m) { + return function() { return ancestor[m].apply(this, arguments) }; + })(property).wrap(method), { + valueOf: function() { return method }, + toString: function() { return method.toString() } + }); + } + this.prototype[property] = value; + } + + return this; + } +}; + +var Abstract = { }; + +Object.extend = function(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; +}; + +Object.extend(Object, { + inspect: function(object) { + try { + if (Object.isUndefined(object)) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : String(object); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + }, + + toJSON: function(object) { + var type = typeof object; + switch (type) { + case 'undefined': + case 'function': + case 'unknown': return; + case 'boolean': return object.toString(); + } + + if (object === null) return 'null'; + if (object.toJSON) return object.toJSON(); + if (Object.isElement(object)) return; + + var results = []; + for (var property in object) { + var value = Object.toJSON(object[property]); + if (!Object.isUndefined(value)) + results.push(property.toJSON() + ': ' + value); + } + + return '{' + results.join(', ') + '}'; + }, + + toQueryString: function(object) { + return $H(object).toQueryString(); + }, + + toHTML: function(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + }, + + keys: function(object) { + var keys = []; + for (var property in object) + keys.push(property); + return keys; + }, + + values: function(object) { + var values = []; + for (var property in object) + values.push(object[property]); + return values; + }, + + clone: function(object) { + return Object.extend({ }, object); + }, + + isElement: function(object) { + return object && object.nodeType == 1; + }, + + isArray: function(object) { + return object != null && typeof object == "object" && + 'splice' in object && 'join' in object; + }, + + isHash: function(object) { + return object instanceof Hash; + }, + + isFunction: function(object) { + return typeof object == "function"; + }, + + isString: function(object) { + return typeof object == "string"; + }, + + isNumber: function(object) { + return typeof object == "number"; + }, + + isUndefined: function(object) { + return typeof object == "undefined"; + } +}); + +Object.extend(Function.prototype, { + argumentNames: function() { + var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip"); + return names.length == 1 && !names[0] ? [] : names; + }, + + bind: function() { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; + var __method = this, args = $A(arguments), object = args.shift(); + return function() { + return __method.apply(object, args.concat($A(arguments))); + } + }, + + bindAsEventListener: function() { + var __method = this, args = $A(arguments), object = args.shift(); + return function(event) { + return __method.apply(object, [event || window.event].concat(args)); + } + }, + + curry: function() { + if (!arguments.length) return this; + var __method = this, args = $A(arguments); + return function() { + return __method.apply(this, args.concat($A(arguments))); + } + }, + + delay: function() { + var __method = this, args = $A(arguments), timeout = args.shift() * 1000; + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + }, + + wrap: function(wrapper) { + var __method = this; + return function() { + return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); + } + }, + + methodize: function() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + return __method.apply(null, [this].concat($A(arguments))); + }; + } +}); + +Function.prototype.defer = Function.prototype.delay.curry(0.01); + +Date.prototype.toJSON = function() { + return '"' + this.getUTCFullYear() + '-' + + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + + this.getUTCDate().toPaddedString(2) + 'T' + + this.getUTCHours().toPaddedString(2) + ':' + + this.getUTCMinutes().toPaddedString(2) + ':' + + this.getUTCSeconds().toPaddedString(2) + 'Z"'; +}; + +var Try = { + these: function() { + var returnValue; + + for (var i = 0, length = arguments.length; i < length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) { } + } + + return returnValue; + } +}; + +RegExp.prototype.match = RegExp.prototype.test; + +RegExp.escape = function(str) { + return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); +}; + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create({ + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + execute: function() { + this.callback(this); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.execute(); + } finally { + this.currentlyExecuting = false; + } + } + } +}); +Object.extend(String, { + interpret: function(value) { + return value == null ? '' : String(value); + }, + specialChar: { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\\': '\\\\' + } +}); + +Object.extend(String.prototype, { + gsub: function(pattern, replacement) { + var result = '', source = this, match; + replacement = arguments.callee.prepareReplacement(replacement); + + while (source.length > 0) { + if (match = source.match(pattern)) { + result += source.slice(0, match.index); + result += String.interpret(replacement(match)); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + }, + + sub: function(pattern, replacement, count) { + replacement = this.gsub.prepareReplacement(replacement); + count = Object.isUndefined(count) ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + }, + + scan: function(pattern, iterator) { + this.gsub(pattern, iterator); + return String(this); + }, + + truncate: function(length, truncation) { + length = length || 30; + truncation = Object.isUndefined(truncation) ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : String(this); + }, + + strip: function() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + }, + + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + stripScripts: function() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + }, + + extractScripts: function() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + }, + + evalScripts: function() { + return this.extractScripts().map(function(script) { return eval(script) }); + }, + + escapeHTML: function() { + var self = arguments.callee; + self.text.data = this; + return self.div.innerHTML; + }, + + unescapeHTML: function() { + var div = new Element('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0] ? (div.childNodes.length > 1 ? + $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : + div.childNodes[0].nodeValue) : ''; + }, + + toQueryParams: function(separator) { + var match = this.strip().match(/([^?#]*)(#.*)?$/); + if (!match) return { }; + + return match[1].split(separator || '&').inject({ }, function(hash, pair) { + if ((pair = pair.split('='))[0]) { + var key = decodeURIComponent(pair.shift()); + var value = pair.length > 1 ? pair.join('=') : pair[0]; + if (value != undefined) value = decodeURIComponent(value); + + if (key in hash) { + if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + return hash; + }); + }, + + toArray: function() { + return this.split(''); + }, + + succ: function() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + }, + + times: function(count) { + return count < 1 ? '' : new Array(count + 1).join(this); + }, + + camelize: function() { + var parts = this.split('-'), len = parts.length; + if (len == 1) return parts[0]; + + var camelized = this.charAt(0) == '-' + ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) + : parts[0]; + + for (var i = 1; i < len; i++) + camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); + + return camelized; + }, + + capitalize: function() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + }, + + underscore: function() { + return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); + }, + + dasherize: function() { + return this.gsub(/_/,'-'); + }, + + inspect: function(useDoubleQuotes) { + var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { + var character = String.specialChar[match[0]]; + return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + }, + + toJSON: function() { + return this.inspect(true); + }, + + unfilterJSON: function(filter) { + return this.sub(filter || Prototype.JSONFilter, '#{1}'); + }, + + isJSON: function() { + var str = this; + if (str.blank()) return false; + str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); + return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); + }, + + evalJSON: function(sanitize) { + var json = this.unfilterJSON(); + try { + if (!sanitize || json.isJSON()) return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + }, + + include: function(pattern) { + return this.indexOf(pattern) > -1; + }, + + startsWith: function(pattern) { + return this.indexOf(pattern) === 0; + }, + + endsWith: function(pattern) { + var d = this.length - pattern.length; + return d >= 0 && this.lastIndexOf(pattern) === d; + }, + + empty: function() { + return this == ''; + }, + + blank: function() { + return /^\s*$/.test(this); + }, + + interpolate: function(object, pattern) { + return new Template(this, pattern).evaluate(object); + } +}); + +if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { + escapeHTML: function() { + return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + }, + unescapeHTML: function() { + return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + } +}); + +String.prototype.gsub.prepareReplacement = function(replacement) { + if (Object.isFunction(replacement)) return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; +}; + +String.prototype.parseQuery = String.prototype.toQueryParams; + +Object.extend(String.prototype.escapeHTML, { + div: document.createElement('div'), + text: document.createTextNode('') +}); + +with (String.prototype.escapeHTML) div.appendChild(text); + +var Template = Class.create({ + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + if (Object.isFunction(object.toTemplateReplacements)) + object = object.toTemplateReplacements(); + + return this.template.gsub(this.pattern, function(match) { + if (object == null) return ''; + + var before = match[1] || ''; + if (before == '\\') return match[2]; + + var ctx = object, expr = match[3]; + var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + match = pattern.exec(expr); + if (match == null) return before; + + while (match != null) { + var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1]; + ctx = ctx[comp]; + if (null == ctx || '' == match[3]) break; + expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); + match = pattern.exec(expr); + } + + return before + String.interpret(ctx); + }); + } +}); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; + +var $break = { }; + +var Enumerable = { + each: function(iterator, context) { + var index = 0; + iterator = iterator.bind(context); + try { + this._each(function(value) { + iterator(value, index++); + }); + } catch (e) { + if (e != $break) throw e; + } + return this; + }, + + eachSlice: function(number, iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var index = -number, slices = [], array = this.toArray(); + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.collect(iterator, context); + }, + + all: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result = true; + this.each(function(value, index) { + result = result && !!iterator(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result = false; + this.each(function(value, index) { + if (result = !!iterator(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var results = []; + this.each(function(value, index) { + results.push(iterator(value, index)); + }); + return results; + }, + + detect: function(iterator, context) { + iterator = iterator.bind(context); + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator, context) { + iterator = iterator.bind(context); + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(filter, iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var results = []; + + if (Object.isString(filter)) + filter = new RegExp(filter); + + this.each(function(value, index) { + if (filter.match(value)) + results.push(iterator(value, index)); + }); + return results; + }, + + include: function(object) { + if (Object.isFunction(this.indexOf)) + if (this.indexOf(object) != -1) return true; + + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + }, + + inGroupsOf: function(number, fillWith) { + fillWith = Object.isUndefined(fillWith) ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + }, + + inject: function(memo, iterator, context) { + iterator = iterator.bind(context); + this.each(function(value, index) { + memo = iterator(memo, value, index); + }); + return memo; + }, + + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + }, + + max: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result; + this.each(function(value, index) { + value = iterator(value, index); + if (result == null || value >= result) + result = value; + }); + return result; + }, + + min: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result; + this.each(function(value, index) { + value = iterator(value, index); + if (result == null || value < result) + result = value; + }); + return result; + }, + + partition: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var trues = [], falses = []; + this.each(function(value, index) { + (iterator(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator, context) { + iterator = iterator.bind(context); + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator, context) { + iterator = iterator.bind(context); + return this.map(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.map(); + }, + + zip: function() { + var iterator = Prototype.K, args = $A(arguments); + if (Object.isFunction(args.last())) + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + }, + + size: function() { + return this.toArray().length; + }, + + inspect: function() { + return '#<Enumerable:' + this.toArray().inspect() + '>'; + } +}; + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + filter: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray, + every: Enumerable.all, + some: Enumerable.any +}); +function $A(iterable) { + if (!iterable) return []; + if (iterable.toArray) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} + +if (Prototype.Browser.WebKit) { + $A = function(iterable) { + if (!iterable) return []; + if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') && + iterable.toArray) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; + }; +} + +Array.from = $A; + +Object.extend(Array.prototype, Enumerable); + +if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse; + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0, length = this.length; i < length; i++) + iterator(this[i]); + }, + + clear: function() { + this.length = 0; + return this; + }, + + first: function() { + return this[0]; + }, + + last: function() { + return this[this.length - 1]; + }, + + compact: function() { + return this.select(function(value) { + return value != null; + }); + }, + + flatten: function() { + return this.inject([], function(array, value) { + return array.concat(Object.isArray(value) ? + value.flatten() : [value]); + }); + }, + + without: function() { + var values = $A(arguments); + return this.select(function(value) { + return !values.include(value); + }); + }, + + reverse: function(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + }, + + reduce: function() { + return this.length > 1 ? this : this[0]; + }, + + uniq: function(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + }, + + intersect: function(array) { + return this.uniq().findAll(function(item) { + return array.detect(function(value) { return item === value }); + }); + }, + + clone: function() { + return [].concat(this); + }, + + size: function() { + return this.length; + }, + + inspect: function() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + }, + + toJSON: function() { + var results = []; + this.each(function(object) { + var value = Object.toJSON(object); + if (!Object.isUndefined(value)) results.push(value); + }); + return '[' + results.join(', ') + ']'; + } +}); + +// use native browser JS 1.6 implementation if available +if (Object.isFunction(Array.prototype.forEach)) + Array.prototype._each = Array.prototype.forEach; + +if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) { + i || (i = 0); + var length = this.length; + if (i < 0) i = length + i; + for (; i < length; i++) + if (this[i] === item) return i; + return -1; +}; + +if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) { + i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; + var n = this.slice(0, i).reverse().indexOf(item); + return (n < 0) ? n : i - n - 1; +}; + +Array.prototype.toArray = Array.prototype.clone; + +function $w(string) { + if (!Object.isString(string)) return []; + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +if (Prototype.Browser.Opera){ + Array.prototype.concat = function() { + var array = []; + for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); + for (var i = 0, length = arguments.length; i < length; i++) { + if (Object.isArray(arguments[i])) { + for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) + array.push(arguments[i][j]); + } else { + array.push(arguments[i]); + } + } + return array; + }; +} +Object.extend(Number.prototype, { + toColorPart: function() { + return this.toPaddedString(2, 16); + }, + + succ: function() { + return this + 1; + }, + + times: function(iterator) { + $R(0, this, true).each(iterator); + return this; + }, + + toPaddedString: function(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + }, + + toJSON: function() { + return isFinite(this) ? this.toString() : 'null'; + } +}); + +$w('abs round ceil floor').each(function(method){ + Number.prototype[method] = Math[method].methodize(); +}); +function $H(object) { + return new Hash(object); +}; + +var Hash = Class.create(Enumerable, (function() { + + function toQueryPair(key, value) { + if (Object.isUndefined(value)) return key; + return key + '=' + encodeURIComponent(String.interpret(value)); + } + + return { + initialize: function(object) { + this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); + }, + + _each: function(iterator) { + for (var key in this._object) { + var value = this._object[key], pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + }, + + set: function(key, value) { + return this._object[key] = value; + }, + + get: function(key) { + return this._object[key]; + }, + + unset: function(key) { + var value = this._object[key]; + delete this._object[key]; + return value; + }, + + toObject: function() { + return Object.clone(this._object); + }, + + keys: function() { + return this.pluck('key'); + }, + + values: function() { + return this.pluck('value'); + }, + + index: function(value) { + var match = this.detect(function(pair) { + return pair.value === value; + }); + return match && match.key; + }, + + merge: function(object) { + return this.clone().update(object); + }, + + update: function(object) { + return new Hash(object).inject(this, function(result, pair) { + result.set(pair.key, pair.value); + return result; + }); + }, + + toQueryString: function() { + return this.map(function(pair) { + var key = encodeURIComponent(pair.key), values = pair.value; + + if (values && typeof values == 'object') { + if (Object.isArray(values)) + return values.map(toQueryPair.curry(key)).join('&'); + } + return toQueryPair(key, values); + }).join('&'); + }, + + inspect: function() { + return '#<Hash:{' + this.map(function(pair) { + return pair.map(Object.inspect).join(': '); + }).join(', ') + '}>'; + }, + + toJSON: function() { + return Object.toJSON(this.toObject()); + }, + + clone: function() { + return new Hash(this); + } + } +})()); + +Hash.prototype.toTemplateReplacements = Hash.prototype.toObject; +Hash.from = $H; +var ObjectRange = Class.create(Enumerable, { + initialize: function(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + }, + + _each: function(iterator) { + var value = this.start; + while (this.include(value)) { + iterator(value); + value = value.succ(); + } + }, + + include: function(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } +}); + +var $R = function(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +}; + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new XMLHttpRequest()}, + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')} + ) || false; + }, + + activeRequestCount: 0 +}; + +Ajax.Responders = { + responders: [], + + _each: function(iterator) { + this.responders._each(iterator); + }, + + register: function(responder) { + if (!this.include(responder)) + this.responders.push(responder); + }, + + unregister: function(responder) { + this.responders = this.responders.without(responder); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (Object.isFunction(responder[callback])) { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) { } + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { Ajax.activeRequestCount++ }, + onComplete: function() { Ajax.activeRequestCount-- } +}); + +Ajax.Base = Class.create({ + initialize: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '', + evalJSON: true, + evalJS: true + }; + Object.extend(this.options, options || { }); + + this.options.method = this.options.method.toLowerCase(); + + if (Object.isString(this.options.parameters)) + this.options.parameters = this.options.parameters.toQueryParams(); + else if (Object.isHash(this.options.parameters)) + this.options.parameters = this.options.parameters.toObject(); + } +}); + +Ajax.Request = Class.create(Ajax.Base, { + _complete: false, + + initialize: function($super, url, options) { + $super(options); + this.transport = Ajax.getTransport(); + this.request(url); + }, + + request: function(url) { + this.url = url; + this.method = this.options.method; + var params = Object.clone(this.options.parameters); + + if (!['get', 'post'].include(this.method)) { + // simulate other verbs over post + params['_method'] = this.method; + this.method = 'post'; + } + + this.parameters = params; + + if (params = Object.toQueryString(params)) { + // when GET, append parameters to URL + if (this.method == 'get') + this.url += (this.url.include('?') ? '&' : '?') + params; + else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + params += '&_='; + } + + try { + var response = new Ajax.Response(this); + if (this.options.onCreate) this.options.onCreate(response); + Ajax.Responders.dispatch('onCreate', this, response); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); + + this.transport.onreadystatechange = this.onStateChange.bind(this); + this.setRequestHeaders(); + + this.body = this.method == 'post' ? (this.options.postBody || params) : null; + this.transport.send(this.body); + + /* Force Firefox to handle ready state 4 for synchronous requests */ + if (!this.options.asynchronous && this.transport.overrideMimeType) + this.onStateChange(); + + } + catch (e) { + this.dispatchException(e); + } + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState > 1 && !((readyState == 4) && this._complete)) + this.respondToReadyState(this.transport.readyState); + }, + + setRequestHeaders: function() { + var headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'X-Prototype-Version': Prototype.Version, + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + }; + + if (this.method == 'post') { + headers['Content-type'] = this.options.contentType + + (this.options.encoding ? '; charset=' + this.options.encoding : ''); + + /* Force "Connection: close" for older Mozilla browsers to work + * around a bug where XMLHttpRequest sends an incorrect + * Content-length header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType && + (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) + headers['Connection'] = 'close'; + } + + // user-defined headers + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (Object.isFunction(extras.push)) + for (var i = 0, length = extras.length; i < length; i += 2) + headers[extras[i]] = extras[i+1]; + else + $H(extras).each(function(pair) { headers[pair.key] = pair.value }); + } + + for (var name in headers) + this.transport.setRequestHeader(name, headers[name]); + }, + + success: function() { + var status = this.getStatus(); + return !status || (status >= 200 && status < 300); + }, + + getStatus: function() { + try { + return this.transport.status || 0; + } catch (e) { return 0 } + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + response.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + var contentType = response.getHeader('Content-type'); + if (this.options.evalJS == 'force' + || (this.options.evalJS && this.isSameOrigin() && contentType + && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); + Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + // avoid memory leak in MSIE: clean up + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + isSameOrigin: function() { + var m = this.url.match(/^\s*https?:\/\/[^\/]*/); + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ + protocol: location.protocol, + domain: document.domain, + port: location.port ? ':' + location.port : '' + })); + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name) || null; + } catch (e) { return null } + }, + + evalResponse: function() { + try { + return eval((this.transport.responseText || '').unfilterJSON()); + } catch (e) { + this.dispatchException(e); + } + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Response = Class.create({ + initialize: function(request){ + this.request = request; + var transport = this.transport = request.transport, + readyState = this.readyState = transport.readyState; + + if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { + this.status = this.getStatus(); + this.statusText = this.getStatusText(); + this.responseText = String.interpret(transport.responseText); + this.headerJSON = this._getHeaderJSON(); + } + + if(readyState == 4) { + var xml = transport.responseXML; + this.responseXML = Object.isUndefined(xml) ? null : xml; + this.responseJSON = this._getResponseJSON(); + } + }, + + status: 0, + statusText: '', + + getStatus: Ajax.Request.prototype.getStatus, + + getStatusText: function() { + try { + return this.transport.statusText || ''; + } catch (e) { return '' } + }, + + getHeader: Ajax.Request.prototype.getHeader, + + getAllHeaders: function() { + try { + return this.getAllResponseHeaders(); + } catch (e) { return null } + }, + + getResponseHeader: function(name) { + return this.transport.getResponseHeader(name); + }, + + getAllResponseHeaders: function() { + return this.transport.getAllResponseHeaders(); + }, + + _getHeaderJSON: function() { + var json = this.getHeader('X-JSON'); + if (!json) return null; + json = decodeURIComponent(escape(json)); + try { + return json.evalJSON(this.request.options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + }, + + _getResponseJSON: function() { + var options = this.request.options; + if (!options.evalJSON || (options.evalJSON != 'force' && + !(this.getHeader('Content-type') || '').include('application/json')) || + this.responseText.blank()) + return null; + try { + return this.responseText.evalJSON(options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + } +}); + +Ajax.Updater = Class.create(Ajax.Request, { + initialize: function($super, container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + }; + + options = Object.clone(options); + var onComplete = options.onComplete; + options.onComplete = (function(response, json) { + this.updateContent(response.responseText); + if (Object.isFunction(onComplete)) onComplete(response, json); + }).bind(this); + + $super(url, options); + }, + + updateContent: function(responseText) { + var receiver = this.container[this.success() ? 'success' : 'failure'], + options = this.options; + + if (!options.evalScripts) responseText = responseText.stripScripts(); + + if (receiver = $(receiver)) { + if (options.insertion) { + if (Object.isString(options.insertion)) { + var insertion = { }; insertion[options.insertion] = responseText; + receiver.insert(insertion); + } + else options.insertion(receiver, responseText); + } + else receiver.update(responseText); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { + initialize: function($super, container, url, options) { + $super(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = { }; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.options.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(response) { + if (this.options.decay) { + this.decay = (response.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = response.responseText; + } + this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); +function $(element) { + if (arguments.length > 1) { + for (var i = 0, elements = [], length = arguments.length; i < length; i++) + elements.push($(arguments[i])); + return elements; + } + if (Object.isString(element)) + element = document.getElementById(element); + return Element.extend(element); +} + +if (Prototype.BrowserFeatures.XPath) { + document._getElementsByXPath = function(expression, parentElement) { + var results = []; + var query = document.evaluate(expression, $(parentElement) || document, + null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + for (var i = 0, length = query.snapshotLength; i < length; i++) + results.push(Element.extend(query.snapshotItem(i))); + return results; + }; +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Node) var Node = { }; + +if (!Node.ELEMENT_NODE) { + // DOM level 2 ECMAScript Language Binding + Object.extend(Node, { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 + }); +} + +(function() { + var element = this.Element; + this.Element = function(tagName, attributes) { + attributes = attributes || { }; + tagName = tagName.toLowerCase(); + var cache = Element.cache; + if (Prototype.Browser.IE && attributes.name) { + tagName = '<' + tagName + ' name="' + attributes.name + '">'; + delete attributes.name; + return Element.writeAttribute(document.createElement(tagName), attributes); + } + if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); + return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); + }; + Object.extend(this.Element, element || { }); +}).call(window); + +Element.cache = { }; + +Element.Methods = { + visible: function(element) { + return $(element).style.display != 'none'; + }, + + toggle: function(element) { + element = $(element); + Element[Element.visible(element) ? 'hide' : 'show'](element); + return element; + }, + + hide: function(element) { + $(element).style.display = 'none'; + return element; + }, + + show: function(element) { + $(element).style.display = ''; + return element; + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + return element; + }, + + update: function(element, content) { + element = $(element); + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) return element.update().insert(content); + content = Object.toHTML(content); + element.innerHTML = content.stripScripts(); + content.evalScripts.bind(content).defer(); + return element; + }, + + replace: function(element, content) { + element = $(element); + if (content && content.toElement) content = content.toElement(); + else if (!Object.isElement(content)) { + content = Object.toHTML(content); + var range = element.ownerDocument.createRange(); + range.selectNode(element); + content.evalScripts.bind(content).defer(); + content = range.createContextualFragment(content.stripScripts()); + } + element.parentNode.replaceChild(content, element); + return element; + }, + + insert: function(element, insertions) { + element = $(element); + + if (Object.isString(insertions) || Object.isNumber(insertions) || + Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) + insertions = {bottom:insertions}; + + var content, insert, tagName, childNodes; + + for (var position in insertions) { + content = insertions[position]; + position = position.toLowerCase(); + insert = Element._insertionTranslations[position]; + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + insert(element, content); + continue; + } + + content = Object.toHTML(content); + + tagName = ((position == 'before' || position == 'after') + ? element.parentNode : element).tagName.toUpperCase(); + + childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + + if (position == 'top' || position == 'after') childNodes.reverse(); + childNodes.each(insert.curry(element)); + + content.evalScripts.bind(content).defer(); + } + + return element; + }, + + wrap: function(element, wrapper, attributes) { + element = $(element); + if (Object.isElement(wrapper)) + $(wrapper).writeAttribute(attributes || { }); + else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); + else wrapper = new Element('div', wrapper); + if (element.parentNode) + element.parentNode.replaceChild(wrapper, element); + wrapper.appendChild(element); + return wrapper; + }, + + inspect: function(element) { + element = $(element); + var result = '<' + element.tagName.toLowerCase(); + $H({'id': 'id', 'className': 'class'}).each(function(pair) { + var property = pair.first(), attribute = pair.last(); + var value = (element[property] || '').toString(); + if (value) result += ' ' + attribute + '=' + value.inspect(true); + }); + return result + '>'; + }, + + recursivelyCollect: function(element, property) { + element = $(element); + var elements = []; + while (element = element[property]) + if (element.nodeType == 1) + elements.push(Element.extend(element)); + return elements; + }, + + ancestors: function(element) { + return $(element).recursivelyCollect('parentNode'); + }, + + descendants: function(element) { + return $(element).select("*"); + }, + + firstDescendant: function(element) { + element = $(element).firstChild; + while (element && element.nodeType != 1) element = element.nextSibling; + return $(element); + }, + + immediateDescendants: function(element) { + if (!(element = $(element).firstChild)) return []; + while (element && element.nodeType != 1) element = element.nextSibling; + if (element) return [element].concat($(element).nextSiblings()); + return []; + }, + + previousSiblings: function(element) { + return $(element).recursivelyCollect('previousSibling'); + }, + + nextSiblings: function(element) { + return $(element).recursivelyCollect('nextSibling'); + }, + + siblings: function(element) { + element = $(element); + return element.previousSiblings().reverse().concat(element.nextSiblings()); + }, + + match: function(element, selector) { + if (Object.isString(selector)) + selector = new Selector(selector); + return selector.match($(element)); + }, + + up: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(element.parentNode); + var ancestors = element.ancestors(); + return Object.isNumber(expression) ? ancestors[expression] : + Selector.findElement(ancestors, expression, index); + }, + + down: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return element.firstDescendant(); + return Object.isNumber(expression) ? element.descendants()[expression] : + element.select(expression)[index || 0]; + }, + + previous: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); + var previousSiblings = element.previousSiblings(); + return Object.isNumber(expression) ? previousSiblings[expression] : + Selector.findElement(previousSiblings, expression, index); + }, + + next: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); + var nextSiblings = element.nextSiblings(); + return Object.isNumber(expression) ? nextSiblings[expression] : + Selector.findElement(nextSiblings, expression, index); + }, + + select: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element, args); + }, + + adjacent: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element.parentNode, args).without(element); + }, + + identify: function(element) { + element = $(element); + var id = element.readAttribute('id'), self = arguments.callee; + if (id) return id; + do { id = 'anonymous_element_' + self.counter++ } while ($(id)); + element.writeAttribute('id', id); + return id; + }, + + readAttribute: function(element, name) { + element = $(element); + if (Prototype.Browser.IE) { + var t = Element._attributeTranslations.read; + if (t.values[name]) return t.values[name](element, name); + if (t.names[name]) name = t.names[name]; + if (name.include(':')) { + return (!element.attributes || !element.attributes[name]) ? null : + element.attributes[name].value; + } + } + return element.getAttribute(name); + }, + + writeAttribute: function(element, name, value) { + element = $(element); + var attributes = { }, t = Element._attributeTranslations.write; + + if (typeof name == 'object') attributes = name; + else attributes[name] = Object.isUndefined(value) ? true : value; + + for (var attr in attributes) { + name = t.names[attr] || attr; + value = attributes[attr]; + if (t.values[attr]) name = t.values[attr](element, value); + if (value === false || value === null) + element.removeAttribute(name); + else if (value === true) + element.setAttribute(name, name); + else element.setAttribute(name, value); + } + return element; + }, + + getHeight: function(element) { + return $(element).getDimensions().height; + }, + + getWidth: function(element) { + return $(element).getDimensions().width; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + var elementClassName = element.className; + return (elementClassName.length > 0 && (elementClassName == className || + new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + if (!element.hasClassName(className)) + element.className += (element.className ? ' ' : '') + className; + return element; + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + element.className = element.className.replace( + new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); + return element; + }, + + toggleClassName: function(element, className) { + if (!(element = $(element))) return; + return element[element.hasClassName(className) ? + 'removeClassName' : 'addClassName'](className); + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + element = $(element); + var node = element.firstChild; + while (node) { + var nextNode = node.nextSibling; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + element.removeChild(node); + node = nextNode; + } + return element; + }, + + empty: function(element) { + return $(element).innerHTML.blank(); + }, + + descendantOf: function(element, ancestor) { + element = $(element), ancestor = $(ancestor); + var originalAncestor = ancestor; + + if (element.compareDocumentPosition) + return (element.compareDocumentPosition(ancestor) & 8) === 8; + + if (element.sourceIndex && !Prototype.Browser.Opera) { + var e = element.sourceIndex, a = ancestor.sourceIndex, + nextAncestor = ancestor.nextSibling; + if (!nextAncestor) { + do { ancestor = ancestor.parentNode; } + while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode); + } + if (nextAncestor && nextAncestor.sourceIndex) + return (e > a && e < nextAncestor.sourceIndex); + } + + while (element = element.parentNode) + if (element == originalAncestor) return true; + return false; + }, + + scrollTo: function(element) { + element = $(element); + var pos = element.cumulativeOffset(); + window.scrollTo(pos[0], pos[1]); + return element; + }, + + getStyle: function(element, style) { + element = $(element); + style = style == 'float' ? 'cssFloat' : style.camelize(); + var value = element.style[style]; + if (!value) { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + if (style == 'opacity') return value ? parseFloat(value) : 1.0; + return value == 'auto' ? null : value; + }, + + getOpacity: function(element) { + return $(element).getStyle('opacity'); + }, + + setStyle: function(element, styles) { + element = $(element); + var elementStyle = element.style, match; + if (Object.isString(styles)) { + element.style.cssText += ';' + styles; + return styles.include('opacity') ? + element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; + } + for (var property in styles) + if (property == 'opacity') element.setOpacity(styles[property]); + else + elementStyle[(property == 'float' || property == 'cssFloat') ? + (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : + property] = styles[property]; + + return element; + }, + + setOpacity: function(element, value) { + element = $(element); + element.style.opacity = (value == 1 || value === '') ? '' : + (value < 0.00001) ? 0 : value; + return element; + }, + + getDimensions: function(element) { + element = $(element); + var display = $(element).getStyle('display'); + if (display != 'none' && display != null) // Safari bug + return {width: element.offsetWidth, height: element.offsetHeight}; + + // All *Width and *Height properties give 0 on elements with display none, + // so enable the element temporarily + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + var originalDisplay = els.display; + els.visibility = 'hidden'; + els.position = 'absolute'; + els.display = 'block'; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = originalDisplay; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + }, + + makePositioned: function(element) { + element = $(element); + var pos = Element.getStyle(element, 'position'); + if (pos == 'static' || !pos) { + element._madePositioned = true; + element.style.position = 'relative'; + // Opera returns the offset relative to the positioning context, when an + // element is position relative but top and left have not been defined + if (window.opera) { + element.style.top = 0; + element.style.left = 0; + } + } + return element; + }, + + undoPositioned: function(element) { + element = $(element); + if (element._madePositioned) { + element._madePositioned = undefined; + element.style.position = + element.style.top = + element.style.left = + element.style.bottom = + element.style.right = ''; + } + return element; + }, + + makeClipping: function(element) { + element = $(element); + if (element._overflow) return element; + element._overflow = Element.getStyle(element, 'overflow') || 'auto'; + if (element._overflow !== 'hidden') + element.style.overflow = 'hidden'; + return element; + }, + + undoClipping: function(element) { + element = $(element); + if (!element._overflow) return element; + element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; + element._overflow = null; + return element; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + if (element.tagName == 'BODY') break; + var p = Element.getStyle(element, 'position'); + if (p !== 'static') break; + } + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + absolutize: function(element) { + element = $(element); + if (element.getStyle('position') == 'absolute') return; + // Position.prepare(); // To be done manually by Scripty when it needs it. + + var offsets = element.positionedOffset(); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.width = width + 'px'; + element.style.height = height + 'px'; + return element; + }, + + relativize: function(element) { + element = $(element); + if (element.getStyle('position') == 'relative') return; + // Position.prepare(); // To be done manually by Scripty when it needs it. + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; + return element; + }, + + cumulativeScrollOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + getOffsetParent: function(element) { + if (element.offsetParent) return $(element.offsetParent); + if (element == document.body) return $(element); + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element, 'position') != 'static') + return $(element); + + return $(document.body); + }, + + viewportOffset: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent == document.body && + Element.getStyle(element, 'position') == 'absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + if (!Prototype.Browser.Opera || element.tagName == 'BODY') { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + + return Element._returnOffset(valueL, valueT); + }, + + clonePosition: function(element, source) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || { }); + + // find page position of source + source = $(source); + var p = source.viewportOffset(); + + // find coordinate system to use + element = $(element); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(element, 'position') == 'absolute') { + parent = element.getOffsetParent(); + delta = parent.viewportOffset(); + } + + // correct by body offsets (fixes Safari) + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if (options.setWidth) element.style.width = source.offsetWidth + 'px'; + if (options.setHeight) element.style.height = source.offsetHeight + 'px'; + return element; + } +}; + +Element.Methods.identify.counter = 1; + +Object.extend(Element.Methods, { + getElementsBySelector: Element.Methods.select, + childElements: Element.Methods.immediateDescendants +}); + +Element._attributeTranslations = { + write: { + names: { + className: 'class', + htmlFor: 'for' + }, + values: { } + } +}; + +if (Prototype.Browser.Opera) { + Element.Methods.getStyle = Element.Methods.getStyle.wrap( + function(proceed, element, style) { + switch (style) { + case 'left': case 'top': case 'right': case 'bottom': + if (proceed(element, 'position') === 'static') return null; + case 'height': case 'width': + // returns '0px' for hidden elements; we want it to return null + if (!Element.visible(element)) return null; + + // returns the border-box dimensions rather than the content-box + // dimensions, so we subtract padding and borders from the value + var dim = parseInt(proceed(element, style), 10); + + if (dim !== element['offset' + style.capitalize()]) + return dim + 'px'; + + var properties; + if (style === 'height') { + properties = ['border-top-width', 'padding-top', + 'padding-bottom', 'border-bottom-width']; + } + else { + properties = ['border-left-width', 'padding-left', + 'padding-right', 'border-right-width']; + } + return properties.inject(dim, function(memo, property) { + var val = proceed(element, property); + return val === null ? memo : memo - parseInt(val, 10); + }) + 'px'; + default: return proceed(element, style); + } + } + ); + + Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( + function(proceed, element, attribute) { + if (attribute === 'title') return element.title; + return proceed(element, attribute); + } + ); +} + +else if (Prototype.Browser.IE) { + // IE doesn't report offsets correctly for static elements, so we change them + // to "relative" to get the values, then change them back. + Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + $w('positionedOffset viewportOffset').each(function(method) { + Element.Methods[method] = Element.Methods[method].wrap( + function(proceed, element) { + element = $(element); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + // Trigger hasLayout on the offset parent so that IE6 reports + // accurate offsetTop and offsetLeft values for position: fixed. + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + offsetParent.setStyle({ zoom: 1 }); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + }); + + Element.Methods.getStyle = function(element, style) { + element = $(element); + style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); + var value = element.style[style]; + if (!value && element.currentStyle) value = element.currentStyle[style]; + + if (style == 'opacity') { + if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) + if (value[1]) return parseFloat(value[1]) / 100; + return 1.0; + } + + if (value == 'auto') { + if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) + return element['offset' + style.capitalize()] + 'px'; + return null; + } + return value; + }; + + Element.Methods.setOpacity = function(element, value) { + function stripAlpha(filter){ + return filter.replace(/alpha\([^\)]*\)/gi,''); + } + element = $(element); + var currentStyle = element.currentStyle; + if ((currentStyle && !currentStyle.hasLayout) || + (!currentStyle && element.style.zoom == 'normal')) + element.style.zoom = 1; + + var filter = element.getStyle('filter'), style = element.style; + if (value == 1 || value === '') { + (filter = stripAlpha(filter)) ? + style.filter = filter : style.removeAttribute('filter'); + return element; + } else if (value < 0.00001) value = 0; + style.filter = stripAlpha(filter) + + 'alpha(opacity=' + (value * 100) + ')'; + return element; + }; + + Element._attributeTranslations = { + read: { + names: { + 'class': 'className', + 'for': 'htmlFor' + }, + values: { + _getAttr: function(element, attribute) { + return element.getAttribute(attribute, 2); + }, + _getAttrNode: function(element, attribute) { + var node = element.getAttributeNode(attribute); + return node ? node.value : ""; + }, + _getEv: function(element, attribute) { + attribute = element.getAttribute(attribute); + return attribute ? attribute.toString().slice(23, -2) : null; + }, + _flag: function(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + }, + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + return element.title; + } + } + } + }; + + Element._attributeTranslations.write = { + names: Object.extend({ + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing' + }, Element._attributeTranslations.read.names), + values: { + checked: function(element, value) { + element.checked = !!value; + }, + + style: function(element, value) { + element.style.cssText = value ? value : ''; + } + } + }; + + Element._attributeTranslations.has = {}; + + $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + + 'encType maxLength readOnly longDesc').each(function(attr) { + Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; + Element._attributeTranslations.has[attr.toLowerCase()] = attr; + }); + + (function(v) { + Object.extend(v, { + href: v._getAttr, + src: v._getAttr, + type: v._getAttr, + action: v._getAttrNode, + disabled: v._flag, + checked: v._flag, + readonly: v._flag, + multiple: v._flag, + onload: v._getEv, + onunload: v._getEv, + onclick: v._getEv, + ondblclick: v._getEv, + onmousedown: v._getEv, + onmouseup: v._getEv, + onmouseover: v._getEv, + onmousemove: v._getEv, + onmouseout: v._getEv, + onfocus: v._getEv, + onblur: v._getEv, + onkeypress: v._getEv, + onkeydown: v._getEv, + onkeyup: v._getEv, + onsubmit: v._getEv, + onreset: v._getEv, + onselect: v._getEv, + onchange: v._getEv + }); + })(Element._attributeTranslations.read.values); +} + +else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1) ? 0.999999 : + (value === '') ? '' : (value < 0.00001) ? 0 : value; + return element; + }; +} + +else if (Prototype.Browser.WebKit) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1 || value === '') ? '' : + (value < 0.00001) ? 0 : value; + + if (value == 1) + if(element.tagName == 'IMG' && element.width) { + element.width++; element.width--; + } else try { + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch (e) { } + + return element; + }; + + // Safari returns margins on body which is incorrect if the child is absolutely + // positioned. For performance reasons, redefine Element#cumulativeOffset for + // KHTML/WebKit only. + Element.Methods.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return Element._returnOffset(valueL, valueT); + }; +} + +if (Prototype.Browser.IE || Prototype.Browser.Opera) { + // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements + Element.Methods.update = function(element, content) { + element = $(element); + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) return element.update().insert(content); + + content = Object.toHTML(content); + var tagName = element.tagName.toUpperCase(); + + if (tagName in Element._insertionTranslations.tags) { + $A(element.childNodes).each(function(node) { element.removeChild(node) }); + Element._getContentFromAnonymousElement(tagName, content.stripScripts()) + .each(function(node) { element.appendChild(node) }); + } + else element.innerHTML = content.stripScripts(); + + content.evalScripts.bind(content).defer(); + return element; + }; +} + +if ('outerHTML' in document.createElement('div')) { + Element.Methods.replace = function(element, content) { + element = $(element); + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + element.parentNode.replaceChild(content, element); + return element; + } + + content = Object.toHTML(content); + var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); + + if (Element._insertionTranslations.tags[tagName]) { + var nextSibling = element.next(); + var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + parent.removeChild(element); + if (nextSibling) + fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); + else + fragments.each(function(node) { parent.appendChild(node) }); + } + else element.outerHTML = content.stripScripts(); + + content.evalScripts.bind(content).defer(); + return element; + }; +} + +Element._returnOffset = function(l, t) { + var result = [l, t]; + result.left = l; + result.top = t; + return result; +}; + +Element._getContentFromAnonymousElement = function(tagName, html) { + var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; + if (t) { + div.innerHTML = t[0] + html + t[1]; + t[2].times(function() { div = div.firstChild }); + } else div.innerHTML = html; + return $A(div.childNodes); +}; + +Element._insertionTranslations = { + before: function(element, node) { + element.parentNode.insertBefore(node, element); + }, + top: function(element, node) { + element.insertBefore(node, element.firstChild); + }, + bottom: function(element, node) { + element.appendChild(node); + }, + after: function(element, node) { + element.parentNode.insertBefore(node, element.nextSibling); + }, + tags: { + TABLE: ['<table>', '</table>', 1], + TBODY: ['<table><tbody>', '</tbody></table>', 2], + TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3], + TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4], + SELECT: ['<select>', '</select>', 1] + } +}; + +(function() { + Object.extend(this.tags, { + THEAD: this.tags.TBODY, + TFOOT: this.tags.TBODY, + TH: this.tags.TD + }); +}).call(Element._insertionTranslations); + +Element.Methods.Simulated = { + hasAttribute: function(element, attribute) { + attribute = Element._attributeTranslations.has[attribute] || attribute; + var node = $(element).getAttributeNode(attribute); + return node && node.specified; + } +}; + +Element.Methods.ByTag = { }; + +Object.extend(Element, Element.Methods); + +if (!Prototype.BrowserFeatures.ElementExtensions && + document.createElement('div').__proto__) { + window.HTMLElement = { }; + window.HTMLElement.prototype = document.createElement('div').__proto__; + Prototype.BrowserFeatures.ElementExtensions = true; +} + +Element.extend = (function() { + if (Prototype.BrowserFeatures.SpecificElementExtensions) + return Prototype.K; + + var Methods = { }, ByTag = Element.Methods.ByTag; + + var extend = Object.extend(function(element) { + if (!element || element._extendedByPrototype || + element.nodeType != 1 || element == window) return element; + + var methods = Object.clone(Methods), + tagName = element.tagName, property, value; + + // extend methods for specific tags + if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); + + for (property in methods) { + value = methods[property]; + if (Object.isFunction(value) && !(property in element)) + element[property] = value.methodize(); + } + + element._extendedByPrototype = Prototype.emptyFunction; + return element; + + }, { + refresh: function() { + // extend methods for all tags (Safari doesn't need this) + if (!Prototype.BrowserFeatures.ElementExtensions) { + Object.extend(Methods, Element.Methods); + Object.extend(Methods, Element.Methods.Simulated); + } + } + }); + + extend.refresh(); + return extend; +})(); + +Element.hasAttribute = function(element, attribute) { + if (element.hasAttribute) return element.hasAttribute(attribute); + return Element.Methods.Simulated.hasAttribute(element, attribute); +}; + +Element.addMethods = function(methods) { + var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; + + if (!methods) { + Object.extend(Form, Form.Methods); + Object.extend(Form.Element, Form.Element.Methods); + Object.extend(Element.Methods.ByTag, { + "FORM": Object.clone(Form.Methods), + "INPUT": Object.clone(Form.Element.Methods), + "SELECT": Object.clone(Form.Element.Methods), + "TEXTAREA": Object.clone(Form.Element.Methods) + }); + } + + if (arguments.length == 2) { + var tagName = methods; + methods = arguments[1]; + } + + if (!tagName) Object.extend(Element.Methods, methods || { }); + else { + if (Object.isArray(tagName)) tagName.each(extend); + else extend(tagName); + } + + function extend(tagName) { + tagName = tagName.toUpperCase(); + if (!Element.Methods.ByTag[tagName]) + Element.Methods.ByTag[tagName] = { }; + Object.extend(Element.Methods.ByTag[tagName], methods); + } + + function copy(methods, destination, onlyIfAbsent) { + onlyIfAbsent = onlyIfAbsent || false; + for (var property in methods) { + var value = methods[property]; + if (!Object.isFunction(value)) continue; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = value.methodize(); + } + } + + function findDOMClass(tagName) { + var klass; + var trans = { + "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", + "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", + "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", + "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", + "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": + "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": + "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": + "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": + "FrameSet", "IFRAME": "IFrame" + }; + if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName.capitalize() + 'Element'; + if (window[klass]) return window[klass]; + + window[klass] = { }; + window[klass].prototype = document.createElement(tagName).__proto__; + return window[klass]; + } + + if (F.ElementExtensions) { + copy(Element.Methods, HTMLElement.prototype); + copy(Element.Methods.Simulated, HTMLElement.prototype, true); + } + + if (F.SpecificElementExtensions) { + for (var tag in Element.Methods.ByTag) { + var klass = findDOMClass(tag); + if (Object.isUndefined(klass)) continue; + copy(T[tag], klass.prototype); + } + } + + Object.extend(Element, Element.Methods); + delete Element.ByTag; + + if (Element.extend.refresh) Element.extend.refresh(); + Element.cache = { }; +}; + +document.viewport = { + getDimensions: function() { + var dimensions = { }; + var B = Prototype.Browser; + $w('width height').each(function(d) { + var D = d.capitalize(); + dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] : + (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D]; + }); + return dimensions; + }, + + getWidth: function() { + return this.getDimensions().width; + }, + + getHeight: function() { + return this.getDimensions().height; + }, + + getScrollOffsets: function() { + return Element._returnOffset( + window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, + window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); + } +}; +/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, + * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style + * license. Please see http://www.yui-ext.com/ for more information. */ + +var Selector = Class.create({ + initialize: function(expression) { + this.expression = expression.strip(); + this.compileMatcher(); + }, + + shouldUseXPath: function() { + if (!Prototype.BrowserFeatures.XPath) return false; + + var e = this.expression; + + // Safari 3 chokes on :*-of-type and :empty + if (Prototype.Browser.WebKit && + (e.include("-of-type") || e.include(":empty"))) + return false; + + // XPath can't do namespaced attributes, nor can it read + // the "checked" property from DOM nodes + if ((/(\[[\w-]*?:|:checked)/).test(this.expression)) + return false; + + return true; + }, + + compileMatcher: function() { + if (this.shouldUseXPath()) + return this.compileXPathMatcher(); + + var e = this.expression, ps = Selector.patterns, h = Selector.handlers, + c = Selector.criteria, le, p, m; + + if (Selector._cache[e]) { + this.matcher = Selector._cache[e]; + return; + } + + this.matcher = ["this.matcher = function(root) {", + "var r = root, h = Selector.handlers, c = false, n;"]; + + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + p = ps[i]; + if (m = e.match(p)) { + this.matcher.push(Object.isFunction(c[i]) ? c[i](m) : + new Template(c[i]).evaluate(m)); + e = e.replace(m[0], ''); + break; + } + } + } + + this.matcher.push("return h.unique(n);\n}"); + eval(this.matcher.join('\n')); + Selector._cache[this.expression] = this.matcher; + }, + + compileXPathMatcher: function() { + var e = this.expression, ps = Selector.patterns, + x = Selector.xpath, le, m; + + if (Selector._cache[e]) { + this.xpath = Selector._cache[e]; return; + } + + this.matcher = ['.//*']; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + if (m = e.match(ps[i])) { + this.matcher.push(Object.isFunction(x[i]) ? x[i](m) : + new Template(x[i]).evaluate(m)); + e = e.replace(m[0], ''); + break; + } + } + } + + this.xpath = this.matcher.join(''); + Selector._cache[this.expression] = this.xpath; + }, + + findElements: function(root) { + root = root || document; + if (this.xpath) return document._getElementsByXPath(this.xpath, root); + return this.matcher(root); + }, + + match: function(element) { + this.tokens = []; + + var e = this.expression, ps = Selector.patterns, as = Selector.assertions; + var le, p, m; + + while (e && le !== e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + p = ps[i]; + if (m = e.match(p)) { + // use the Selector.assertions methods unless the selector + // is too complex. + if (as[i]) { + this.tokens.push([i, Object.clone(m)]); + e = e.replace(m[0], ''); + } else { + // reluctantly do a document-wide search + // and look for a match in the array + return this.findElements(document).include(element); + } + } + } + } + + var match = true, name, matches; + for (var i = 0, token; token = this.tokens[i]; i++) { + name = token[0], matches = token[1]; + if (!Selector.assertions[name](element, matches)) { + match = false; break; + } + } + + return match; + }, + + toString: function() { + return this.expression; + }, + + inspect: function() { + return "#<Selector:" + this.expression.inspect() + ">"; + } +}); + +Object.extend(Selector, { + _cache: { }, + + xpath: { + descendant: "//*", + child: "/*", + adjacent: "/following-sibling::*[1]", + laterSibling: '/following-sibling::*', + tagName: function(m) { + if (m[1] == '*') return ''; + return "[local-name()='" + m[1].toLowerCase() + + "' or local-name()='" + m[1].toUpperCase() + "']"; + }, + className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", + id: "[@id='#{1}']", + attrPresence: function(m) { + m[1] = m[1].toLowerCase(); + return new Template("[@#{1}]").evaluate(m); + }, + attr: function(m) { + m[1] = m[1].toLowerCase(); + m[3] = m[5] || m[6]; + return new Template(Selector.xpath.operators[m[2]]).evaluate(m); + }, + pseudo: function(m) { + var h = Selector.xpath.pseudos[m[1]]; + if (!h) return ''; + if (Object.isFunction(h)) return h(m); + return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); + }, + operators: { + '=': "[@#{1}='#{3}']", + '!=': "[@#{1}!='#{3}']", + '^=': "[starts-with(@#{1}, '#{3}')]", + '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", + '*=': "[contains(@#{1}, '#{3}')]", + '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", + '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" + }, + pseudos: { + 'first-child': '[not(preceding-sibling::*)]', + 'last-child': '[not(following-sibling::*)]', + 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', + 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", + 'checked': "[@checked]", + 'disabled': "[@disabled]", + 'enabled': "[not(@disabled)]", + 'not': function(m) { + var e = m[6], p = Selector.patterns, + x = Selector.xpath, le, v; + + var exclusion = []; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in p) { + if (m = e.match(p[i])) { + v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m); + exclusion.push("(" + v.substring(1, v.length - 1) + ")"); + e = e.replace(m[0], ''); + break; + } + } + } + return "[not(" + exclusion.join(" and ") + ")]"; + }, + 'nth-child': function(m) { + return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); + }, + 'nth-last-child': function(m) { + return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); + }, + 'nth-of-type': function(m) { + return Selector.xpath.pseudos.nth("position() ", m); + }, + 'nth-last-of-type': function(m) { + return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); + }, + 'first-of-type': function(m) { + m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); + }, + 'last-of-type': function(m) { + m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); + }, + 'only-of-type': function(m) { + var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); + }, + nth: function(fragment, m) { + var mm, formula = m[6], predicate; + if (formula == 'even') formula = '2n+0'; + if (formula == 'odd') formula = '2n+1'; + if (mm = formula.match(/^(\d+)$/)) // digit only + return '[' + fragment + "= " + mm[1] + ']'; + if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (mm[1] == "-") mm[1] = -1; + var a = mm[1] ? Number(mm[1]) : 1; + var b = mm[2] ? Number(mm[2]) : 0; + predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + + "((#{fragment} - #{b}) div #{a} >= 0)]"; + return new Template(predicate).evaluate({ + fragment: fragment, a: a, b: b }); + } + } + } + }, + + criteria: { + tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', + className: 'n = h.className(n, r, "#{1}", c); c = false;', + id: 'n = h.id(n, r, "#{1}", c); c = false;', + attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', + attr: function(m) { + m[3] = (m[5] || m[6]); + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); + }, + pseudo: function(m) { + if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); + return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); + }, + descendant: 'c = "descendant";', + child: 'c = "child";', + adjacent: 'c = "adjacent";', + laterSibling: 'c = "laterSibling";' + }, + + patterns: { + // combinators must be listed first + // (and descendant needs to be last combinator) + laterSibling: /^\s*~\s*/, + child: /^\s*>\s*/, + adjacent: /^\s*\+\s*/, + descendant: /^\s/, + + // selectors follow + tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, + id: /^#([\w\-\*]+)(\b|$)/, + className: /^\.([\w\-\*]+)(\b|$)/, + pseudo: +/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, + attrPresence: /^\[([\w]+)\]/, + attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ + }, + + // for Selector.match and Element#match + assertions: { + tagName: function(element, matches) { + return matches[1].toUpperCase() == element.tagName.toUpperCase(); + }, + + className: function(element, matches) { + return Element.hasClassName(element, matches[1]); + }, + + id: function(element, matches) { + return element.id === matches[1]; + }, + + attrPresence: function(element, matches) { + return Element.hasAttribute(element, matches[1]); + }, + + attr: function(element, matches) { + var nodeValue = Element.readAttribute(element, matches[1]); + return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); + } + }, + + handlers: { + // UTILITY FUNCTIONS + // joins two collections + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + a.push(node); + return a; + }, + + // marks an array of nodes for counting + mark: function(nodes) { + var _true = Prototype.emptyFunction; + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = _true; + return nodes; + }, + + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = undefined; + return nodes; + }, + + // mark each child node with its position (for nth calls) + // "ofType" flag indicates whether we're indexing for nth-of-type + // rather than nth-child + index: function(parentNode, reverse, ofType) { + parentNode._countedByPrototype = Prototype.emptyFunction; + if (reverse) { + for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { + var node = nodes[i]; + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; + } + } else { + for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; + } + }, + + // filters out duplicates and extends all nodes + unique: function(nodes) { + if (nodes.length == 0) return nodes; + var results = [], n; + for (var i = 0, l = nodes.length; i < l; i++) + if (!(n = nodes[i])._countedByPrototype) { + n._countedByPrototype = Prototype.emptyFunction; + results.push(Element.extend(n)); + } + return Selector.handlers.unmark(results); + }, + + // COMBINATOR FUNCTIONS + descendant: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + h.concat(results, node.getElementsByTagName('*')); + return results; + }, + + child: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) { + for (var j = 0, child; child = node.childNodes[j]; j++) + if (child.nodeType == 1 && child.tagName != '!') results.push(child); + } + return results; + }, + + adjacent: function(nodes) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + var next = this.nextElementSibling(node); + if (next) results.push(next); + } + return results; + }, + + laterSibling: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + h.concat(results, Element.nextSiblings(node)); + return results; + }, + + nextElementSibling: function(node) { + while (node = node.nextSibling) + if (node.nodeType == 1) return node; + return null; + }, + + previousElementSibling: function(node) { + while (node = node.previousSibling) + if (node.nodeType == 1) return node; + return null; + }, + + // TOKEN FUNCTIONS + tagName: function(nodes, root, tagName, combinator) { + var uTagName = tagName.toUpperCase(); + var results = [], h = Selector.handlers; + if (nodes) { + if (combinator) { + // fastlane for ordinary descendant combinators + if (combinator == "descendant") { + for (var i = 0, node; node = nodes[i]; i++) + h.concat(results, node.getElementsByTagName(tagName)); + return results; + } else nodes = this[combinator](nodes); + if (tagName == "*") return nodes; + } + for (var i = 0, node; node = nodes[i]; i++) + if (node.tagName.toUpperCase() === uTagName) results.push(node); + return results; + } else return root.getElementsByTagName(tagName); + }, + + id: function(nodes, root, id, combinator) { + var targetNode = $(id), h = Selector.handlers; + if (!targetNode) return []; + if (!nodes && root == document) return [targetNode]; + if (nodes) { + if (combinator) { + if (combinator == 'child') { + for (var i = 0, node; node = nodes[i]; i++) + if (targetNode.parentNode == node) return [targetNode]; + } else if (combinator == 'descendant') { + for (var i = 0, node; node = nodes[i]; i++) + if (Element.descendantOf(targetNode, node)) return [targetNode]; + } else if (combinator == 'adjacent') { + for (var i = 0, node; node = nodes[i]; i++) + if (Selector.handlers.previousElementSibling(targetNode) == node) + return [targetNode]; + } else nodes = h[combinator](nodes); + } + for (var i = 0, node; node = nodes[i]; i++) + if (node == targetNode) return [targetNode]; + return []; + } + return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; + }, + + className: function(nodes, root, className, combinator) { + if (nodes && combinator) nodes = this[combinator](nodes); + return Selector.handlers.byClassName(nodes, root, className); + }, + + byClassName: function(nodes, root, className) { + if (!nodes) nodes = Selector.handlers.descendant([root]); + var needle = ' ' + className + ' '; + for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { + nodeClassName = node.className; + if (nodeClassName.length == 0) continue; + if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) + results.push(node); + } + return results; + }, + + attrPresence: function(nodes, root, attr, combinator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); + var results = []; + for (var i = 0, node; node = nodes[i]; i++) + if (Element.hasAttribute(node, attr)) results.push(node); + return results; + }, + + attr: function(nodes, root, attr, value, operator, combinator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); + var handler = Selector.operators[operator], results = []; + for (var i = 0, node; node = nodes[i]; i++) { + var nodeValue = Element.readAttribute(node, attr); + if (nodeValue === null) continue; + if (handler(nodeValue, value)) results.push(node); + } + return results; + }, + + pseudo: function(nodes, name, value, root, combinator) { + if (nodes && combinator) nodes = this[combinator](nodes); + if (!nodes) nodes = root.getElementsByTagName("*"); + return Selector.pseudos[name](nodes, value, root); + } + }, + + pseudos: { + 'first-child': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + if (Selector.handlers.previousElementSibling(node)) continue; + results.push(node); + } + return results; + }, + 'last-child': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + if (Selector.handlers.nextElementSibling(node)) continue; + results.push(node); + } + return results; + }, + 'only-child': function(nodes, value, root) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) + results.push(node); + return results; + }, + 'nth-child': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root); + }, + 'nth-last-child': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, true); + }, + 'nth-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, false, true); + }, + 'nth-last-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, true, true); + }, + 'first-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, "1", root, false, true); + }, + 'last-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, "1", root, true, true); + }, + 'only-of-type': function(nodes, formula, root) { + var p = Selector.pseudos; + return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); + }, + + // handles the an+b logic + getIndices: function(a, b, total) { + if (a == 0) return b > 0 ? [b] : []; + return $R(1, total).inject([], function(memo, i) { + if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); + return memo; + }); + }, + + // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type + nth: function(nodes, formula, root, reverse, ofType) { + if (nodes.length == 0) return []; + if (formula == 'even') formula = '2n+0'; + if (formula == 'odd') formula = '2n+1'; + var h = Selector.handlers, results = [], indexed = [], m; + h.mark(nodes); + for (var i = 0, node; node = nodes[i]; i++) { + if (!node.parentNode._countedByPrototype) { + h.index(node.parentNode, reverse, ofType); + indexed.push(node.parentNode); + } + } + if (formula.match(/^\d+$/)) { // just a number + formula = Number(formula); + for (var i = 0, node; node = nodes[i]; i++) + if (node.nodeIndex == formula) results.push(node); + } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (m[1] == "-") m[1] = -1; + var a = m[1] ? Number(m[1]) : 1; + var b = m[2] ? Number(m[2]) : 0; + var indices = Selector.pseudos.getIndices(a, b, nodes.length); + for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { + for (var j = 0; j < l; j++) + if (node.nodeIndex == indices[j]) results.push(node); + } + } + h.unmark(nodes); + h.unmark(indexed); + return results; + }, + + 'empty': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + // IE treats comments as element nodes + if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; + results.push(node); + } + return results; + }, + + 'not': function(nodes, selector, root) { + var h = Selector.handlers, selectorType, m; + var exclusions = new Selector(selector).findElements(root); + h.mark(exclusions); + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!node._countedByPrototype) results.push(node); + h.unmark(exclusions); + return results; + }, + + 'enabled': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!node.disabled) results.push(node); + return results; + }, + + 'disabled': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (node.disabled) results.push(node); + return results; + }, + + 'checked': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (node.checked) results.push(node); + return results; + } + }, + + operators: { + '=': function(nv, v) { return nv == v; }, + '!=': function(nv, v) { return nv != v; }, + '^=': function(nv, v) { return nv.startsWith(v); }, + '$=': function(nv, v) { return nv.endsWith(v); }, + '*=': function(nv, v) { return nv.include(v); }, + '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, + '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } + }, + + split: function(expression) { + var expressions = []; + expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { + expressions.push(m[1].strip()); + }); + return expressions; + }, + + matchElements: function(elements, expression) { + var matches = $$(expression), h = Selector.handlers; + h.mark(matches); + for (var i = 0, results = [], element; element = elements[i]; i++) + if (element._countedByPrototype) results.push(element); + h.unmark(matches); + return results; + }, + + findElement: function(elements, expression, index) { + if (Object.isNumber(expression)) { + index = expression; expression = false; + } + return Selector.matchElements(elements, expression || '*')[index || 0]; + }, + + findChildElements: function(element, expressions) { + expressions = Selector.split(expressions.join(',')); + var results = [], h = Selector.handlers; + for (var i = 0, l = expressions.length, selector; i < l; i++) { + selector = new Selector(expressions[i].strip()); + h.concat(results, selector.findElements(element)); + } + return (l > 1) ? h.unique(results) : results; + } +}); + +if (Prototype.Browser.IE) { + Object.extend(Selector.handlers, { + // IE returns comment nodes on getElementsByTagName("*"). + // Filter them out. + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + if (node.tagName !== "!") a.push(node); + return a; + }, + + // IE improperly serializes _countedByPrototype in (inner|outer)HTML. + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node.removeAttribute('_countedByPrototype'); + return nodes; + } + }); +} + +function $$() { + return Selector.findChildElements(document, $A(arguments)); +} +var Form = { + reset: function(form) { + $(form).reset(); + return form; + }, + + serializeElements: function(elements, options) { + if (typeof options != 'object') options = { hash: !!options }; + else if (Object.isUndefined(options.hash)) options.hash = true; + var key, value, submitted = false, submit = options.submit; + + var data = elements.inject({ }, function(result, element) { + if (!element.disabled && element.name) { + key = element.name; value = $(element).getValue(); + if (value != null && (element.type != 'submit' || (!submitted && + submit !== false && (!submit || key == submit) && (submitted = true)))) { + if (key in result) { + // a key is already present; construct an array of values + if (!Object.isArray(result[key])) result[key] = [result[key]]; + result[key].push(value); + } + else result[key] = value; + } + } + return result; + }); + + return options.hash ? data : Object.toQueryString(data); + } +}; + +Form.Methods = { + serialize: function(form, options) { + return Form.serializeElements(Form.getElements(form), options); + }, + + getElements: function(form) { + return $A($(form).getElementsByTagName('*')).inject([], + function(elements, child) { + if (Form.Element.Serializers[child.tagName.toLowerCase()]) + elements.push(Element.extend(child)); + return elements; + } + ); + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) return $A(inputs).map(Element.extend); + + for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || (name && input.name != name)) + continue; + matchingInputs.push(Element.extend(input)); + } + + return matchingInputs; + }, + + disable: function(form) { + form = $(form); + Form.getElements(form).invoke('disable'); + return form; + }, + + enable: function(form) { + form = $(form); + Form.getElements(form).invoke('enable'); + return form; + }, + + findFirstElement: function(form) { + var elements = $(form).getElements().findAll(function(element) { + return 'hidden' != element.type && !element.disabled; + }); + var firstByIndex = elements.findAll(function(element) { + return element.hasAttribute('tabIndex') && element.tabIndex >= 0; + }).sortBy(function(element) { return element.tabIndex }).first(); + + return firstByIndex ? firstByIndex : elements.find(function(element) { + return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + + focusFirstElement: function(form) { + form = $(form); + form.findFirstElement().activate(); + return form; + }, + + request: function(form, options) { + form = $(form), options = Object.clone(options || { }); + + var params = options.parameters, action = form.readAttribute('action') || ''; + if (action.blank()) action = window.location.href; + options.parameters = form.serialize(true); + + if (params) { + if (Object.isString(params)) params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(action, options); + } +}; + +/*--------------------------------------------------------------------------*/ + +Form.Element = { + focus: function(element) { + $(element).focus(); + return element; + }, + + select: function(element) { + $(element).select(); + return element; + } +}; + +Form.Element.Methods = { + serialize: function(element) { + element = $(element); + if (!element.disabled && element.name) { + var value = element.getValue(); + if (value != undefined) { + var pair = { }; + pair[element.name] = value; + return Object.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + setValue: function(element, value) { + element = $(element); + var method = element.tagName.toLowerCase(); + Form.Element.Serializers[method](element, value); + return element; + }, + + clear: function(element) { + $(element).value = ''; + return element; + }, + + present: function(element) { + return $(element).value != ''; + }, + + activate: function(element) { + element = $(element); + try { + element.focus(); + if (element.select && (element.tagName.toLowerCase() != 'input' || + !['button', 'reset', 'submit'].include(element.type))) + element.select(); + } catch (e) { } + return element; + }, + + disable: function(element) { + element = $(element); + element.blur(); + element.disabled = true; + return element; + }, + + enable: function(element) { + element = $(element); + element.disabled = false; + return element; + } +}; + +/*--------------------------------------------------------------------------*/ + +var Field = Form.Element; +var $F = Form.Element.Methods.getValue; + +/*--------------------------------------------------------------------------*/ + +Form.Element.Serializers = { + input: function(element, value) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element, value); + default: + return Form.Element.Serializers.textarea(element, value); + } + }, + + inputSelector: function(element, value) { + if (Object.isUndefined(value)) return element.checked ? element.value : null; + else element.checked = !!value; + }, + + textarea: function(element, value) { + if (Object.isUndefined(value)) return element.value; + else element.value = value; + }, + + select: function(element, index) { + if (Object.isUndefined(index)) + return this[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + else { + var opt, value, single = !Object.isArray(index); + for (var i = 0, length = element.length; i < length; i++) { + opt = element.options[i]; + value = this.optionValue(opt); + if (single) { + if (value == index) { + opt.selected = true; + return; + } + } + else opt.selected = index.include(value); + } + } + }, + + selectOne: function(element) { + var index = element.selectedIndex; + return index >= 0 ? this.optionValue(element.options[index]) : null; + }, + + selectMany: function(element) { + var values, length = element.length; + if (!length) return null; + + for (var i = 0, values = []; i < length; i++) { + var opt = element.options[i]; + if (opt.selected) values.push(this.optionValue(opt)); + } + return values; + }, + + optionValue: function(opt) { + // extend element because hasAttribute may not be native + return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; + } +}; + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = Class.create(PeriodicalExecuter, { + initialize: function($super, element, frequency, callback) { + $super(callback, frequency); + this.element = $(element); + this.lastValue = this.getValue(); + }, + + execute: function() { + var value = this.getValue(); + if (Object.isString(this.lastValue) && Object.isString(value) ? + this.lastValue != value : String(this.lastValue) != String(value)) { + this.callback(this.element, value); + this.lastValue = value; + } + } +}); + +Form.Element.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = Class.create({ + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + Form.getElements(this.element).each(this.registerCallback, this); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + default: + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +}); + +Form.Element.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) var Event = { }; + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + KEY_HOME: 36, + KEY_END: 35, + KEY_PAGEUP: 33, + KEY_PAGEDOWN: 34, + KEY_INSERT: 45, + + cache: { }, + + relatedTarget: function(event) { + var element; + switch(event.type) { + case 'mouseover': element = event.fromElement; break; + case 'mouseout': element = event.toElement; break; + default: return null; + } + return Element.extend(element); + } +}); + +Event.Methods = (function() { + var isButton; + + if (Prototype.Browser.IE) { + var buttonMap = { 0: 1, 1: 4, 2: 2 }; + isButton = function(event, code) { + return event.button == buttonMap[code]; + }; + + } else if (Prototype.Browser.WebKit) { + isButton = function(event, code) { + switch (code) { + case 0: return event.which == 1 && !event.metaKey; + case 1: return event.which == 1 && event.metaKey; + default: return false; + } + }; + + } else { + isButton = function(event, code) { + return event.which ? (event.which === code + 1) : (event.button === code); + }; + } + + return { + isLeftClick: function(event) { return isButton(event, 0) }, + isMiddleClick: function(event) { return isButton(event, 1) }, + isRightClick: function(event) { return isButton(event, 2) }, + + element: function(event) { + var node = Event.extend(event).target; + return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node); + }, + + findElement: function(event, expression) { + var element = Event.element(event); + if (!expression) return element; + var elements = [element].concat(element.ancestors()); + return Selector.findElement(elements, expression, 0); + }, + + pointer: function(event) { + return { + x: event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)), + y: event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)) + }; + }, + + pointerX: function(event) { return Event.pointer(event).x }, + pointerY: function(event) { return Event.pointer(event).y }, + + stop: function(event) { + Event.extend(event); + event.preventDefault(); + event.stopPropagation(); + event.stopped = true; + } + }; +})(); + +Event.extend = (function() { + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { + m[name] = Event.Methods[name].methodize(); + return m; + }); + + if (Prototype.Browser.IE) { + Object.extend(methods, { + stopPropagation: function() { this.cancelBubble = true }, + preventDefault: function() { this.returnValue = false }, + inspect: function() { return "[object Event]" } + }); + + return function(event) { + if (!event) return false; + if (event._extendedByPrototype) return event; + + event._extendedByPrototype = Prototype.emptyFunction; + var pointer = Event.pointer(event); + Object.extend(event, { + target: event.srcElement, + relatedTarget: Event.relatedTarget(event), + pageX: pointer.x, + pageY: pointer.y + }); + return Object.extend(event, methods); + }; + + } else { + Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__; + Object.extend(Event.prototype, methods); + return Prototype.K; + } +})(); + +Object.extend(Event, (function() { + var cache = Event.cache; + + function getEventID(element) { + if (element._prototypeEventID) return element._prototypeEventID[0]; + arguments.callee.id = arguments.callee.id || 1; + return element._prototypeEventID = [++arguments.callee.id]; + } + + function getDOMEventName(eventName) { + if (eventName && eventName.include(':')) return "dataavailable"; + return eventName; + } + + function getCacheForID(id) { + return cache[id] = cache[id] || { }; + } + + function getWrappersForEventName(id, eventName) { + var c = getCacheForID(id); + return c[eventName] = c[eventName] || []; + } + + function createWrapper(element, eventName, handler) { + var id = getEventID(element); + var c = getWrappersForEventName(id, eventName); + if (c.pluck("handler").include(handler)) return false; + + var wrapper = function(event) { + if (!Event || !Event.extend || + (event.eventName && event.eventName != eventName)) + return false; + + Event.extend(event); + handler.call(element, event); + }; + + wrapper.handler = handler; + c.push(wrapper); + return wrapper; + } + + function findWrapper(id, eventName, handler) { + var c = getWrappersForEventName(id, eventName); + return c.find(function(wrapper) { return wrapper.handler == handler }); + } + + function destroyWrapper(id, eventName, handler) { + var c = getCacheForID(id); + if (!c[eventName]) return false; + c[eventName] = c[eventName].without(findWrapper(id, eventName, handler)); + } + + function destroyCache() { + for (var id in cache) + for (var eventName in cache[id]) + cache[id][eventName] = null; + } + + if (window.attachEvent) { + window.attachEvent("onunload", destroyCache); + } + + return { + observe: function(element, eventName, handler) { + element = $(element); + var name = getDOMEventName(eventName); + + var wrapper = createWrapper(element, eventName, handler); + if (!wrapper) return element; + + if (element.addEventListener) { + element.addEventListener(name, wrapper, false); + } else { + element.attachEvent("on" + name, wrapper); + } + + return element; + }, + + stopObserving: function(element, eventName, handler) { + element = $(element); + var id = getEventID(element), name = getDOMEventName(eventName); + + if (!handler && eventName) { + getWrappersForEventName(id, eventName).each(function(wrapper) { + element.stopObserving(eventName, wrapper.handler); + }); + return element; + + } else if (!eventName) { + Object.keys(getCacheForID(id)).each(function(eventName) { + element.stopObserving(eventName); + }); + return element; + } + + var wrapper = findWrapper(id, eventName, handler); + if (!wrapper) return element; + + if (element.removeEventListener) { + element.removeEventListener(name, wrapper, false); + } else { + element.detachEvent("on" + name, wrapper); + } + + destroyWrapper(id, eventName, handler); + + return element; + }, + + fire: function(element, eventName, memo) { + element = $(element); + if (element == document && document.createEvent && !element.dispatchEvent) + element = document.documentElement; + + var event; + if (document.createEvent) { + event = document.createEvent("HTMLEvents"); + event.initEvent("dataavailable", true, true); + } else { + event = document.createEventObject(); + event.eventType = "ondataavailable"; + } + + event.eventName = eventName; + event.memo = memo || { }; + + if (document.createEvent) { + element.dispatchEvent(event); + } else { + element.fireEvent(event.eventType, event); + } + + return Event.extend(event); + } + }; +})()); + +Object.extend(Event, Event.Methods); + +Element.addMethods({ + fire: Event.fire, + observe: Event.observe, + stopObserving: Event.stopObserving +}); + +Object.extend(document, { + fire: Element.Methods.fire.methodize(), + observe: Element.Methods.observe.methodize(), + stopObserving: Element.Methods.stopObserving.methodize(), + loaded: false +}); + +(function() { + /* Support for the DOMContentLoaded event is based on work by Dan Webb, + Matthias Miller, Dean Edwards and John Resig. */ + + var timer; + + function fireContentLoadedEvent() { + if (document.loaded) return; + if (timer) window.clearInterval(timer); + document.fire("dom:loaded"); + document.loaded = true; + } + + if (document.addEventListener) { + if (Prototype.Browser.WebKit) { + timer = window.setInterval(function() { + if (/loaded|complete/.test(document.readyState)) + fireContentLoadedEvent(); + }, 0); + + Event.observe(window, "load", fireContentLoadedEvent); + + } else { + document.addEventListener("DOMContentLoaded", + fireContentLoadedEvent, false); + } + + } else { + document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>"); + $("__onDOMContentLoaded").onreadystatechange = function() { + if (this.readyState == "complete") { + this.onreadystatechange = null; + fireContentLoadedEvent(); + } + }; + } +})(); +/*------------------------------- DEPRECATED -------------------------------*/ + +Hash.toQueryString = Object.toQueryString; + +var Toggle = { display: Element.toggle }; + +Element.Methods.childOf = Element.Methods.descendantOf; + +var Insertion = { + Before: function(element, content) { + return Element.insert(element, {before:content}); + }, + + Top: function(element, content) { + return Element.insert(element, {top:content}); + }, + + Bottom: function(element, content) { + return Element.insert(element, {bottom:content}); + }, + + After: function(element, content) { + return Element.insert(element, {after:content}); + } +}; + +var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); + +// This should be moved to script.aculo.us; notice the deprecated methods +// further below, that map to the newer Element methods. +var Position = { + // set to true if needed, warning: firefox performance problems + // NOT neeeded for page scrolling, only if draggable contained in + // scrollable elements + includeScrollOffsets: false, + + // must be called before calling withinIncludingScrolloffset, every time the + // page is scrolled + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + // caches x/y coordinate pair to use with overlap + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = Element.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = Element.cumulativeScrollOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = Element.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + // within must be called directly before + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + // Deprecation layer -- use newer Element methods now (1.5.2). + + cumulativeOffset: Element.Methods.cumulativeOffset, + + positionedOffset: Element.Methods.positionedOffset, + + absolutize: function(element) { + Position.prepare(); + return Element.absolutize(element); + }, + + relativize: function(element) { + Position.prepare(); + return Element.relativize(element); + }, + + realOffset: Element.Methods.cumulativeScrollOffset, + + offsetParent: Element.Methods.getOffsetParent, + + page: Element.Methods.viewportOffset, + + clone: function(source, target, options) { + options = options || { }; + return Element.clonePosition(target, source, options); + } +}; + +/*--------------------------------------------------------------------------*/ + +if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ + function iter(name) { + return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; + } + + instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? + function(element, className) { + className = className.toString().strip(); + var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); + return cond ? document._getElementsByXPath('.//*' + cond, element) : []; + } : function(element, className) { + className = className.toString().strip(); + var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); + if (!classNames && !className) return elements; + + var nodes = $(element).getElementsByTagName('*'); + className = ' ' + className + ' '; + + for (var i = 0, child, cn; child = nodes[i]; i++) { + if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || + (classNames && classNames.all(function(name) { + return !name.toString().blank() && cn.include(' ' + name + ' '); + })))) + elements.push(Element.extend(child)); + } + return elements; + }; + + return function(className, parentElement) { + return $(parentElement || document.body).getElementsByClassName(className); + }; +}(Element.Methods); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set($A(this).concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set($A(this).without(classNameToRemove).join(' ')); + }, + + toString: function() { + return $A(this).join(' '); + } +}; + +Object.extend(Element.ClassNames.prototype, Enumerable); + +/*--------------------------------------------------------------------------*/ + +Element.addMethods();
\ No newline at end of file |