From 2099f898f1e86e30fac385d063733a34b8394089 Mon Sep 17 00:00:00 2001 From: Lusito Date: Sat, 17 Aug 2024 11:34:26 +0000 Subject: [PATCH] deploy: cf5ee29ba29d151ae32221b75996b26e55623f50 --- all-pages.html | 2 +- search-data.json | 2 +- sitemap.xml | 2 +- testbed/index.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/all-pages.html b/all-pages.html index 7a88fd40..0974bf9f 100644 --- a/all-pages.html +++ b/all-pages.html @@ -1 +1 @@ -All Pages - @box2d

Search results:

    Copyright © 2022 Santo Pfingsten
    \ No newline at end of file +All Pages - @box2d

    Search results:

      Copyright © 2022 Santo Pfingsten
      \ No newline at end of file diff --git a/search-data.json b/search-data.json index 42c125a9..ca91b749 100644 --- a/search-data.json +++ b/search-data.json @@ -1 +1 @@ -[{"url":"https://lusito.github.io/box2d.ts/","content":"@box2d Monorepository\n\n\n\n\n\nWork in Progress of a full Box2D ecosystem for the web.\n\nThis project is kept in sync with the original Box2D project (using a special tool to easily compare differences to upstream)!\nYou only need to install what you actually want. Don't need particles or 2D lights? Then just install the core.\nCheck out the demos and the benchmarks\n\nIncluded Libraries:\n\n@box2d/core, a TypeScript port of Box2D\n@box2d/controllers, a TypeScript port of LiquidFun's controllers\n@box2d/lights, a TypeScript port of Box2D Lights\n@box2d/particles, a TypeScript port of LiquidFun's particles\n@box2d/debug-draw, a TypeScript port Box2D's debug drawing helper\n\nQuick Start\nThis monorepo is in it's early stage, so to get started, you'll have to taka a look at the testbed project for now.\nMonorepo Commands:\nMost important commands to execute from the root folder (you need npm 18 installed):\n\nnpm ci -> install dependencies\nnpm run build -> build all projects\nnpm run build:libs -> build only the libraries\nnpm run build:testbed -> build the testbed\nnpm run credit "<username>" <type> -> Add user to all contributors list. Use quotes, as otherwise wrong people get added.\nnpm run start -> Run testbed locally\nnpm run bench -> Run the benchmark using node.js\nnpm run bench:web -> Start a webserver for running the benchmarks using a browser,\nnpm run lint -> Run linters, formatters, etc.\nnpm run lint:fix -> Run linters, formatters, etc. and autofix if possible\nnpm run release -> Release one of the libraries\n\nThe @box2d Ecosystem\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js\nCheck out demos and compare performance here: https://lusito.github.io/box2d.ts/\nFair Warning: The whole @box2d ecosystem is in an early stage, so it will probably change a lot before we release the first stable version (1.0.0).\nOther packages included in the ecosystem:\n\nBenchmark: Based on bench2d by joelgwebber\nControllers: From the LiquidFun project\nParticles: Also from the LiquidFun project\nLights: ported from LibGDX\nDebugDraw: Debug drawing using a canvas\nTestbed: A set of demos, partially ports of the original projects, partially new ones.\n\nContributing\nWe're looking for contributors to make this the best place to start with box2d on the web.\nCheck out the project page for more information: https://github.com/Lusito/box2d.ts\nContributors ✨\nThanks goes to these wonderful people (emoji key):\n\n\n\n\n\n \n Erin Catto💻\n Isaac Burns💻 📦\n Maxime Veber💻\n finscn💻\n lusito💻 🚧\n Daniel Zhang🤔\n \n\n\n\n\n\nThis project follows the all-contributors specification. Contributions of any kind welcome!\n","title":"@box2d Monorepository"},{"url":"https://lusito.github.io/box2d.ts/core/","content":"@box2d/core\nBox2D is a 2D physics engine for games.\n@box2d/core is a TypeScript port of Erin Cattos Box2D.\nThis is a fork of box2d.ts from Isaac Burns (flyover) who did a huge job initially porting Box2D and LiquidFun to TypeScript.\nHow to Use\nCheck out the documentation\nThe @box2d Ecosystem\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js\nCheck out demos and compare performance here: https://lusito.github.io/box2d.ts/\nFair Warning: The whole @box2d ecosystem is in an early stage, so it will probably change a lot before we release the first stable version (1.0.0).\nOther packages included in the ecosystem:\n\nBenchmark: Based on bench2d by joelgwebber\nControllers: From the LiquidFun project\nParticles: Also from the LiquidFun project\nLights: ported from LibGDX\nDebugDraw: Debug drawing using a canvas\nTestbed: A set of demos, partially ports of the original projects, partially new ones.\n\nContributing\nWe're looking for contributors to make this the best place to start with box2d on the web.\nCheck out the project page for more information: https://github.com/Lusito/box2d.ts\n","title":"core","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/faq.html","content":"FAQ\nWhat Is Box2D?\nBox2D is a feature rich 2D rigid body physics engine, written in C++ by Erin Catto. It has been used in many games, including Crayon Physics Deluxe, winner of the 2008 Independant Game Festival Grand Prize.\nBox2D uses the MIT license license and can be used free of charge.\nWhat Is @box2d?\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js.\nIt includes more libraries to enable light rendering, liquid simulation and more.\n@box2d/core represents the original Box2D part.\nWho Makes It?\nErin Catto is the driving force behind Box2D, with various others supporting the ports. Box2D is an open source project, and accepts community feedback.\nSanto Pfingsten is the author of @box2d, building on previous work by from Isaac Burns (flyover).\n@box2d is also an open source project, partially on different licenses (depending on which part). This was not an active choice,\nbut was due to the fact, that other libraries, which have been ported to TypeScript have been released under a different license.\nCheck out each package for their license to see what is being used. @box2d/core for example uses the original MIT license from Box2D.\nHow Do I Get Help?\nYou should read the documentation and the rest of this FAQ first. Also, you should study the examples included in the source distribution. Then you can visit the subreddit to ask any remaining questions.\nPlease to not PM or email Erin Catto for support. It is best to ask questions in the forum so that everyone can benefit from the discussion.\nDocumentation\nWhy Isn't Feature Foo Documented?\nIf you grab the latest code from the git master branch you will likely find features that are not documented in the manual. New features are added to the manual after they are mature and a new point release is imminent. However, all major features added to Box2D are accompanied by example code in the testbed to test the feature and show the intended usage.\nPrerequisites\nProgramming\nYou should have a working knowledge of TypeScript or at least JavaScript before you use Box2D. You should understand classes, inheritance, references, and the implications of garbage collection. There are plenty of resources on the web for learning TypeScript/JavaScript. You should also understand your development environment: bundling, the dev-server, and debugging.\nMath and Physics\nYou should have a basic knowledge of rigid bodies, force, torque, and impulses. If you come across a math or physics concept you don't understand, please read about it on Wikipedia. Visit this page if you want a deeper knowledge of the algorithms used in Box2D.\nAPI\nWhat Units Does Box2D Use?\nBox2D is tuned for meters-kilograms-seconds (MKS). Your moving objects should be between 0.1 - 10 meters. Do not use pixels as units! You will get a jittery simulation.\nHow Do I Convert Pixels to Meters?\nSuppose you have a sprite for a character that is 100x100 pixels. You decide to use a scaling factor that is 0.01. This will make the character physics box 1m x 1m. So go make a physics box that is 1x1. Now suppose the character starts out at pixel coordinate (345,679). So position the physics box at (3.45,6.79). Now simulate the physics world. Suppose the character physics box moves to (2.31,4.98), so move your character sprite to pixel coordinates (231,498).\nNow the only tricky part is choosing a scaling factor. This really depends on your game. You should try to get your moving objects in the range 0.1 - 10 meters, with 1 meter being the sweet spot.\nCan I Use @box2d With Just JavaScript\nLike most npm packages written in TypeScript, @box2d is transpiled to JavaScript + types, so you can use it with JavaScript. You will get a better experience with TypeScript though.\nRendering\nWhat Are Box2D's Rendering Capabilities?\nBox2D is only a physics engine. How you draw stuff is up to you.\nBut The Testbed Draws Stuff?\nVisualization is very important for debugging collision and physics. I wrote the test bed to help me test Box2D and give you examples of how to use Box2D. The TestBed is not part of the Box2D library.\nHow Do I Draw Shapes?\nDrawing shapes is not supported and shape internal data is likely to change. Instead you should implement the b2Draw interface.\nYou can use the @core/debug-draw library for simple debugging on a canvas or use it as a reference for your own renderer.\nAccuracy\nBox2D uses approximate methods for a few reasons.\n\nPerformance\nSome differential equations don't have known solutions\nSome constraints cannot be determined uniquely\n\nWhat this means is that constraints are not perfectly rigid and sometimes you will see some bounce even when the restitution is zero.\nBox2D uses Gauss-Seidel to approximately solve constraints.\nBox2D also uses Semi-implicit Euler to approximately solve the differential equations.\nBox2D also does not have exact collision. Polygons are covered with a thin skin (around 0.5cm thick) to avoid numerical problems. This can sometimes lead to unexpected contact normals. Also, some shapes may begin to overlap and then be pushed apart by the solver.\nMaking Games\nWorms Clones\nMaking a worms clone requires arbitrarily destructible terrain. This is beyond the scope of Box2D, so you will have to figure out how to do this on your own.\nTile Based Environment\nUsing many boxes for your terrain may not work well because box-like characters can get snagged on internal corners. A future update to Box2D should allow for smooth motion over edge chains. In general you should avoid using a rectangular character because collision tolerances will still lead to undesirable snagging.\nAsteroid Type Coordinate Systems\nBox2D does not have any support for coordinate frame wrapping. You would likely need to customize Box2D for this purpose. You may need to use a different broad-phase for this to work.\nDeterminism\nIs Box2D Deterministic?\nFor the same input, and same browser, @box2d will reproduce any simulation. Box2D does not use any random numbers nor base any computation on random events (such as timers, etc).\nHowever, people often want more stringent determinism. People often want to know if Box2D can produce identical results on different browsers and on different platforms. The answer is no. The reason for this answer has to do with how floating point math is implemented in many compilers and processors. I recommend reading this article if you are curious: http://www.yosefk.com/blog/consistency-how-to-defeat-the-purpose-of-ieee-floating-point.html\nBut I Really Want Determinism\nThis naturally leads to the question of fixed-point math. Box2D does not support fixed-point math. In the past Box2D was ported to the NDS in fixed-point and apparently it worked okay. Fixed-point math is slower and more tedious to develop, so I have chosen not to use fixed-point for the development of Box2D.\nWhy Is The Restitution/Friction Mixing Inaccurate?\nA physically correct restitution value must be measured in experiments. But as soon as you change the geometry from the experiment then the value is wrong. Next, adding simultaneous collision makes the answer worse. We've been down this road before.\nSo the question of accuracy has been answered: failure.\nThe only remaining question is how do we make it convenient. On this opinions may vary.\nb2Settings is just that. Settings you can adjust if you know what you are doing. See Settings for more details.\nWhat Are The Biggest Mistakes Made By New Users?\n\nUsing pixels for length instead of meters.\nExpecting Box2D to give pixel perfect results.\nUsing b2Polygon to create concave polygons.\nNot learning TypeScript/JavaScript before using Box2D.\nNot reading this FAQ. :)\n\n","title":"FAQ","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/collision.html","content":"Collision Module\nThe Collision module contains shapes and functions that operate on them.\nThe module also contains a dynamic tree and broad-phase to acceleration\ncollision processing of large systems.\nThe collision module is designed to be usable outside of the dynamic\nsystem. For example, you can use the dynamic tree for other aspects of\nyour game besides physics.\nHowever, the main purpose of Box2D is to provide a rigid body physics\nengine, so the using the collision module by itself may feel limited for\nsome applications. Likewise, I will not make a strong effort to document\nit or polish the APIs.\nShapes\nShapes describe collision geometry and may be used independently of\nphysics simulation. At a minimum, you should understand how to create\nshapes that can be later attached to rigid bodies.\nBox2D shapes implement the b2Shape base class. The base class defines\nfunctions to:\n\nTest a point for overlap with the shape.\nPerform a ray cast against the shape.\nCompute the shape's AABB.\nCompute the mass properties of the shape.\n\nIn addition, each shape has a type member and a radius. The radius even\napplies to polygons, as discussed below.\nKeep in mind that a shape does not know about bodies and stand apart\nfrom the dynamics system. Shapes are stored in a compact form that is\noptimized for size and performance. As such, shapes are not easily moved\naround. You have to manually set the shape vertex positions to move a\nshape. However, when a shape is attached to a body using a fixture, the\nshapes move rigidly with the host body. In summary:\n\nWhen a shape is not attached to a body, you can view it's vertices as being expressed in world-space.\nWhen a shape is attached to a body, you can view it's vertices as being expressed in local coordinates.\n\nCircle Shapes\nCircle shapes have a position and radius. Circles are solid. You cannot\nmake a hollow circle using the circle shape.\nconst circle = new b2CircleShape();\nconst radius = 0.5;\ncircle.Set({ x: 2, y: 3 }, radius);\n\nPolygon Shapes\nPolygon shapes are solid convex polygons. A polygon is convex when all\nline segments connecting two points in the interior do not cross any\nedge of the polygon. Polygons are solid and never hollow. A polygon must\nhave 3 or more vertices.\n\nPolygons vertices are stored with a counter clockwise winding (CCW). We\nmust be careful because the notion of CCW is with respect to a\nright-handed coordinate system with the z-axis pointing out of the\nplane. This might turn out to be clockwise on your screen, depending on\nyour coordinate system conventions.\n\nThe polygon members are public, but you should use initialization\nfunctions to create a polygon. The initialization functions create\nnormal vectors and perform validation.\nYou can create a polygon shape by passing in a vertex array. The maximal\nsize of the array is controlled by b2_maxPolygonVertices which has a\ndefault value of 8. This is sufficient to describe most convex polygons.\nThe b2PolygonShape::Set function automatically computes the convex hull\nand establishes the proper winding order. This function is fast when the\nnumber of vertices is low. If you increase b2_maxPolygonVertices, then\nthe convex hull computation might become slow. Also note that the convex\nhull function may eliminate and/or re-order the points you provide.\nVertices that are closer than b2_linearSlop may be merged.\n// This defines a triangle in CCW order.\nconst vertices: XY[] = [\n { x: 0, y: 0 },\n { x: 1, y: 0 },\n { x: 0, y: 1 },\n];\n\nconst polygon = new b2PolygonShape();\npolygon.Set(vertices, vertices.length);\n\nThe polygon shape has a convenience function to create boxes.\n// on b2PolygonShape\npublic SetAsBox(hx: number, hy: number, center?: XY, angle = 0): b2PolygonShape;\n\nPolygons inherit a radius from b2Shape. The radius creates a skin around\nthe polygon. The skin is used in stacking scenarios to keep polygons\nslightly separated. This allows continuous collision to work against the\ncore polygon.\n\nThe polygon skin helps prevent tunneling by keeping the polygons\nseparated. This results in small gaps between the shapes. Your visual\nrepresentation can be larger than the polygon to hide any gaps.\n\nNote that polygon skin is only provided to help with continuous collision.\nThe purpose is not to simulate rounded polygons.\nEdge Shapes\nEdge shapes are line segments. These are provided to assist in making a\nfree-form static environment for your game. A major limitation of edge\nshapes is that they can collide with circles and polygons but not with\nthemselves. The collision algorithms used by Box2D require that at least\none of two colliding shapes have volume. Edge shapes have no volume, so\nedge-edge collision is not possible.\n// This an edge shape.\nconst v1: XY = { x: 0, y: 0 };\nconst v2: XY = { x: 1, y: 0 };\n\nconst edge = new b2EdgeShape();\nedge.SetTwoSided(v1, v2);\n\nIn many cases a game environment is constructed by connecting several\nedge shapes end-to-end. This can give rise to an unexpected artifact\nwhen a polygon slides along the chain of edges. In the figure below we\nsee a box colliding with an internal vertex. These ghost collisions\nare caused when the polygon collides with an internal vertex generating\nan internal collision normal.\n\nIf edge1 did not exist this collision would seem fine. With edge1\npresent, the internal collision seems like a bug. But normally when\nBox2D collides two shapes, it views them in isolation.\nFortunately, the edge shape provides a mechanism for eliminating ghost\ncollisions by storing the adjacent ghost vertices. Box2D uses these\nghost vertices to prevent internal collisions.\n\nThe Box2D algorithm for dealing with ghost collisions only supports\none-sided collision. The front face is to the right when looking from the first\nvertex towards the second vertex. This matches the CCW winding order\nused by polygons.\n// This is an edge shape with ghost vertices.\nconst v0: XY = { x: 1.7, y: 0 };\nconst v1: XY = { x: 1, y: 0.25 };\nconst v2: XY = { x: 0, y: 0 };\nconst v3: XY = { x: -1.7, y: 0.4 };\n\nconst edge = new b2EdgeShape();\nedge.SetOneSided(v0, v1, v2, v3);\n\nIn general stitching edges together this way is a bit wasteful and\ntedious. This brings us to chain shapes.\nChain Shapes\nThe chain shape provides an efficient way to connect many edges together\nto construct your static game worlds. Chain shapes automatically\neliminate ghost collisions and provide one-sided collision. The collision is\none-sided to eliminate ghost collisions.\nIf you don't care about ghost collisions, you can just create a bunch of\ntwo-sided edge shapes. The efficiency is similar.\nThe simplest way to use chain shapes is to create loops. Simply provide an\narray of vertices.\nconst vs: XY[] = [\n { x: 1.7, y: 0 },\n { x: 1, y: 0.25 },\n { x: 0, y: 0 },\n { x: -1.7, y: 0.4 },\n];\n\nconst chain = new b2ChainShape();\nchain.CreateLoop(vs, vs.length);\n\nThe edge normal depends on the winding order. A counter-clockwise winding order orients the normal outwards and a clockwise winding order orients the normal inwards.\n\n\nYou may have a scrolling game world and would like to connect several chains together.\nYou can connect chains together using ghost vertices, like we did with b2EdgeShape.\n\n// on b2ChainShape\npublic CreateChain(\n vertices: XY[],\n count: number,\n prevVertex: Readonly<XY>,\n nextVertex: Readonly<XY>,\n ): b2ChainShape {\n\nSelf-intersection of chain shapes is not supported. It might work, it\nmight not. The code that prevents ghost collisions assumes there are no\nself-intersections of the chain. Also, very close vertices can cause\nproblems. Make sure all your edges are longer than b2_linearSlop (5mm).\n\nEach edge in the chain is treated as a child shape and can be accessed\nby index. When a chain shape is connected to a body, each edge gets its\nown bounding box in the broad-phase collision tree.\n// Visit each child edge.\nfor (let i = 0; i < chain.GetChildCount(); ++i) {\n const edge = new b2EdgeShape();\n chain.GetChildEdge(edge, i);\n\n ...\n}\n\nGeometric Queries\nYou can perform a couple geometric queries on a single shape.\nShape Point Test\nYou can test a point for overlap with a shape. You provide a transform\nfor the shape and a world point.\nconst transform = new b2Transform();\ntransform.SetIdentity();\nconst point: XY = { x: 5, 2 };\n\nconst hit = shape.TestPoint(transform, point);\n\nEdge and chain shapes always return false, even if the chain is a loop.\nShape Ray Cast\nYou can cast a ray at a shape to get the point of first intersection and normal vector. A child index is included for chain shapes because the ray cast will only check a single edge at a time.\n\nCaution:\nNo hit will register if the ray starts inside a convex shape like a circle or polygon. This is consistent with Box2D treating convex shapes as solid.\n\nconst transform = new b2Transfrom();\ntransform.SetIdentity();\n\nconst input = new b2RayCastInput();\ninput.p1.Set(0, 0);\ninput.p2.Set(1, 0);\ninput.maxFraction = 1;\nconst childIndex = 0;\n\nconst output = new b2RayCastOutput();\nconst hit = shape.RayCast(output, input, transform, childIndex);\n\nif (hit) {\n const hitPoint = input.p1.Clone().AddScaled(output.fraction, Math.Subtract(input.p2, input.p1, new b2Vec2()));\n // Keep in mind, that the above line might be bad for garbage collection. Avoid Clone() and new b2Vec2() for code that runs often!\n ...\n}\n\nPairwise Functions\nThe Collision module contains functions that take a pair of shapes and compute some results. These include:\n\nOverlap\nContact manifolds\nDistance\nTime of impact\n\nOverlap\nYou can test two shapes for overlap using this function:\nconst xfA: b2Transform = ..., xfB: b2Transform = ...;\nconst overlap = b2TestOverlap(shapeA, indexA, shapeB, indexB, xfA, xfB);\n\nAgain you must provide child indices to for the case of chain shapes.\nContact Manifolds\nBox2D has functions to compute contact points for overlapping shapes. If\nwe consider circle-circle or circle-polygon, we can only get one contact\npoint and normal. In the case of polygon-polygon we can get two points.\nThese points share the same normal vector so Box2D groups them into a\nmanifold class. The contact solver takes advantage of this to\nimprove stacking stability.\n\nNormally you don't need to compute contact manifolds directly, however\nyou will likely use the results produced in the simulation.\nThe b2Manifold class holds a normal vector and up to two contact\npoints. The normal and points are held in local coordinates. As a\nconvenience for the contact solver, each point stores the normal and\ntangential (friction) impulses.\nThe data stored in b2Manifold is optimized for internal use. If you need\nthis data, it is usually best to use the b2WorldManifold class to\ngenerate the world coordinates of the contact normal and points. You\nneed to provide a b2Manifold and the shape transforms and radii.\nconst worldManifold = new b2WorldManifold();\nworldManifold.Initialize(manifold, transformA, shapeA.m_radius,\ntransformB, shapeB.m_radius);\n\nfor (let i = 0; i < manifold.pointCount; ++i) {\n const point = worldManifold.points[i];\n ...\n}\n\nNotice that the world manifold uses the point count from the original\nmanifold.\nDuring simulation shapes may move and the manifolds may change. Points\nmay be added or removed. You can detect this using b2GetPointStates.\nconst state1: b2PointState[] = [];\nconst state2: b2PointState[] = [];\nb2GetPointStates(state1, state2, manifold1, manifold2);\n\nif (state1[0] === b2_removeState) {\n // process event\n}\n\nDistance\nThe b2Distance function can be used to compute the distance between two\nshapes. The distance function needs both shapes to be converted into a\nb2DistanceProxy. There is also some caching used to warm start the\ndistance function for repeated calls.\n\nTime of Impact\nIf two shapes are moving fast, they may tunnel through each other in a\nsingle time step.\n\nThe b2TimeOfImpact function is used to determine the time when two\nmoving shapes collide. This is called the time of impact (TOI). The\nmain purpose of b2TimeOfImpact is for tunnel prevention. In particular,\nit is designed to prevent moving objects from tunneling outside of\nstatic level geometry.\nThis function accounts for rotation and translation of both shapes,\nhowever if the rotations are large enough, then the function may miss a\ncollision. However the function will still report a non-overlapped time\nand will capture all translational collisions.\nThe time of impact function identities an initial separating axis and\nensures the shapes do not cross on that axis. This might miss collisions\nthat are clear at the final positions. While this approach may miss some\ncollisions, it is very fast and adequate for tunnel prevention.\n\n\nIt is difficult to put a restriction on the rotation magnitude. There\nmay be cases where collisions are missed for small rotations. Normally,\nthese missed rotational collisions should not harm game play. They tend\nto be glancing collisions.\nThe function requires two shapes (converted to b2DistanceProxy) and two\nb2Sweep instances. The sweep class defines the initial and final\ntransforms of the shapes.\nYou can use fixed rotations to perform a shape cast. In this case, the\ntime of impact function will not miss any collisions.\nDynamic Tree\nThe b2DynamicTree class is used by Box2D to organize large numbers of\nshapes efficiently. The class does not know about shapes. Instead it\noperates on axis-aligned bounding boxes (AABBs) with user data pointers.\nThe dynamic tree is a hierarchical AABB tree. Each internal node in the\ntree has two children. A leaf node is a single user AABB. The tree uses\nrotations to keep the tree balanced, even in the case of degenerate\ninput.\nThe tree structure allows for efficient ray casts and region queries.\nFor example, you may have hundreds of shapes in your scene. You could\nperform a ray cast against the scene in a brute force manner by ray\ncasting each shape. This would be inefficient because it does not take\nadvantage of shapes being spread out. Instead, you can maintain a\ndynamic tree and perform ray casts against the tree. This traverses the\nray through the tree skipping large numbers of shapes.\nA region query uses the tree to find all leaf AABBs that overlap a query\nAABB. This is faster than a brute force approach because many shapes can\nbe skipped.\n\n\nNormally you will not use the dynamic tree directly. Rather you will go\nthrough the b2World class for ray casts and region queries. If you plan\nto instantiate your own dynamic tree, you can learn how to use it by\nlooking at how Box2D uses it.\nBroad-Phase\nCollision processing in a physics step can be divided into narrow-phase\nand broad-phase. In the narrow-phase we compute contact points between\npairs of shapes. Imagine we have N shapes. Using brute force, we would\nneed to perform the narrow-phase for N*N/2 pairs.\nThe b2BroadPhase class reduces this load by using a dynamic tree for\npair management. This greatly reduces the number of narrow-phase calls.\nNormally you do not interact with the broad-phase directly. Instead,\nBox2D creates and manages a broad-phase internally. Also, b2BroadPhase\nis designed with Box2D's simulation loop in mind, so it is likely not\nsuited for other use cases.\n","title":"Collision Module","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/common.html","content":"Common Module\nThe Common module contains settings, helpers, and vector math.\nSettings\nBox2D defines several constants. These are all documented in\nb2_settings.ts. Normally you do not need to adjust these constants.\nBox2D uses floating point math for collision and simulation. Due to\nround-off error some numerical tolerances are defined. Some tolerances\nare absolute and some are relative. Absolute tolerances use MKS units.\nAdjusting Settings\nIn order to adjust the above settings, you'll need to create a new file in your code box2d_config.ts with the following content:\nimport { configure } from "@box2d/core/config";\n\nconfigure({\n lengthUnitsPerMeter: 2, // default 1\n maxPolygonVertices: 10, // default 8\n});\n\nThis file must be imported before any box2d imports. Ideally at the top of your entrypoint. For example:\n// index.ts\nimport "./box2d_config.ts";\n// ... other imports\nimport { b2_lengthUnitsPerMeter, b2_maxPolygonVertices } from "@box2d/core";\n\n// The following should show your changes from above:\nconsole.log({\n b2_lengthUnitsPerMeter,\n b2_maxPolygonVertices,\n});\n\nVersion\nThe b2_version constant from b2_common.ts holds the current C++ reference version. It does not (yet) correlate to the current @box2d version.\nYou should ignore this for now. This might change in the future.\nMath\nBox2D includes a simple small vector and matrix module. This has been\ndesigned to suit the internal needs of Box2D and the API. All the\nmembers are exposed, so you may use them freely in your application.\nThe math library is kept simple to make Box2D easy to port and maintain.\nReadonly Types\nIn order to avoid mistakes like adding to a vector that wasn't meant to be modified, we've introduced a type helper b2Readonly.\nIt strips away all the modifying methods of @box2d/cores math types.\nExample\nBefore b2Readonly was introduced, you might have accidentally done something like this:\nfunction getOffsetPosition(body: b2Body) {\n return body.GetPosition().Add({x: 10, y: 20);\n}\n\nTypeScript would not complain about this, but the body position would have been modified, as b2Vec2::Add does not return a new vector, but instead modifies its own values.\nThe return type of GetPosition was Readonly<b2Vec2>, but that only changed the properties to readonly and did not remove the methods that might internally modify the properties.\nNow, getPosition returns b2Readonly<b2Vec2>, which only contains readonly properties and methods that do not modify the state of the class instance.\nSo the above code will return an error: Property 'Add' does not exist on type 'Readonly<...>.\nb2Readonly has been configured to work with all @box2d/cores math types. Other types won't work by default, as TypeScript doesn't give enough information to do this automatically.\nExtending b2Readonly\nHowever, if you feel like you want to use b2Readonly for your types as well, there is a way to do that.\nLet's say you have this custom rectangle type:\nclass Rectangle {\n public position: b2Vec2;\n\n public width: number;\n\n public height: number;\n\n public constructor(x: number, y: number, width: number, height: number) {\n this.position = new b2Vec2(x, y);\n this.width = width;\n this.height = height;\n }\n\n public setSize(width: number, height: number) {\n this.width = width;\n this.height = height;\n }\n\n public getArea() {\n return this.width * this.height;\n }\n}\n\nIn order for b2Readonly<Rectangle> to return the desired type, you'll need to do some declaration merging:\ndeclare module "@box2d/core" {\n export interface b2ReadonlyTypes {\n // <name>: [type, picked properties, manual properties]\n Rectangle: [Rectangle, "width" | "height" | "getArea", { position: b2Readonly<b2Vec2> }];\n }\n}\n\nThe parts explained:\n\nname is just there to uniquely identify your type. You might want to avoid conflicts if working with other libraries.\ntype is obviously the type you want to make compatible with b2Readonly\npicked properties are the properties (and methods) you want to add to the readonly type based on your type.\nmanual properties is a way to specify objects within your type, which also might need to be b2Readonly.\n\nThis might be a bit cumbersome compared to just adding the name to the picked properties, but the reward is better intellisense.\nIf you don't have any of those kinds of properties, you can just pass unknown.\n\n\n\nExample for a rectangle which has x/y properties instead of position:\ndeclare module "@box2d/core" {\n export interface b2ReadonlyTypes {\n Rectangle: [Rectangle, "x" | "y" | "width" | "height" | "getArea", unknown];\n }\n}\n\n","title":"Common Module","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/dynamics.html","content":"Dynamics Module\nThe Dynamics module is the most complex part of Box2D and is the part\nyou likely interact with the most. The Dynamics module sits on top of\nthe Common and Collision modules, so you should be somewhat familiar\nwith those by now.\nThe Dynamics module contains:\n\nfixture class\nrigid body class\ncontact class\njoint classes\nworld class\nlistener classes\n\nThere are many dependencies between these classes so it is difficult to\ndescribe one class without referring to another. In the following, you\nmay see some references to classes that have not been described yet.\nTherefore, you may want to quickly skim this chapter before reading it\nclosely.\nThe dynamics module is covered in the following chapters.\nBodies\nBodies have position and velocity. You can apply forces, torques, and\nimpulses to bodies. Bodies can be static, kinematic, or dynamic. Here\nare the body type definitions:\nb2_staticBody\nA static body does not move under simulation and behaves as if it has\ninfinite mass. Internally, Box2D stores zero for the mass and the\ninverse mass. Static bodies can be moved manually by the user. A static\nbody has zero velocity. Static bodies do not collide with other static\nor kinematic bodies.\nb2_kinematicBody\nA kinematic body moves under simulation according to its velocity.\nKinematic bodies do not respond to forces. They can be moved manually by\nthe user, but normally a kinematic body is moved by setting its\nvelocity. A kinematic body behaves as if it has infinite mass, however,\nBox2D stores zero for the mass and the inverse mass. Kinematic bodies do\nnot collide with other kinematic or static bodies.\nb2_dynamicBody\nA dynamic body is fully simulated. They can be moved manually by the\nuser, but normally they move according to forces. A dynamic body can\ncollide with all body types. A dynamic body always has finite, non-zero\nmass. If you try to set the mass of a dynamic body to zero, it will\nautomatically acquire a mass of one kilogram and it won't rotate.\nBodies are the backbone for fixtures (shapes). Bodies carry fixtures and\nmove them around in the world. Bodies are always rigid bodies in Box2D.\nThat means that two fixtures attached to the same rigid body never move\nrelative to each other and fixtures attached to the same body don't\ncollide.\nFixtures have collision geometry and density. Normally, bodies acquire\ntheir mass properties from the fixtures. However, you can override the\nmass properties after a body is constructed.\nYou usually keep references to all the bodies you create. This way you can\nquery the body positions to update the positions of your graphical\nentities. You should also keep body references so you can destroy them\nwhen you are done with them.\nBody Definition\nBefore a body is created you must create a body definition (b2BodyDef).\nThe body definition holds the data needed to create and initialize a\nbody.\nBox2D copies the data out of the body definition; it does not keep a\nreference to the body definition. This means you can recycle a body\ndefinition to create multiple bodies.\nLet's go over some of the key members of the body definition.\nBody Type\nAs discussed at the beginning of this chapter, there are three different\nbody types: static, kinematic, and dynamic. You should establish the\nbody type at creation because changing the body type later is expensive.\nconst bodyDef: b2BodyDef = {\n type: b2BodyType.b2_dynamicBody,\n};\n\nSetting the body type is mandatory.\nPosition and Angle\nThe body definition gives you the chance to initialize the position of\nthe body on creation. This has far better performance than creating the\nbody at the world origin and then moving the body.\n\nCaution:\nDo not create a body at the origin and then move it. If you create\nseveral bodies at the origin, then performance will suffer.\n\nA body has two main points of interest. The first point is the body's\norigin. Fixtures and joints are attached relative to the body's origin.\nThe second point of interest is the center of mass. The center of mass\nis determined from mass distribution of the attached shapes or is\nexplicitly set with b2MassData. Much of Box2D's internal computations\nuse the center of mass position. For example b2Body stores the linear\nvelocity for the center of mass.\nWhen you are building the body definition, you may not know where the\ncenter of mass is located. Therefore you specify the position of the\nbody's origin. You may also specify the body's angle in radians, which\nis not affected by the position of the center of mass. If you later\nchange the mass properties of the body, then the center of mass may move\non the body, but the origin position does not change and the attached\nshapes and joints do not move.\nconst bodyDef: b2BodyDef = {\n // ...\n position: { x: 0, y: 2 }, // the body's origin position.\n angle: 0.25 * Math.PI, // the body's angle in radians.\n};\n\nA rigid body is also a frame of reference. You can define fixtures and\njoints in that frame. Those fixtures and joint anchors never move in the\nlocal frame of the body.\nDamping\nDamping is used to reduce the world velocity of bodies. Damping is\ndifferent than friction because friction only occurs with contact.\nDamping is not a replacement for friction and the two effects should be\nused together.\nDamping parameters should be between 0 and infinity, with 0 meaning no\ndamping, and infinity meaning full damping. Normally you will use a\ndamping value between 0 and 0.1. I generally do not use linear damping\nbecause it makes bodies look like they are floating.\nconst bodyDef: b2BodyDef = {\n // ...\n linearDamping: 0,\n angularDamping: 0.01,\n};\n\nDamping is approximated for stability and performance. At small damping\nvalues the damping effect is mostly independent of the time step. At\nlarger damping values, the damping effect will vary with the time step.\nThis is not an issue if you use a fixed time step (recommended).\nGravity Scale\nYou can use the gravity scale to adjust the gravity on a single body. Be\ncareful though, increased gravity can decrease stability.\n// Set the gravity scale to zero so this body will float\nconst bodyDef: b2BodyDef = {\n // ...\n gravityScale: 0,\n};\n\nSleep Parameters\nWhat does sleep mean? Well it is expensive to simulate bodies, so the\nless we have to simulate the better. When a body comes to rest we would\nlike to stop simulating it.\nWhen Box2D determines that a body (or group of bodies) has come to rest,\nthe body enters a sleep state which has very little CPU overhead. If a\nbody is awake and collides with a sleeping body, then the sleeping body\nwakes up. Bodies will also wake up if a joint or contact attached to\nthem is destroyed. You can also wake a body manually.\nThe body definition lets you specify whether a body can sleep and\nwhether a body is created sleeping.\nconst bodyDef: b2BodyDef = {\n // ...\n allowSleep: true,\n awake: true,\n};\n\nFixed Rotation\nYou may want a rigid body, such as a character, to have a fixed\nrotation. Such a body should not rotate, even under load. You can use\nthe fixed rotation setting to achieve this:\nconst bodyDef: b2BodyDef = {\n // ...\n fixedRotation: true,\n};\n\nThe fixed rotation flag causes the rotational inertia and its inverse to\nbe set to zero.\nBullets\nGame simulation usually generates a sequence of images that are played\nat some frame rate. This is called discrete simulation. In discrete\nsimulation, rigid bodies can move by a large amount in one time step. If\na physics engine doesn't account for the large motion, you may see some\nobjects incorrectly pass through each other. This effect is called\ntunneling.\nBy default, Box2D uses continuous collision detection (CCD) to prevent\ndynamic bodies from tunneling through static bodies. This is done by\nsweeping shapes from their old position to their new positions. The\nengine looks for new collisions during the sweep and computes the time\nof impact (TOI) for these collisions. Bodies are moved to their first\nTOI and then the solver performs a sub-step to complete the full time\nstep. There may be additional TOI events within a sub-step.\nNormally CCD is not used between dynamic bodies. This is done to keep\nperformance reasonable. In some game scenarios you need dynamic bodies\nto use CCD. For example, you may want to shoot a high speed bullet at a\nstack of dynamic bricks. Without CCD, the bullet might tunnel through\nthe bricks.\nFast moving objects in Box2D can be labeled as bullets. Bullets will\nperform CCD with both static and dynamic bodies. You should decide what\nbodies should be bullets based on your game design. If you decide a body\nshould be treated as a bullet, use the following setting.\nconst bodyDef: b2BodyDef = {\n // ...\n bullet: true,\n};\n\nThe bullet flag only affects dynamic bodies.\nActivation\nYou may wish a body to be created but not participate in collision or\ndynamics. This state is similar to sleeping except the body will not be\nwoken by other bodies and the body's fixtures will not be placed in the\nbroad-phase. This means the body will not participate in collisions, ray\ncasts, etc.\nYou can create a body in an inactive state and later re-activate it.\nconst bodyDef: b2BodyDef = {\n // ...\n active: true,\n};\n\nJoints may be connected to inactive bodies. These joints will not be\nsimulated. You should be careful when you activate a body that its\njoints are not distorted.\nNote that activating a body is almost as expensive as creating the body\nfrom scratch. So you should not use activation for streaming worlds. Use\ncreation/destruction for streaming worlds to save memory.\nUser Data\nUser data is a Record of references. This gives you a hook to link your\napplication objects to bodies, fixtures and joints.\nCheck loose ends for more details.\nconst bodyDef: b2BodyDef = {\n // ...\n userData: {\n actor: myActor,\n },\n};\n\nBody Factory\nBodies are created and destroyed using a body factory provided by the\nworld class. This lets the world create the body and add the body to the world data structure.\nconst myWorld: b2World = ...;\nconst dynamicBody: b2Body = myWorld.createBody(bodyDef);\n\n// ... do stuff ...\n\nmyWorld.DestroyBody(dynamicBody);\n// stop using dynamicBody from this point on!\n\n\nCaution:\nYou should never use new to create a body. The world won't\nknow about the body and the body won't be properly initialized.\n\nBox2D does not keep a reference to the body definition or any of the\ndata it holds (except references in user data records).\nSo you can create temporary body definitions and reuse the same body definitions.\nBox2D allows you to avoid destroying bodies by deleting your b2World\nobject, which does all the cleanup work for you. However, you should be\nmindful to remove body references that you keep in your game engine.\nWhen you destroy a body, the attached fixtures and joints are\nautomatically destroyed. This has important implications for how you\nmanage shape and joint references.\nUsing a Body\nAfter creating a body, there are many operations you can perform on the\nbody. These include setting mass properties, accessing position and\nvelocity, applying forces, and transforming points and vectors.\nMass Data\nA body has mass (scalar), center of mass (2-vector), and rotational\ninertia (scalar). For static bodies, the mass and rotational inertia are\nset to zero. When a body has fixed rotation, its rotational inertia is\nzero.\nNormally the mass properties of a body are established automatically\nwhen fixtures are added to the body. You can also adjust the mass of a\nbody at run-time. This is usually done when you have special game\nscenarios that require altering the mass.\n// on b2Body:\npublic SetMassData(massData: b2MassData): void;\n\nAfter setting a body's mass directly, you may wish to revert to the\nnatural mass dictated by the fixtures. You can do this with:\n// on b2Body:\npublic ResetMassData(): void;\n\nThe body's mass data is available through the following functions:\n// on b2Body:\npublic GetMass(): number;\npublic GetInertia(): number;\npublic GetLocalCenter(): b2Readonly<b2Vec2>;\npublic GetMassData(data: b2MassData): b2MassData;\n\nState Information\nThere are many aspects to the body's state. You can access this state\ndata efficiently through the following functions:\n// on b2Body:\npublic SetType(type: b2BodyType): void;\npublic GetType(): b2BodyType;\npublic SetBullet(flag: boolean): void;\npublic IsBullet(): boolean;\npublic SetSleepingAllowed(flag: boolean): void;\npublic IsSleepingAllowed(): boolean;\npublic SetAwake(flag: boolean): void;\npublic IsAwake(): boolean;\npublic SetEnabled(flag: boolean): void;\npublic IsEnabled(): boolean;\npublic SetFixedRotation(flag: boolean): void;\npublic IsFixedRotation(): boolean;\n\nPosition and Velocity\nYou can access the position and rotation of a body. This is common when\nrendering your associated game actor. You can also set the position,\nalthough this is less common since you will normally use Box2D to\nsimulate movement.\n// on b2Body:\npublic SetTransformVec(position: XY, angle: number): void;\npublic SetTransformXY(x: number, y: number, angle: number): void\npublic GetTransform(): b2Readonly<b2Transform>;\npublic GetPosition(): b2Readonly<b2Vec2>;\npublic GetAngle(): number;\n\nYou can access the center of mass position in local and world\ncoordinates. Much of the internal simulation in Box2D uses the center of\nmass. However, you should normally not need to access it. Instead you\nwill usually work with the body transform. For example, you may have a\nbody that is square. The body origin might be a corner of the square,\nwhile the center of mass is located at the center of the square.\npublic GetWorldCenter(): b2Readonly<b2Vec2>;\npublic GetLocalCenter(): b2Readonly<b2Vec2>;\n\nYou can access the linear and angular velocity. The linear velocity is\nfor the center of mass. Therefore, the linear velocity may change if the\nmass properties change.\nForces and Impulses\nYou can apply forces, torques, and impulses to a body. When you apply a\nforce or an impulse, you provide a world point where the load is\napplied. This often results in a torque about the center of mass.\npublic ApplyForce(force: XY, point: XY, wake = true): void;\npublic ApplyTorque(torque: number, wake = true): void;\npublic ApplyLinearImpulse(impulse: XY, point: XY, wake = true): void;\npublic ApplyAngularImpulse(impulse: number, wake = true): void ;\n\nApplying a force, torque, or impulse wakes the body. Sometimes this is\nundesirable. For example, you may be applying a steady force and want to\nallow the body to sleep to improve performance. In this case you can use\nthe following code.\nif (myBody.IsAwake() === true) {\n myBody.ApplyForce(myForce, myPoint);\n}\n\nCoordinate Transformations\nThe body class has some utility functions to help you transform points\nand vectors between local and world space. If you don't understand\nthese concepts, please read "Essential Mathematics for Games and\nInteractive Applications" by Jim Van Verth and Lars Bishop. These\nfunctions are efficient (when inlined).\npublic GetWorldPoint<T extends XY>(localPoint: Readonly<XY>, out: T): T;\npublic GetWorldVector<T extends XY>(localVector: Readonly<XY>, out: T): T;\npublic GetLocalPoint<T extends XY>(worldPoint: Readonly<XY>, out: T): T;\npublic GetLocalVector<T extends XY>(worldVector: Readonly<XY>, out: T): T;\n\nAcessing Fixtures, Joints, and Contacts\nYou can iterate over a body's fixtures. This is mainly useful if you\nneed to access the fixture's user data.\nfor (let f: b2Fixture | null = b.GetFixtureList(); f; f = f.GetNext()) {\n const data: b2FixtureUserData = f.GetUserData();\n // do something with data ...\n}\n\nYou can similarly iterate over the body's joint list.\nThe body also provides a list of associated contacts. You can use this\nto get information about the current contacts. Be careful, because the\ncontact list may not contain all the contacts that existed during the\nprevious time step.\nFixtures\nRecall that shapes don't know about bodies and may be used independently\nof the physics simulation. Therefore Box2D provides the b2Fixture class\nto attach shapes to bodies. A body may have zero or more fixtures. A\nbody with multiple fixtures is sometimes called a compound body.\nFixtures hold the following:\n\na single shape\nbroad-phase proxies\ndensity, friction, and restitution\ncollision filtering flags\nback reference to the parent body\nuser data\nsensor flag\n\nThese are described in the following sections.\nFixture Creation\nFixtures are created by initializing a fixture definition and then\npassing the definition to the parent body.\nconst myBody: b2Body = ...;\nconst fixtureDef: b2FixtureDef = {\n shape: myShape;\n density: 1,\n};\nconst myFixture: b2Fixture = myBody.CreateFixture(fixtureDef);\n\nThis creates the fixture and attaches it to the body. You do not need to\nstore the fixture reference since the fixture will automatically be\ndestroyed when the parent body is destroyed. You can create multiple\nfixtures on a single body.\nYou can destroy a fixture on the parent body. You may do this to model a\nbreakable object. Otherwise you can just leave the fixture alone and let\nthe body destruction take care of destroying the attached fixtures.\nmyBody.DestroyFixture(myFixture);\n\nDensity\nThe fixture density is used to compute the mass properties of the parent\nbody. The density can be zero or positive. You should generally use\nsimilar densities for all your fixtures. This will improve stacking\nstability.\nThe mass of a body is not adjusted when you set the density. You must\ncall ResetMassData for this to occur.\nconst fixture: b2Fixture = ...;\nfixture.SetDensity(5);\nconst body: b2Body = ...;\nbody.ResetMassData();\n\nFriction\nFriction is used to make objects slide along each other realistically.\nBox2D supports static and dynamic friction, but uses the same parameter\nfor both. Friction is simulated accurately in Box2D and the friction\nstrength is proportional to the normal force (this is called Coulomb\nfriction). The friction parameter is usually set between 0 and 1, but\ncan be any non-negative value. A friction value of 0 turns off friction\nand a value of 1 makes the friction strong. When the friction force is\ncomputed between two shapes, Box2D must combine the friction parameters\nof the two parent fixtures. This is done with the geometric mean:\nconst fixtureA: b2Fixture = ...;\nconst fixtureB: b2Fixture = ...;\n\nconst friction: number = Math.sqrt(fixtureA.friction * fixtureB.friction);\n\nSo if one fixture has zero friction then the contact will have zero\nfriction.\nYou can override the default mixed friction using\nb2Contact::SetFriction. This is usually done in the b2ContactListener\ncallback.\nRestitution\nRestitution is used to make objects bounce. The restitution value is\nusually set to be between 0 and 1. Consider dropping a ball on a table.\nA value of zero means the ball won't bounce. This is called an\ninelastic collision. A value of one means the ball's velocity will be\nexactly reflected. This is called a perfectly elastic collision.\nRestitution is combined using the following formula.\nconst fixtureA: b2Fixture = ...;\nconst fixtureB: b2Fixture = ...;\nconst restitution: number = b2Max(fixtureA.restitution, fixtureB.restitution);\n\nRestitution is combined this way so that you can have a bouncy super\nball without having a bouncy floor.\nYou can override the default mixed restitution using\nb2Contact::SetRestitution. This is usually done in the b2ContactListener\ncallback.\nWhen a shape develops multiple contacts, restitution is simulated\napproximately. This is because Box2D uses an iterative solver. Box2D\nalso uses inelastic collisions when the collision velocity is small.\nThis is done to prevent jitter. See b2ContactVelocityConstraint::threshold.\nFiltering\nCollision filtering allows you to prevent collision between fixtures.\nFor example, say you make a character that rides a bicycle. You want the\nbicycle to collide with the terrain and the character to collide with\nthe terrain, but you don't want the character to collide with the\nbicycle (because they must overlap). Box2D supports such collision\nfiltering using categories and groups.\nBox2D supports 16 collision categories. For each fixture you can specify\nwhich category it belongs to. You also specify what other categories\nthis fixture can collide with. For example, you could specify in a\nmultiplayer game that all players don't collide with each other and\nmonsters don't collide with each other, but players and monsters should\ncollide. This is done with masking bits. For example:\nconst playerFixtureDef: b2FixtureDef = {\n categoryBits: 0x0002,\n maskBits: 0x0004,\n};\nconst monsterFixtureDef: b2FixtureDef = {\n categoryBits: 0x0004,\n maskBits: 0x0002,\n};\n\nHere is the rule for a collision to occur:\nconst catA: number = fixtureA.filter.categoryBits;\nconst maskA: number = fixtureA.filter.maskBits;\nconst catB: number = fixtureB.filter.categoryBits;\nconst maskB: number = fixtureB.filter.maskBits;\n\nif ((catA & maskB) !== 0 && (catB & maskA) !== 0) {\n // fixtures can collide\n}\n\nCollision groups let you specify an integral group index. You can have\nall fixtures with the same group index always collide (positive index)\nor never collide (negative index). Group indices are usually used for\nthings that are somehow related, like the parts of a bicycle. In the\nfollowing example, fixture1 and fixture2 always collide, but fixture3\nand fixture4 never collide.\nfixture1Def.filter.groupIndex = 2;\nfixture2Def.filter.groupIndex = 2;\nfixture3Def.filter.groupIndex = -8;\nfixture4Def.filter.groupIndex = -8;\n\nCollisions between fixtures of different group indices are filtered\naccording the category and mask bits. In other words, group filtering\nhas higher precedence than category filtering.\nNote that additional collision filtering occurs in Box2D. Here is a\nlist:\n\nA fixture on a static body can only collide with a dynamic body.\nA fixture on a kinematic body can only collide with a dynamic body.\nFixtures on the same body never collide with each other.\nYou can optionally enable/disable collision between fixtures on bodies connected by a joint.\n\nSometimes you might need to change collision filtering after a fixture\nhas already been created. You can get and set the b2Filter class on\nan existing fixture using b2Fixture::GetFilterData and\nb2Fixture::SetFilterData. Note that changing the filter data will not\nadd or remove contacts until the next time step (see the World class).\nSensors\nSometimes game logic needs to know when two fixtures overlap yet there\nshould be no collision response. This is done by using sensors. A sensor\nis a fixture that detects collision but does not produce a response.\nYou can flag any fixture as being a sensor. Sensors may be static,\nkinematic, or dynamic. Remember that you may have multiple fixtures per\nbody and you can have any mix of sensors and solid fixtures. Also,\nsensors only form contacts when at least one body is dynamic, so you\nwill not get a contact for kinematic versus kinematic, kinematic versus\nstatic, or static versus static.\nSensors do not generate contact points. There are two ways to get the\nstate of a sensor:\n\nb2Contact::IsTouching\nb2ContactListener::BeginContact and b2ContactListener::EndContact\n\nJoints\nJoints are used to constrain bodies to the world or to each other.\nTypical examples in games include ragdolls, teeters, and pulleys. Joints\ncan be combined in many different ways to create interesting motions.\nSome joints provide limits so you can control the range of motion. Some\njoint provide motors which can be used to drive the joint at a\nprescribed speed until a prescribed force/torque is exceeded.\nJoint motors can be used in many ways. You can use motors to control\nposition by specifying a joint velocity that is proportional to the\ndifference between the actual and desired position. You can also use\nmotors to simulate joint friction: set the joint velocity to zero and\nprovide a small, but significant maximum motor force/torque. Then the\nmotor will attempt to keep the joint from moving until the load becomes\ntoo strong.\nJoint Definition\nEach joint type has a definition that derives from b2JointDef. All\njoints are connected between two different bodies. One body may be static.\nJoints between static and/or kinematic bodies are allowed, but have no\neffect and use some processing time.\nYou can specify user data for any joint type and you can provide a flag\nto prevent the attached bodies from colliding with each other. This is\nactually the default behavior and you must set the collideConnected\nboolean to allow collision between to connected bodies.\nMany joint definitions require that you provide some geometric data.\nOften a joint will be defined by anchor points. These are points fixed\nin the attached bodies. Box2D requires these points to be specified in\nlocal coordinates. This way the joint can be specified even when the\ncurrent body transforms violate the joint constraint --- a common\noccurrence when a game is saved and reloaded. Additionally, some joint\ndefinitions need to know the default relative angle between the bodies.\nThis is necessary to constrain rotation correctly.\nInitializing the geometric data can be tedious, so many joints have\ninitialization functions that use the current body transforms to remove\nmuch of the work. However, these initialization functions should usually\nonly be used for prototyping. Production code should define the geometry\ndirectly. This will make joint behavior more robust.\nThe rest of the joint definition data depends on the joint type. We\ncover these now.\nJoint Factory\nJoints are created and destroyed using the world factory methods. This\nbrings up an old issue:\n\nCaution:\nDon't try to create a joint using new.\nYou must create and destroy bodies and joints using the create\nand destroy methods of the b2World class.\n\nHere's an example of the lifetime of a revolute joint:\nconst myWorld: b2World = ...;\nconst jointDef = new b2RevoluteJointDef();\njointDef.initialize(myBodyA, myBodyB, myBodyA.GetCenterPosition());\n\nconst joint: b2RevoluteJoint = myWorld.CreateJoint(jointDef);\n\n// ... do stuff ...\n\nmyWorld.DestroyJoint(joint);\n// stop using the joint from this point on!\n\nIt is always good to remove your reference after they are destroyed. This\nwill make the program behave unexpectedly if you try to reuse\nthe reference.\nThe lifetime of a joint is not simple. Heed this warning well:\n\nCaution:\nJoints are destroyed when an attached body is destroyed.\n\nThis precaution is not always necessary. You may organize your game\nengine so that joints are always destroyed before the attached bodies.\nIn this case you don't need to implement the listener class. See the\nsection on Implicit Destruction for details.\nUsing Joints\nMany simulations create the joints and don't access them again until\nthey are destroyed. However, there is a lot of useful data contained in\njoints that you can use to create a rich simulation.\nFirst of all, you can get the bodies, anchor points, and user data from\na joint.\n// on b2Joint:\npublic GetBodyA(): b2Body;\npublic GetBodyB(): bn2Body;\npublic GetAnchorA<T extends XY>(out: T): T;\npublic GetAnchorB<T extends XY>(out: T): T;\npublic GetUserData(): b2JointUserData;\n\nAll joints have a reaction force and torque. This the reaction force\napplied to body 2 at the anchor point. You can use reaction forces to\nbreak joints or trigger other game events. These functions may do some\ncomputations, so don't call them if you don't need the result.\npublic GetReactionForce<T extends XY>(inv_dt: number, out: T): T;\npublic GetReactionTorque(inv_dt: number): number;\n\nDistance Joint\nOne of the simplest joint is a distance joint which says that the\ndistance between two points on two bodies must be constant. When you\nspecify a distance joint the two bodies should already be in place. Then\nyou specify the two anchor points in world coordinates. The first anchor\npoint is connected to body 1, and the second anchor point is connected\nto body 2. These points imply the length of the distance constraint.\n\nHere is an example of a distance joint definition. In this case we\ndecide to allow the bodies to collide.\nconst jointDef = new b2DistanceJointDef();\njointDef.Initialize(myBodyA, myBodyB, worldAnchorOnBodyA, worldAnchorOnBodyB);\njointDef.collideConnected = true;\n\nThe distance joint can also be made soft, like a spring-damper\nconnection. See the Web example in the testbed to see how this behaves.\nSoftness is achieved by tuning two constants in the definition:\nstiffness and damping. It can be non-intuitive setting these values directly\nsince they have units in terms on Newtons. Box2D provides an API to compute\nthese values in terms of frequency and damping ratio.\nfunction b2LinearStiffness(\n def: { stiffness: number; damping: number },\n frequencyHertz: number,\n dampingRatio: number,\n bodyA: b2Body,\n bodyB: b2Body,\n): void;\n\nThink of the frequency as the frequency of a harmonic oscillator (like a\nguitar string). The frequency is specified in Hertz. Typically the frequency\nshould be less than a half the frequency of the time step. So if you are using\na 60Hz time step, the frequency of the distance joint should be less than 30Hz.\nThe reason is related to the Nyquist frequency.\nThe damping ratio is non-dimensional and is typically between 0 and 1,\nbut can be larger. At 1, the damping is critical (all oscillations\nshould vanish).\nconst frequencyHz = 4;\nconst dampingRatio = 0.5;\nb2LinearStiffness(jointDef, frequencyHz, dampingRatio, jointDef.bodyA, jointDef.bodyB);\n\nIt is also possible to define a minimum and maximum length for the distance joint.\nSee b2DistanceJointDef for details.\nRevolute Joint\nA revolute joint forces two bodies to share a common anchor point, often\ncalled a hinge point. The revolute joint has a single degree of freedom:\nthe relative rotation of the two bodies. This is called the joint angle.\n\nTo specify a revolute you need to provide two bodies and a single anchor\npoint in world space. The initialization function assumes that the\nbodies are already in the correct position.\nIn this example, two bodies are connected by a revolute joint at the\nfirst body's center of mass.\nconst jointDef = new b2RevoluteJointDef();\njointDef.Initialize(myBodyA, myBodyB, myBodyA.GetWorldCenter());\n\nThe revolute joint angle is positive when bodyB rotates CCW about the\nangle point. Like all angles in Box2D, the revolute angle is measured in\nradians. By convention the revolute joint angle is zero when the joint\nis created using Initialize(), regardless of the current rotation of the\ntwo bodies.\nIn some cases you might wish to control the joint angle. For this, the\nrevolute joint can optionally simulate a joint limit and/or a motor.\nA joint limit forces the joint angle to remain between a lower and upper\nbound. The limit will apply as much torque as needed to make this\nhappen. The limit range should include zero, otherwise the joint will\nlurch when the simulation begins.\nA joint motor allows you to specify the joint speed (the time derivative\nof the angle). The speed can be negative or positive. A motor can have\ninfinite force, but this is usually not desirable. Recall the eternal\nquestion:\n\nWhat happens when an irresistible force meets an immovable object?\n\nI can tell you it's not pretty. So you can provide a maximum torque for\nthe joint motor. The joint motor will maintain the specified speed\nunless the required torque exceeds the specified maximum. When the\nmaximum torque is exceeded, the joint will slow down and can even\nreverse.\nYou can use a joint motor to simulate joint friction. Just set the joint\nspeed to zero, and set the maximum torque to some small, but significant\nvalue. The motor will try to prevent the joint from rotating, but will\nyield to a significant load.\nHere's a revision of the revolute joint definition above; this time the\njoint has a limit and a motor enabled. The motor is setup to simulate\njoint friction.\nconst jointDef = new b2RevoluteJointDef();\njointDef.Initialize(bodyA, bodyB, myBodyA.GetWorldCenter());\njointDef.lowerAngle = -0.5 * Math.PI; // -90 degrees\njointDef.upperAngle = 0.25 * Math.PI; // 45 degrees\njointDef.enableLimit = true;\njointDef.maxMotorTorque = 10.0;\njointDef.motorSpeed = 0.0;\njointDef.enableMotor = true;\n\nYou can access a revolute joint's angle, speed, and motor torque.\n// on b2RevoluteJoint:\npublic GetJointAngle(): number;\npublic GetJointSpeed(): number;\npublic GetMotorTorque(inv_dt: number): number;\n\nYou also update the motor parameters each step.\n// on b2RevoluteJoint:\npublic SetMotorSpeed(speed: number): number;\npublic SetMaxMotorTorque(torque: number): void;\n\nJoint motors have some interesting abilities. You can update the joint\nspeed every time step so you can make the joint move back-and-forth like\na sine-wave or according to whatever function you want.\n// ... Game Loop Begin ...\n\nmyJoint.SetMotorSpeed(Math.cos(0.5 * time));\n\n// ... Game Loop End ...\n\nYou can also use joint motors to track a desired joint angle. For example:\n// ... Game Loop Begin ...\n\nconst angleError = myJoint.GetJointAngle() - angleTarget;\nconst gain = 0.1;\nmyJoint.SetMotorSpeed(-gain * angleError);\n\n// ... Game Loop End ...\n\nGenerally your gain parameter should not be too large. Otherwise your\njoint may become unstable.\nPrismatic Joint\nA prismatic joint allows for relative translation of two bodies along a\nspecified axis. A prismatic joint prevents relative rotation. Therefore,\na prismatic joint has a single degree of freedom.\n\nThe prismatic joint definition is similar to the revolute joint\ndescription; just substitute translation for angle and force for torque.\nUsing this analogy provides an example prismatic joint definition with a\njoint limit and a friction motor:\nconst jointDef = new b2PrismaticJointDef();\nconst worldAxis: XY = { x: 1, y: 0 };\njointDef.Initialize(myBodyA, myBodyB, myBodyA.GetWorldCenter(), worldAxis);\njointDef.lowerTranslation = -5;\njointDef.upperTranslation = 2.5;\njointDef.enableLimit = true;\njointDef.maxMotorForce = 1;\njointDef.motorSpeed = 0;\njointDef.enableMotor = true;\n\nThe revolute joint has an implicit axis coming out of the screen. The\nprismatic joint needs an explicit axis parallel to the screen. This axis\nis fixed in the two bodies and follows their motion.\nLike the revolute joint, the prismatic joint translation is zero when\nthe joint is created using Initialize(). So be sure zero is between your\nlower and upper translation limits.\nUsing a prismatic joint is similar to using a revolute joint. Here are\nthe relevant member functions:\n// on b2PrismaticJoint\npublic GetJointTranslation(): number;\npublic GetJointSpeed(): number;\npublic GetMotorForce(inv_dt: number): number;\npublic SetMotorSpeed(speed: number): number;\npublic SetMaxMotorForce(force: number): void;\n\nPulley Joint\nA pulley is used to create an idealized pulley. The pulley connects two\nbodies to ground and to each other. As one body goes up, the other goes\ndown. The total length of the pulley rope is conserved according to the\ninitial configuration.\nlength1 + length2 === constant;\n\nYou can supply a ratio that simulates a block and tackle. This causes\none side of the pulley to extend faster than the other. At the same time\nthe constraint force is smaller on one side than the other. You can use\nthis to create mechanical leverage.\nlength1 + ratio * length2 === constant;\n\nFor example, if the ratio is 2, then length1 will vary at twice the rate\nof length2. Also the force in the rope attached to body1 will have half\nthe constraint force as the rope attached to body2.\n\nPulleys can be troublesome when one side is fully extended. The rope on\nthe other side will have zero length. At this point the constraint\nequations become singular (bad). You should configure collision shapes\nto prevent this.\nHere is an example pulley definition:\nconst anchor1 = myBody1.GetWorldCenter();\nconst anchor2 = myBody2.GetWorldCenter();\n\nconst groundAnchor1: XY = { x: p1.x, y: p1.y + 10 };\nconst groundAnchor2: XY = { x: p2.x, y: p2.y + 12 };\n\nconst ratio = 1;\n\nconst jointDef = new b2PulleyJointDef();\njointDef.Initialize(myBody1, myBody2, groundAnchor1, groundAnchor2, anchor1, anchor2, ratio);\n\nPulley joints provide the current lengths.\n// on b2PulleyJoint:\npublic GetLengthA(): number;\npublic GetLengthB(): number;\n\nGear Joint\nIf you want to create a sophisticated mechanical contraption you might\nwant to use gears. In principle you can create gears in Box2D by using\ncompound shapes to model gear teeth. This is not very efficient and\nmight be tedious to author. You also have to be careful to line up the\ngears so the teeth mesh smoothly. Box2D has a simpler method of creating\ngears: the gear joint.\n\nThe gear joint can only connect revolute and/or prismatic joints.\nLike the pulley ratio, you can specify a gear ratio. However, in this\ncase the gear ratio can be negative. Also keep in mind that when one\njoint is a revolute joint (angular) and the other joint is prismatic\n(translation), and then the gear ratio will have units of length or one\nover length.\ncoordinate1 + ratio * coordinate2 === constant;\n\nHere is an example gear joint. The bodies myBodyA and myBodyB are any\nbodies from the two joints, as long as they are not the same bodies.\nconst jointDef = new b2GearJointDef();\njointDef.bodyA = myBodyA;\njointDef.bodyB = myBodyB;\njointDef.joint1 = myRevoluteJoint;\njointDef.joint2 = myPrismaticJoint;\njointDef.ratio = (2 * Math.PI) / myLength;\n\nNote that the gear joint depends on two other joints. This creates a\nfragile situation. What happens if those joints are destroyed?\n\nCaution:\nAlways destroy gear joints before the revolute/prismatic joints on the\ngears. Otherwise you will see unexpected behavior due to the orphaned\njoint references in the gear joint. You should also destroy the gear joint\nbefore you destroy any of the bodies involved.\n\nMouse Joint\nThe mouse joint is used in the testbed to manipulate bodies with the\nmouse. It attempts to drive a point on a body towards the current\nposition of the cursor. There is no restriction on rotation.\nThe mouse joint definition has a target point, maximum force, frequency,\nand damping ratio. The target point initially coincides with the body's\nanchor point. The maximum force is used to prevent violent reactions\nwhen multiple dynamic bodies interact. You can make this as large as you\nlike. The frequency and damping ratio are used to create a spring/damper\neffect similar to the distance joint.\nMany users have tried to adapt the mouse joint for game play. Users\noften want to achieve precise positioning and instantaneous response.\nThe mouse joint doesn't work very well in that context. You may wish to\nconsider using kinematic bodies instead.\nWheel Joint\nThe wheel joint restricts a point on bodyB to a line on bodyA. The wheel\njoint also provides a suspension spring. See b2_wheel_joint.ts and car.ts\nfor details.\n\nWeld Joint\nThe weld joint attempts to constrain all relative motion between two\nbodies. See the cantilever.ts in the testbed to see how the weld joint\nbehaves.\nIt is tempting to use the weld joint to define breakable structures.\nHowever, the Box2D solver is iterative so the joints are a bit soft. So\nchains of bodies connected by weld joints will flex.\nInstead it is better to create breakable bodies starting with a single\nbody with multiple fixtures. When the body breaks, you can destroy a\nfixture and recreate it on a new body. See the Breakable example in the\ntestbed.\nFriction Joint\nThe friction joint is used for top-down friction. The joint provides 2D\ntranslational friction and angular friction. See b2_friction_joint.ts and\napply_force.ts for details.\nMotor Joint\nA motor joint lets you control the motion of a body by specifying target\nposition and rotation offsets. You can set the maximum motor force and\ntorque that will be applied to reach the target position and rotation.\nIf the body is blocked, it will stop and the contact forces will be\nproportional the maximum motor force and torque. See b2MotorJoint and\nmotor_joint.ts for details.\nWheel Joint\nThe wheel joint is designed specifically for vehicles. It provides a translation\nand rotation. The translation has a spring and damper to simulate the vehicle\nsuspension. The rotation allows the wheel to rotate. You can specify an rotational\nmotor to drive the wheel and to apply braking. See b2WheelJoint, wheel_joint.ts,\nand car.ts for details.\nContacts\nContacts are objects created by Box2D to manage collision between two\nfixtures. If the fixture has children, such as a chain shape, then a\ncontact exists for each relevant child. There are different kinds of\ncontacts, derived from b2Contact, for managing contact between different\nkinds of fixtures. For example there is a contact class for managing\npolygon-polygon collision and another contact class for managing\ncircle-circle collision.\nHere is some terminology associated with contacts.\nContact Point\nA contact point is a point where two shapes touch. Box2D approximates\ncontact with a small number of points.\nContact Normal\nA contact normal is a unit vector that points from one shape to another.\nBy convention, the normal points from fixtureA to fixtureB.\nContact Separation\nSeparation is the opposite of penetration. Separation is negative when\nshapes overlap. It is possible that future versions of Box2D will create\ncontact points with positive separation, so you may want to check the\nsign when contact points are reported.\nContact Manifold\nContact between two convex polygons may generate up to 2 contact points.\nBoth of these points use the same normal, so they are grouped into a\ncontact manifold, which is an approximation of a continuous region of\ncontact.\nNormal Impulse\nThe normal force is the force applied at a contact point to prevent the\nshapes from penetrating. For convenience, Box2D works with impulses. The\nnormal impulse is just the normal force multiplied by the time step.\nTangent Impulse\nThe tangent force is generated at a contact point to simulate friction.\nFor convenience, this is stored as an impulse.\nContact Ids\nBox2D tries to re-use the contact force results from a time step as the\ninitial guess for the next time step. Box2D uses contact ids to match\ncontact points across time steps. The ids contain geometric features\nindices that help to distinguish one contact point from another.\nContacts are created when two fixture's AABBs overlap. Sometimes\ncollision filtering will prevent the creation of contacts. Contacts are\ndestroyed with the AABBs cease to overlap.\nSo you might gather that there may be contacts created for fixtures that\nare not touching (just their AABBs). Well, this is correct. It's a\n"chicken or egg" problem. We don't know if we need a contact object\nuntil one is created to analyze the collision. We could delete the\ncontact right away if the shapes are not touching, or we can just wait\nuntil the AABBs stop overlapping. Box2D takes the latter approach\nbecause it lets the system cache information to improve performance.\nContact Class\nAs mentioned before, the contact class is created and destroyed by\nBox2D. Contact objects are not created by the user. However, you are\nable to access the contact class and interact with it.\nYou can access the raw contact manifold:\n// on b2Contact:\npublic GetManifold(): b2Manifold;\n\nYou can potentially modify the manifold, but this is generally not\nsupported and is for advanced usage.\nThere is a helper function to get the b2WorldManifold:\n// on b2Contact:\npublic GetWorldManifold(worldManifold: b2WorldManifold): void;\n\nThis uses the current positions of the bodies to compute world positions\nof the contact points.\nSensors do not create manifolds, so for them use:\nconst touching = sensorContact.IsTouching();\n\nThis function also works for non-sensors.\nYou can get the fixtures from a contact. From those you can get the\nbodies.\nconst fixtureA: b2Fixture = myContact.GetFixtureA();\nconst bodyA: b2Body = fixtureA.GetBody();\n\nYou can disable a contact. This only works inside the\nb2ContactListener::PreSolve event, discussed below.\nAccessing Contacts\nYou can get access to contacts in several ways. You can access the\ncontacts directly on the world and body structures. You can also\nimplement a contact listener.\nYou can iterate over all contacts in the world:\nfor (let c: b2Contact | null = myWorld.GetContactList(); c; c = c.GetNext()) {\n // process c\n}\n\nYou can also iterate over all the contacts on a body. These are stored\nin a graph using a contact edge class.\nfor (let ce: b2ContactEdge | null = myBody.GetContactList(); ce; ce = ce.next) {\n const c: b2Contact = ce.contact;\n // process c\n}\n\nYou can also access contacts using the contact listener that is\ndescribed below.\n\nCaution:\nAccessing contacts off b2World and b2Body may miss some transient\ncontacts that occur in the middle of the time step. Use\nb2ContactListener to get the most accurate results.\n\nContact Listener\nYou can receive contact data by extending b2ContactListener. The\ncontact listener supports several events: begin, end, pre-solve, and\npost-solve.\nclass MyContactListener extends b2ContactListener {\n public BeginContact(contact: b2Contact): void {\n /* handle begin event */\n }\n\n public EndContact(contact: b2Contact): void {\n /* handle end event */\n }\n\n public PreSolve(contact: b2Contact, oldManifold: b2Manifold): void {\n /* handle pre-solve event */\n }\n\n public PostSolve(contact: b2Contact, impulse: b2ContactImpulse): void {\n /* handle post-solve event */\n }\n}\n\n\nCaution:\nDo not keep a reference to the references sent to b2ContactListener.\nInstead make a deep copy of the contact point data into your own buffer.\nThe example below shows one way of doing this.\n\nAt run-time you can create an instance of the listener and register it\nwith b2World::SetContactListener.\nBegin Contact Event\nThis is called when two fixtures begin to overlap. This is called for\nsensors and non-sensors. This event can only occur inside the time step.\nEnd Contact Event\nThis is called when two fixtures cease to overlap. This is called for\nsensors and non-sensors. This may be called when a body is destroyed, so\nthis event can occur outside the time step.\nPre-Solve Event\nThis is called after collision detection, but before collision\nresolution. This gives you a chance to disable the contact based on the\ncurrent configuration. For example, you can implement a one-sided\nplatform using this callback and calling b2Contact::SetEnabled(false).\nThe contact will be re-enabled each time through collision processing,\nso you will need to disable the contact every time-step. The pre-solve\nevent may be fired multiple times per time step per contact due to\ncontinuous collision detection.\npublic PreSolve(contact: b2Contact, oldManifold: b2Manifold): void {\n const worldManifold = new b2WorldManifold();\n contact.GetWorldManifold(worldManifold);\n if (worldManifold.normal.y < -0.5) {\n contact.SetEnabled(false);\n }\n}\n\nThe pre-solve event is also a good place to determine the point state\nand the approach velocity of collisions.\npublic PreSolve(contact: b2Contact, oldManifold: b2Manifold): void {\n const worldManifold = new b2WorldManifold();\n contact.GetWorldManifold(worldManifold);\n\n const state1: b2PointState[] = [];\n const state2: b2PointState[] = [];\n b2GetPointStates(state1, state2, oldManifold, contact.GetManifold());\n\n if (state2[0] === b2_addState) {\n const bodyA: b2Body = contact.GetFixtureA().GetBody();\n const bodyB: b2Body = contact.GetFixtureB().GetBody();\n const point: b2Vec2 = worldManifold.points[0];\n const vA: b2Vec2 = bodyA.GetLinearVelocityFromWorldPoint(point, new b2Vec2());\n const vB: b2Vec2 = bodyB.GetLinearVelocityFromWorldPoint(point, new b2Vec2());\n\n const approachVelocity: number = b2Vec2.Dot(b2Vec2.Subtract(vB, vA, new b2Vec2()), worldManifold.normal);\n\n if (approachVelocity > 1) {\n MyPlayCollisionSound();\n }\n }\n}\n\nPost-Solve Event\nThe post solve event is where you can gather collision impulse results.\nIf you don't care about the impulses, you should probably just implement\nthe pre-solve event.\nIt is tempting to implement game logic that alters the physics world\ninside a contact callback. For example, you may have a collision that\napplies damage and try to destroy the associated actor and its rigid\nbody. However, Box2D does not allow you to alter the physics world\ninside a callback because you might destroy objects that Box2D is\ncurrently processing, leading to orphaned references.\nThe recommended practice for processing contact points is to buffer all\ncontact data that you care about and process it after the time step. You\nshould always process the contact points immediately after the time\nstep; otherwise some other client code might alter the physics world,\ninvalidating the contact buffer. When you process the contact buffer you\ncan alter the physics world, but you still need to be careful that you\ndon't orphan references stored in the contact point buffer. The testbed\nhas example contact point processing that is safe from orphaned\nreferences.\nThis code from the CollisionProcessing test shows how to handle orphaned\nbodies when processing the contact buffer. Here is an excerpt. Be sure\nto read the comments in the listing. This code assumes that all contact\npoints have been buffered in the b2ContactPoint array m_points.\n// We are going to destroy some bodies according to contact\n// points. We must buffer the bodies that should be destroyed\n// because they may belong to multiple contact points.\nconst k_maxNuke = 6;\n// Using a set to prevent duplicates\nconst nuke = new Set<b2Body>();\nlet nukeCount = 0;\n\n// Traverse the contact buffer. Destroy bodies that\n// are touching heavier bodies.\nfor (const point of m_points) {\n const bodyA: b2Body = point.fixtureA.GetBody();\n const bodyB: b2Body = point.FixtureB.GetBody();\n const massA: number = bodyA.GetMass();\n const massB: number = bodyB.GetMass();\n\n if (massA > 0 && massB > 0) {\n if (massB > massA) {\n nuke.add(bodyA);\n } else {\n nuke.add(bodyB);\n }\n\n if (nukeCount === k_maxNuke) {\n break;\n }\n }\n}\n\n// Destroy the bodies\nnuke.forEach((b) => m_world.DestroyBody(b));\n\nContact Filtering\nOften in a game you don't want all objects to collide. For example, you\nmay want to create a door that only certain characters can pass through.\nThis is called contact filtering, because some interactions are filtered\nout.\nBox2D allows you to achieve custom contact filtering by implementing a\nb2ContactFilter class. This class requires you to implement a\nShouldCollide function that receives two b2Shape references. Your function\nreturns true if the shapes should collide.\nThe default implementation of ShouldCollide uses the b2FilterData\ndefined in Chapter 6, Fixtures.\npublic ShouldCollide(fixtureA: b2Fixture, fixtureB: b2Fixture): boolean {\n const filterA: Readonly<b2Filter> = fixtureA.GetFilterData();\n const filterB: Readonly<b2Filter> = fixtureB.GetFilterData();\n\n if (filterA.groupIndex === filterB.groupIndex && filterA.groupIndex !== 0) {\n return filterA.groupIndex > 0;\n }\n\n const collideA = (filterA.maskBits & filterB.categoryBits) !== 0;\n const collideB = (filterA.categoryBits & filterB.maskBits) !== 0\n const collide = collideA && collideB;\n return collide;\n}\n\nAt run-time you can create an instance of your contact filter and\nregister it with b2World::SetContactFilter.\nconst filter = new MyContactFilter();\nworld.SetContactFilter(filter);\n\nWorld\nThe b2World class contains the bodies and joints. It manages all aspects\nof the simulation and allows for asynchronous queries (like AABB queries\nand ray-casts). Much of your interactions with Box2D will be with a\nb2World object.\nCreating and Destroying a World\nCreating a world is fairly simple. You just need to provide a gravity\nvector. Usually you will create a world like this:\nconst myWorld = new b2World.Create(gravity);\n\n// ... do stuff ...\n\nUnlike the C++, there is no destroying a world. Just remove all references and the garbage collector will take care of the rest.\nUsing a World\nThe world class contains factories for creating and destroying bodies\nand joints. These factories are discussed later in the sections on\nbodies and joints. There are some other interactions with b2World that I\nwill cover now.\nSimulation\nThe world class is used to drive the simulation. You specify a time step\nand a velocity and position iteration count. For example:\nconst timeStep = 1 / 60;\nconst stepConfig: b2StepConfig = {\n velocityIterations: 10,\n positionIterations: 8,\n};\nmyWorld.Step(timeStep, stepConfig);\n\nAfter the time step you can examine your bodies and joints for\ninformation. Most likely you will grab the position off the bodies so\nthat you can update your actors and render them. You can perform the\ntime step anywhere in your game loop, but you should be aware of the\norder of things. For example, you must create bodies before the time\nstep if you want to get collision results for the new bodies in that\nframe.\nAs I discussed above in the HelloWorld tutorial, you should use a fixed\ntime step. By using a larger time step you can improve performance in\nlow frame rate scenarios. But generally you should use a time step no\nlarger than 1/30 seconds. A time step of 1/60 seconds will usually\ndeliver a high quality simulation.\nThe iteration count controls how many times the constraint solver sweeps\nover all the contacts and joints in the world. More iteration always\nyields a better simulation. But don't trade a small time step for a\nlarge iteration count. 60Hz and 10 iterations is far better than 30Hz\nand 20 iterations.\nAfter stepping, you should clear any forces you have applied to your\nbodies. This is done with the command b2World::ClearForces. This lets\nyou take multiple sub-steps with the same force field.\nmyWorld.ClearForces();\n\nExploring the World\nThe world is a container for bodies, contacts, and joints. You can grab\nthe body, contact, and joint lists off the world and iterate over them.\nFor example, this code wakes up all the bodies in the world:\nfor (let b: b2Body | null = myWorld.GetBodyList(); b; b = b.GetNext()) {\n b.SetAwake(true);\n}\n\nIn the C++ version, it would have been problematic to destroy a body during iteration.\nIn the TypeScript version, there should be no such issue, since the body is only garbage\ncollected when no more references exist.\nAABB Queries\nSometimes you want to determine all the shapes in a region. The b2World\nclass has a fast log(N) method for this using the broad-phase data\nstructure. You provide an AABB in world coordinates and an\nimplementation of b2QueryCallback. The world calls your class with each\nfixture whose AABB overlaps the query AABB. Return true to continue the\nquery, otherwise return false. For example, the following code finds all\nthe fixtures that potentially intersect a specified AABB and wakes up\nall of the associated bodies.\nconst myQueryCallback: b2QueryCallback = (fixture: b2Fixture): boolean => {\n const body: b2Body = fixture.GetBody();\n body.SetAwake(true);\n\n // Return true to continue the query.\n return true;\n};\n\n// Elsewhere ...\nconst aabb = new b2AABB();\n\naabb.lowerBound.Set(-1, -1);\naabb.upperBound.Set(1, 1);\nmyWorld.QueryAABB(aabb, myQueryCallback);\n\nYou cannot make any assumptions about the order of the callbacks.\nRay Casts\nYou can use ray casts to do line-of-sight checks, fire guns, etc. You\nperform a ray cast by implementing a callback class and providing the\nstart and end points. The world class calls your class with each fixture\nhit by the ray. Your callback is provided with the fixture, the point of\nintersection, the unit normal vector, and the fractional distance along\nthe ray. You cannot make any assumptions about the order of the\ncallbacks.\nYou control the continuation of the ray cast by returning a fraction.\nReturning a fraction of zero indicates the ray cast should be\nterminated. A fraction of one indicates the ray cast should continue as\nif no hit occurred. If you return the fraction from the argument list,\nthe ray will be clipped to the current intersection point. So you can\nray cast any shape, ray cast all shapes, or ray cast the closest shape\nby returning the appropriate fraction.\nYou may also return of fraction of -1 to filter the fixture. Then the\nray cast will proceed as if the fixture does not exist.\nHere is an example:\n// This class captures the closest hit shape.\n// Note that you don't need a class for this, you can do it with functions (and closures) as well!\nclass MyRayCastCallback {\n public fixture: b2Fixture | null = null;\n public readonly point = new b2Vec2();\n public readonly normal = new b2Vec2();\n public fraction = 0;\n\n public callback: b2RayCastCallback = (\n fixture: b2Fixture,\n point: b2Readonly<b2Vec2>,\n normal: b2Readonly<b2Vec2>,\n fraction: number,\n ): number => {\n this.fixture = fixture;\n this.point.Copy(point);\n this.normal.Copy(normal);\n this.fraction = fraction;\n return fraction;\n };\n}\n\n// Elsewhere ...\nconst myCallback = new MyRayCastCallback();\nconst point1: XY = { x: -1, y: 0 };\nconst point2: XY = { x: 3, y: 1 };\nmyWorld.RayCast(point1, point2, myCallback.callback);\nconst { fixture, point, normal, fraction } = myCallback;\n// ...\n\n\nCaution:\nDue to round-off errors, ray casts can sneak through small cracks\nbetween polygons in your static environment. If this is not acceptable\nin your application, trying slightly overlapping your polygons.\n\n","title":"Dynamics Module","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/hello.html","content":"Hello Box2D\nIn the distribution of Box2D is a Hello World project. The program\ncreates a large ground box and a small dynamic box. This code does not\ncontain any graphics. All you will see is text output in the console of\nthe box's position over time.\nThis is a good example of how to get up and running with Box2D.\nCreating a World\nEvery Box2D program begins with the creation of a b2World object.\nb2World is the physics hub that manages objects and simulation.\nIt is easy to create a Box2D world. First, we define the gravity vector.\nconst gravity: XY = { x: 0, y: -10 };\n\nNow we create the world object.\nconst world = b2World.Create(gravity);\n\nSo now we have our physics world, let's start adding some stuff to it.\nCreating a Ground Box\nBodies are built using the following steps:\n\nDefine a body with position, damping, etc.\nUse the world object to create the body.\nDefine fixtures with a shape, friction, density, etc.\nCreate fixtures on the body.\n\nFor step 1 we create the ground body. For this we need a body\ndefinition. With the body definition we specify the initial position of\nthe ground body.\nconst groundBodyDef: b2BodyDef = {\n position: { x: 0, y: -10 },\n};\n\nFor step 2 the body definition is passed to the world object to create\nthe ground body. The world object does not keep a reference to the body\ndefinition. Bodies are static by default. Static bodies don't collide\nwith other static bodies and are immovable.\nconst body = world.CreateBody(groundBodyDef);\n\nFor step 3 we create a ground polygon. We use the SetAsBox shortcut to\nform the ground polygon into a box shape, with the box centered on the\norigin of the parent body.\nconst box = new b2PolygonShape();\nbox.SetAsBox(50, 10);\n\nThe SetAsBox function takes the half-width and\nhalf-height (extents). So in this case the ground box is 100\nunits wide (x-axis) and 20 units tall (y-axis). Box2D is tuned for\nmeters, kilograms, and seconds. So you can consider the extents to be in\nmeters. Box2D generally works best when objects are the size of typical\nreal world objects. For example, a barrel is about 1 meter tall. Due to\nthe limitations of floating point arithmetic, using Box2D to model the\nmovement of glaciers or dust particles is not a good idea.\nWe finish the ground body in step 4 by creating the shape fixture.\nA b2FixtureDef has lots of optional properties. For now, we only care\nabout shape. Later we will see how to use the other properties.\nFor a static body we don't need anything more right now.\ngroundBody.CreateFixture({ shape: groundBox });\n\nBox2D does not keep a reference to the shape. It clones the data into a\nnew b2Shape object.\nNote that every fixture must have a parent body, even fixtures that are\nstatic. However, you can attach all static fixtures to a single static\nbody.\nWhen you attach a shape to a body using a fixture, the shape's\ncoordinates become local to the body. So when the body moves, so does\nthe shape. A fixture's world transform is inherited from the parent\nbody. A fixture does not have a transform independent of the body. So we\ndon't move a shape around on the body. Moving or modifying a shape that\nis on a body is not supported. The reason is simple: a body with\nmorphing shapes is not a rigid body, but Box2D is a rigid body engine.\nMany of the assumptions made in Box2D are based on the rigid body model.\nIf this is violated many things will break\nCreating a Dynamic Body\nSo now we have a ground body. We can use the same technique to create a\ndynamic body. The main difference, besides dimensions, is that we must\nestablish the dynamic body's mass properties.\nFirst we create the body using CreateBody. By default bodies are static,\nso we should set the b2BodyType at construction time to make the body\ndynamic.\nconst bodyDef: b2BodyDef = {\n type: b2BodyType.b2_dynamicBody,\n position: { x: 0, y: 4 },\n};\nconst body = world.CreateBody(bodyDef);\n\n\nCaution:\nYou must set the body type to b2_dynamicBody if you want the body to\nmove in response to forces.\n\nNext we create and attach a polygon shape using a fixture definition.\nFirst we create a box shape:\nconst dynamicBox = new b2PolygonShape();\ndynamicBox.SetAsBox(1, 1);\n\nNext we create a fixture definition using the box. Notice that we set\ndensity to 1. The default density is zero. Also, the friction on the\nshape is set to 0.3.\nconst fixtureDef: b2FixtureDef = {\n shape: groundBox,\n density: 1,\n friction: 0.3,\n};\n\n\nCaution:\nA dynamic body should have at least one fixture with a non-zero density.\nOtherwise you will get strange behavior.\n\nUsing the fixture definition we can now create the fixture. This\nautomatically updates the mass of the body. You can add as many fixtures\nas you like to a body. Each one contributes to the total mass.\nbody.CreateFixture(fixtureDef);\n\nThat's it for initialization. We are now ready to begin simulating.\nSimulating the World\nSo we have initialized the ground box and a dynamic box. Now we are\nready to set Newton loose to do his thing. We just have a couple more\nissues to consider.\nBox2D uses a computational algorithm called an integrator. Integrators\nsimulate the physics equations at discrete points of time. This goes\nalong with the traditional game loop where we essentially have a flip\nbook of movement on the screen. So we need to pick a time step for\nBox2D. Generally physics engines for games like a time step at least as\nfast as 60Hz or 1/60 seconds. You can get away with larger time steps,\nbut you will have to be more careful about setting up the definitions\nfor your world. We also don't like the time step to change much. A\nvariable time step produces variable results, which makes it difficult\nto debug. So don't tie the time step to your frame rate (unless you\nreally, really have to). Without further ado, here is the time step.\nconst timeStep = 1 / 60;\n\nIn addition to the integrator, Box2D also uses a larger bit of code\ncalled a constraint solver. The constraint solver solves all the\nconstraints in the simulation, one at a time. A single constraint can be\nsolved perfectly. However, when we solve one constraint, we slightly\ndisrupt other constraints. To get a good solution, we need to iterate\nover all constraints a number of times.\nThere are two phases in the constraint solver: a velocity phase and a\nposition phase. In the velocity phase the solver computes the impulses\nnecessary for the bodies to move correctly. In the position phase the\nsolver adjusts the positions of the bodies to reduce overlap and joint\ndetachment. Each phase has its own iteration count. In addition, the\nposition phase may exit iterations early if the errors are small.\nThe suggested iteration count for Box2D is 8 for velocity and 3 for\nposition. You can tune this number to your liking, just keep in mind\nthat this has a trade-off between performance and accuracy. Using fewer\niterations increases performance but accuracy suffers. Likewise, using\nmore iterations decreases performance but improves the quality of your\nsimulation. For this simple example, we don't need much iteration. Here\nare our chosen iteration counts.\nconst stepConfig: b2StepConfig = {\n velocityIterations: 6,\n positionIterations: 2,\n};\n\nNote that the time step and the iteration count are completely\nunrelated. An iteration is not a sub-step. One solver iteration is a\nsingle pass over all the constraints within a time step. You can have\nmultiple passes over the constraints within a single time step.\nWe are now ready to begin the simulation loop. In your game the\nsimulation loop can be merged with your game loop. In each pass through\nyour game loop you call b2World::Step. Just one call is usually enough,\ndepending on your frame rate and your physics time step.\nThe Hello World program was designed to be simple, so it has no\ngraphical output. The code prints out the position and rotation of the\ndynamic body. Here is the simulation loop that simulates 60 time steps\nfor a total of 1 second of simulated time.\nfor (let i = 0; i < 60; ++i) {\n world.Step(timeStep, stepConfig);\n const position = body.GetPosition();\n const angle = body.GetAngle();\n console.log(`${position.x.toFixed(2)} ${position.y.toFixed(2)} ${angle.toFixed(2)}\\n`);\n}\n\nThe output shows the box falling and landing on the ground box. Your\noutput should look like this:\n0.00 4.00 0.00\n0.00 3.99 0.00\n0.00 3.98 0.00\n...\n0.00 1.25 0.00\n0.00 1.13 0.00\n0.00 1.01 0.00\n\nCleanup\nUnlike the C++, there is no destroying a world. Just remove all references and the garbage collector will take care of the rest.\nHowever, you will need to remove any body, fixture, or joint references you have\nbecause will prevent the garbage collector from doing its thing.\n","title":"Hello Box2D","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/key-differences.html","content":"Key Differences to C++\nThe TypeScript API differs in a few points from the original C++ API.\nLuckily, TypeScript will help you with the API as you use it, so you should be able to spot the changes quickly.\nNevertheless, make sure you read the following points to understand how to handle use this library correctly:\nNo Class Instances on the Stack\nThis one is obvious for anyone who worked with JS before:\nYou can't create class instances on the stack. This means, that you'll need to explicitly create them with new:\n// b2PolygonShape groundBox;\nconst groundBox = new b2PolygonShape();\n\nGarbage Collector Considerations\nSince we can't have class instances on the stack, we need to make sure we don't spam the garbage collector with lots & lots of new objects on every iteration.\nThe usual approach is to have temporary objects, which you can reuse.\n// Bad:\nexport function doStuff(xx: number, xy: number, yx: number, yy: number) {\n const a = new b2Vec2(xx, xy);\n const b = new b2Vec2(yx, yy);\n //...\n return b2Vec2.Clone(a).add(b);\n}\n\n// Better:\nconst temp = {\n a: new b2Vec2(),\n b: new b2Vec2(),\n};\nexport function doStuffRight(xx: number, xy: number, yx: number, yy: number, out: b2Vec2) {\n a.Set(xx, xy);\n b.Set(yx, yy);\n //...\n return out.Copy(a).add(b);\n}\n\nExamples in this documentation might not reflect the above pattern in order to stay small and understandable.\nOperators\nIn C++ you can use operators (+, -, etc.) on classes. There is no equivalent in TypeScript (or JavaScript). Instead, you'll find methods, which we tried to name as best as possible:\nconst temp = {\n sum: new b2Vec2(),\n};\nfunction doStuff(a: b2Readonly<b2Vec2>, b: b2Readonly<b2Vec2>) {\n // b2Vec2 sum = a + b;\n const sum = sum.Copy(a).add(b);\n //...\n}\n\nBe sure to check out all methods and static methods of the classes you are interested in to see all possibilities.\nMath Operations\nAs you might have noticed, math operations return a reference to itself, so you can use chaining. Due to garbage collector considerations, there is no automatic cloning going on!\nVec2 vs XY\nYou might have noticed the type XY, which seems to be in places, where the C++ version of Box2D used b2Vec2.\nThis change has been introduced to simplify the code, as in most cases, we only care about the x/y properties of the vector.\nSo in some cases, you can just use { x: 1, y: 2 } instead of new b2Vec2(1, 2)\nOverloads\nJavaScript has no overloads and while TypeScript supports overload method definitions, we'd have to check the parameters in runtime to make this work (not good for performance). That's why we try to avoid them and supply alternatively named functions instead:\nconst temp = {\n sum: new b2Vec2(),\n};\nfunction doStuff(a: b2Readonly<b2Vec2>, x: number, y: number) {\n // b2Vec2 sum = a + b2Vec2(x, y);\n const sum = sum.Copy(a).addXY(x, y);\n //...\n}\n\nNotice how add(v) accepts one parameter of type b2Readonly<b2Vec2>, while addXY(x, y) accepts two parameters of type number.\nFactory Functions vs Constructors\nIn order to be able to split the extension packages from the core package, we needed to intercept the constructor call of certain classes. Since this is (to my knowledge) not possible in JavaScript, I've introduced factory functions for these cases. In these cases, you'll find that the constructor is private and you can't create an instance. In this case, look for a static Create method:\n// b2World world(gravity);\nconst world = b2World.Create(gravity);\n\nUser Data\nUser data in the c++ version is a pointer to an object you specify.\nIn the TypeScript Version, it's a record of properties. This helps to keep things type safe.\nSee loose ends for more details.\n","title":"Key Differences to C++","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/loose-ends.html","content":"Loose Ends\nUser Data\nThe b2Fixture, b2Body, and b2Joint classes allow you to attach user data.\nThis is handy when you are examining Box2D data structures and you want to\ndetermine how they relate to the objects in your game engine.\nFor example, it is typical to attach an actor reference to the rigid body\non that actor. This sets up a circular reference. If you have the actor,\nyou can get the body. If you have the body, you can get the actor.\nconst actor = GameCreateActor();\nconst bodyDef: b2BodyDef = {\n userData: { actor },\n};\nactor.body = myWorld.CreateBody(bodyDef);\n\nYou can also use this to hold an primitive value rather than a reference.\nHere are some examples of cases where you would need the user data:\n\nApplying damage to an actor using a collision result.\nPlaying a scripted event if the player is inside an axis-aligned box.\nAccessing a game structure when Box2D notifies you that a joint is\ngoing to be destroyed.\n\nKeep in mind that user data is optional and you can put anything in it.\nUser data records are empty by default.\nFor fixtures you might consider defining a user data structure that lets\nyou store game specific information, such as material type, effects\nhooks, sound hooks, etc.\nconst fixtureDef: b2FixtureDef = {\n shape: someShape,\n userData: {\n materialIndex: 2,\n },\n};\n\nconst fixture = body.CreateFixture(fixtureDef);\n\nUser Data Types\nUser data is a Record of references. You can specify the types of user data\nrecords for bodies, fixtures and joints separately.\n// box2dconfig.d.ts\nimport "@box2d/core";\n\ndeclare module "@box2d/core" {\n export interface b2BodyUserDataMap {\n actor: MyActor;\n }\n export interface b2FixtureUserDataMap {\n materialIndex: number;\n }\n export interface b2JointUserDataMap {\n bar: MyBar;\n }\n}\n\nAny property you specify in these b2*UserDataMap interfaces will be optionally available in the respective user data record.\nSince specifying user data properties is optional, you'll need to check if it exists when reading the user data:\nconst myActor: MyActor | undefined = myBody.GetUserData().actor;\nif (myActor !== undefined) {\n // work with the data..\n}\n\nImplicit Destruction\n@box2d is in the JavaScript environment and as such, the garbage collector\nis responsible for removing unused objects. For that to be possible, you need\nto make sure you have no references left to those objects.\nIf you destroy a Box2D entity, it is up to you to make sure you remove\nall references to the destroyed object. This is easy if you only have a\nsingle reference to the entity. If you have multiple references, you\nmight consider implementing a handle class to wrap the reference.\nOften when using Box2D you will create and destroy many bodies, shapes,\nand joints. Managing these entities is somewhat automated by Box2D. If\nyou destroy a body then all associated shapes and joints are\nautomatically destroyed. This is called implicit destruction.\nWhen you destroy a body, all its attached shapes, joints, and contacts\nare destroyed. This is called implicit destruction. Any body connected\nto one of those joints and/or contacts is woken. This process is usually\nconvenient. However, you must be aware of one crucial issue:\n\nCaution:\nWhen a body is destroyed, all fixtures and joints attached to the body\nare automatically destroyed. You must remove any references you have to\nthose shapes and joints. Otherwise, your program will behave strange if\nyou try to access or destroy those shapes or joints later.\n\nTo help you remove your joint references, Box2D provides a listener class\nnamed b2DestructionListener that you can implement and provide to your\nworld object. Then the world object will notify you when a joint is\ngoing to be implicitly destroyed\nNote that there no notification when a joint or fixture is explicitly\ndestroyed. In this case ownership is clear and you can perform the\nnecessary cleanup on the spot. If you like, you can call your own\nimplementation of b2DestructionListener to keep cleanup code\ncentralized.\nImplicit destruction is a great convenience in many cases. It can also\nmake your program fall apart. You may store references to shapes and\njoints somewhere in your code. These references become orphaned when an\nassociated body is destroyed. The situation becomes worse when you\nconsider that joints are often created by a part of the code unrelated\nto management of the associated body. For example, the testbed creates a\nb2MouseJoint for interactive manipulation of bodies on the screen.\nBox2D provides a callback mechanism to inform your application when\nimplicit destruction occurs. This gives your application a chance to\nremove the orphaned references. This callback mechanism is described\nlater in this manual.\nYou can implement a b2DestructionListener that allows b2World to inform\nyou when a shape or joint is implicitly destroyed because an associated\nbody was destroyed. This will help prevent your code from accessing\norphaned references.\nclass MyDestructionListener extends b2DestructionListener {\n override void SayGoodbye(joint: b2Joint) {\n // remove all references to joint.\n }\n};\n\nYou can then register an instance of your destruction listener with your\nworld object. You should do this during world initialization.\nmyWorld.SetDestructionListener(myDestructionListener);\n\nPixels and Coordinate Systems\nRecall that Box2D uses MKS (meters, kilograms, and seconds) units and\nradians for angles. You may have trouble working with meters because\nyour game is expressed in terms of pixels. To deal with this in the\ntestbed I have the whole game work in meters and just use an OpenGL\nviewport transformation to scale the world into screen space.\nconst lowerX = -25,\n upperX = 25,\n lowerY = -5,\n upperY = 25;\nyourOrtho2D(lowerX, upperX, lowerY, upperY);\n\nIf your game must work in pixel units then you should convert your\nlength units from pixels to meters when passing values from Box2D.\nLikewise you should convert the values received from Box2D from meters\nto pixels. This will improve the stability of the physics simulation.\nYou have to come up with a reasonable conversion factor. I suggest\nmaking this choice based on the size of your characters. Suppose you\nhave determined to use 50 pixels per meter (because your character is 75\npixels tall). Then you can convert from pixels to meters using these\nformulas:\nxMeters = 0.02 * xPixels;\nyMeters = 0.02 * yPixels;\n\nIn reverse:\nxPixels = 50 * xMeters;\nyPixels = 50 * yMeters;\n\nYou should consider using MKS units in your game code and just convert\nto pixels when you render. This will simplify your game logic and reduce\nthe chance for errors since the rendering conversion can be isolated to\na small amount of code.\nIf you use a conversion factor, you should try tweaking it globally to\nmake sure nothing breaks. You can also try adjusting it to improve\nstability.\nDebug Drawing\nYou can implement the b2Draw interface to get detailed drawing of the\nphysics world. Here are the available entities:\n\nshape outlines\njoint connectivity\nbroad-phase axis-aligned bounding boxes (AABBs)\ncenter of mass\n\n\nThis is the preferred method of drawing these physics entities, rather\nthan accessing the data directly. The reason is that much of the\nnecessary data is internal and subject to change.\nThe testbed draws physics entities using the debug draw facility and the\ncontact listener, so it serves as the primary example of how to\nimplement debug drawing as well as how to draw contact points.\nLimitations\nBox2D uses several approximations to simulate rigid body physics\nefficiently. This brings some limitations.\nHere are the current limitations:\n\nStacking heavy bodies on top of much lighter bodies is not stable. Stability degrades as the mass ratio passes 10:1.\nChains of bodies connected by joints may stretch if a lighter body is supporting a heavier body. For example, a wrecking ball connect to a chain of light weight bodies may not be stable. Stability degrades as the mass ratio passes 10:1.\nThere is typically around 0.5cm of slop in shape versus shape collision.\nContinuous collision does not handle joints. So you may see joint stretching on fast moving objects.\nBox2D uses the symplectic Euler integration scheme. It does not reproduce parabolic motion of projectiles and has only first-order accuracy. However it is fast and has good stability.\nBox2D uses an iterative solver to provide real-time performance. You will not get precisely rigid collisions or pixel perfect accuracy. Increasing the iterations will improve accuracy.\n\n","title":"Loose Ends","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/overview.html","content":"Overview\nBox2D is a 2D rigid body simulation library for games. Programmers can\nuse it in their games to make objects move in realistic ways and make\nthe game world more interactive. From the game engine's point of view,\na physics engine is just a system for procedural animation.\n@box2d is written in TypeScript. Most of the types defined in the\nengine begin with the b2 prefix, so that existing code and examples\nfrom the C++ version are easily ported.\nPrerequisites\nIn this manual I'll assume you are familiar with basic physics\nconcepts, such as mass, force, torque, and impulses. If not, please\nfirst consult Google search and Wikipedia.\nBox2D was created as part of a physics tutorial at the Game Developer\nConference. You can get these tutorials from the download section of\nbox2d.org.\n@box2d is a TypeScript port of Erin Cattos Box2D.\nThis is a fork of box2d.ts from Isaac Burns (flyover)\nwho did a huge job initially porting Box2D and LiquidFun to TypeScript.\nIn order to avoid changing the whole documentation wording from Box2D to @box2d,\nplease assume them to be synonymous within this documentation unless specified otherwise.\nSince Box2D is written in TypeScript, you are expected to be experienced\nin TypeScript or at least JavaScript programming.\nBox2D should not be your first TypeScript/JavaScript programming project! You\nshould be comfortable with bundling, creating a dev-server, and debugging.\n\nCaution:\nBox2D should not be your first TypeScript/JavaScript project. Please learn TS/JS\nprogramming, bundling and debugging before working with\nBox2D. There are many resources for this on the net.\n\nScope\nThis manual covers the majority of the Box2D API. However, not every\naspect is covered. Please look at the testbed included\nwith Box2D to learn more.\nThis manual is updated with every commit in the master branch,\nso it might be newer than the version you currently have installed.\nNonetheless, since there is no major work going on with the API,\nit should not change too much.\nFeedback and Bugs\nPlease file bugs and feature requests here:\n@box2d Issues\nYou can help to ensure your issue gets fixed if you provide sufficient\ndetail. A testbed example that reproduces the problem is ideal. You can\nread about the testbed later in this document.\nThere is also a Discord server and a\nsubreddit for the C++ version of Box2D\nif you need general help with Box2D (rather than the TypeScript port).\nCore Concepts\nBox2D works with several fundamental concepts and objects. We briefly\ndefine these objects here and more details are given later in this\ndocument.\nShape\nA shape is 2D geometrical object, such as a circle or polygon.\nRigid Body\nA chunk of matter that is so strong that the distance between any two\nbits of matter on the chunk is constant. They are hard like a diamond.\nIn the following discussion we use body interchangeably with rigid body.\nFixture\nA fixture binds a shape to a body and adds material properties such as\ndensity, friction, and restitution. A fixture puts a shape into the\ncollision system (broad-phase) so that it can collide with other shapes.\nConstraint\nA constraint is a physical connection that removes degrees of freedom\nfrom bodies. A 2D body has 3 degrees of freedom (two translation\ncoordinates and one rotation coordinate). If we take a body and pin it\nto the wall (like a pendulum) we have constrained the body to the wall.\nAt this point the body can only rotate about the pin, so the constraint\nhas removed 2 degrees of freedom.\nContact Constraint\nA special constraint designed to prevent penetration of rigid bodies and\nto simulate friction and restitution. You do not create contact\nconstraints; they are created automatically by Box2D.\nJoint\nThis is a constraint used to hold two or more bodies together. Box2D\nsupports several joint types: revolute, prismatic, distance, and more.\nSome joints may have limits and motors.\nJoint Limit\nA joint limit restricts the range of motion of a joint. For example, the\nhuman elbow only allows a certain range of angles.\nJoint Motor\nA joint motor drives the motion of the connected bodies according to the\njoint's degrees of freedom. For example, you can use a motor to drive\nthe rotation of an elbow.\nWorld\nA physics world is a collection of bodies, fixtures, and constraints\nthat interact together. Box2D supports the creation of multiple worlds,\nbut this is usually not necessary or desirable.\nSolver\nThe physics world has a solver that is used to advance time and to\nresolve contact and joint constraints. The Box2D solver is a high\nperformance iterative solver that operates in order N time, where N is\nthe number of constraints.\nContinuous Collision\nThe solver advances bodies in time using discrete time steps. Without\nintervention this can lead to tunneling.\n\nBox2D contains specialized algorithms to deal with tunneling. First, the\ncollision algorithms can interpolate the motion of two bodies to find\nthe first time of impact (TOI). Second, there is a sub-stepping solver\nthat moves bodies to their first time of impact and then resolves the\ncollision.\nModules\nBox2D is composed of three modules: Common, Collision, and Dynamics. The\nCommon module has code for augmentation, math, and settings. The Collision\nmodule defines shapes, a broad-phase, and collision functions/queries.\nFinally the Dynamics module provides the simulation world, bodies,\nfixtures, and joints.\n\nUnits\nBox2D works with floating point numbers and tolerances have to be used\nto make Box2D perform well. These tolerances have been tuned to work\nwell with meters-kilogram-second (MKS) units. In particular, Box2D has\nbeen tuned to work well with moving shapes between 0.1 and 10 meters. So\nthis means objects between soup cans and buses in size should work well.\nStatic shapes may be up to 50 meters long without trouble.\nBeing a 2D physics engine, it is tempting to use pixels as your units.\nUnfortunately this will lead to a poor simulation and possibly weird\nbehavior. An object of length 200 pixels would be seen by Box2D as the\nsize of a 45 story building.\n\nCaution:\nBox2D is tuned for MKS units. Keep the size of moving objects roughly\nbetween 0.1 and 10 meters. You'll need to use some scaling system when\nyou render your environment and actors. The Box2D testbed does this by\nusing an OpenGL viewport transform. DO NOT USE PIXELS.\n\nIt is best to think of Box2D bodies as moving billboards upon which you\nattach your artwork. The billboard may move in a unit system of meters,\nbut you can convert that to pixel coordinates with a simple scaling\nfactor. You can then use those pixel coordinates to place your sprites,\netc. You can also account for flipped coordinate axes.\nAnother limitation to consider is overall world size. If your world units\nbecome larger than 2 kilometers or so, then the lost precision can affect\nstability.\n\nCaution:\nBox2D works best with world sizes less than 2 kilometers. Use\nb2World::ShiftOrigin to support larger worlds.\n\nIf you need to have a larger game world, consider using\nb2World::ShiftOrigin to keep the world origin close to your player. I recommend\nto use grid lines along with some hysteresis for triggering calls to ShiftOrigin.\nThis call should be made infrequently because it is has CPU cost. You may\nneed to store a physics offset when translating between game units and Box2D units.\nBox2D uses radians for angles. The body rotation is stored in radians\nand may grow unbounded. Consider normalizing the angle of your bodies if\nthe magnitude of the angle becomes too large (use b2Body::SetTransform).\n\nCaution:\nBox2D uses radians, not degrees.\n\nChanging The Length Units\nAdvanced users may change the length unit.\nFactories and Definitions\nFast memory management plays a central role in the design of the Box2D\nAPI. So when you create a b2Body or a b2Joint, you need to call the\nfactory functions on b2World. You should never try to allocate these\ntypes in another manner.\nThere are creation functions:\nexport class b2World {\n // ...\n public CreateBody(def: b2BodyDef = {}): b2Body;\n // ...\n // CreateJoint has a couple of overloads:\n public CreateJoint(def: b2IAreaJointDef): b2AreaJoint;\n public CreateJoint(def: b2IDistanceJointDef): b2DistanceJoint;\n public CreateJoint(def: b2IFrictionJointDef): b2FrictionJoint;\n public CreateJoint(def: b2IGearJointDef): b2GearJoint;\n public CreateJoint(def: b2IMotorJointDef): b2MotorJoint;\n public CreateJoint(def: b2IMouseJointDef): b2MouseJoint;\n public CreateJoint(def: b2IPrismaticJointDef): b2PrismaticJoint;\n public CreateJoint(def: b2IPulleyJointDef): b2PulleyJoint;\n public CreateJoint(def: b2IRevoluteJointDef): b2RevoluteJoint;\n public CreateJoint(def: b2IWeldJointDef): b2WeldJoint;\n public CreateJoint(def: b2IWheelJointDef): b2WheelJoint;\n // ...\n\nAnd there are corresponding destruction functions:\nexport class b2World {\n // ...\n public DestroyBody(b: b2Body): void;\n public DestroyJoint(j: b2Joint): void;\n // ...\n\nWhen you create a body or joint, you need to provide a definition. These\ndefinitions contain all the information needed to build the body or\njoint. By using this approach we can prevent construction errors, keep\nthe number of function parameters small, provide sensible defaults, and\nreduce the number of accessors.\nSince fixtures (shapes) must be parented to a body, they are created and\ndestroyed using a factory method on b2Body:\nexport class b2Body {\n // ...\n public CreateFixture(def: b2FixtureDef): b2Fixture;\n public DestroyFixture(fixture: b2Fixture): void;\n // ...\n\nFactories do not retain references to the definitions. So you can reuse\nthem for other further Create* calls.\n","title":"Overview","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/references.html","content":"References\n\nErin Catto's Publications\nCollision Detection in Interactive 3D Environments, Gino van den Bergen, 2004\nReal-Time Collision Detection, Christer Ericson, 2005\n\n","title":"References","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/setup.html","content":"Setup\nThis is a TypeScript port of Box2D by Erin Catto, which means that for the most part you can use the documentation found at the official homepage. If you need examples, you can find them on GitHub.\nWe aim to stay true to the original API where we can, but sometimes it's not possible. We'll cover the differences in the following tutorials.\nInstall With NPM\nnpm i @box2d/core\n\nTypeScript vs JavaScript\nA note on the documentation on this page:\nAll examples are shown in TypeScript, but you can use @box2d with JavaScript as well. Just remove the type information from the examples and you are good to go.\n","title":"Setup","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/testbed.html","content":"Testbed\nOnce you have conquered the HelloWorld example, you should start looking\nat Box2D's testbed. The testbed is a testing framework and demo\nenvironment. Here are some of the features:\n\nCamera with pan and zoom.\nMouse picking of shapes attached to dynamic bodies.\nExtensible set of tests.\nGUI for selecting tests, parameter tuning, and debug drawing options.\nPause and single step simulation.\nText rendering.\n\n\nThe above screenshot is from the C++ version. You can try the @box2d version online:\nhttps://lusito.github.io/box2d.ts/testbed/\nThe testbed has many examples of Box2D usage in the test cases and the\nframework itself. I encourage you to explore and tinker with the testbed\nas you learn Box2D.\nNote: the testbed is written using React.\nThe testbed is not part of the Box2D library.\nThe Box2D library is agnostic about rendering. As shown by\nthe HelloWorld example, you don't need a renderer to use Box2D.\n","title":"Testbed","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/controllers/","content":"@box2d/controllers\nControllers for @box2d/core\n@box2d/controllers is a TypeScript port of Googles LiquidFun controllers.\nThis is a fork of box2d.ts from Isaac Burns (flyover) who did a huge job initially porting Box2D and LiquidFun to TypeScript.\nHow to Use\nCheck out the documentation\nThe @box2d Ecosystem\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js\nCheck out demos and compare performance here: https://lusito.github.io/box2d.ts/\nFair Warning: The whole @box2d ecosystem is in an early stage, so it will probably change a lot before we release the first stable version (1.0.0).\nOther packages included in the ecosystem:\n\nBenchmark: Based on bench2d by joelgwebber\nControllers: From the LiquidFun project\nParticles: Also from the LiquidFun project\nLights: ported from LibGDX\nDebugDraw: Debug drawing using a canvas\nTestbed: A set of demos, partially ports of the original projects, partially new ones.\n\nContributing\nWe're looking for contributors to make this the best place to start with box2d on the web.\nCheck out the project page for more information: https://github.com/Lusito/box2d.ts\n","title":"controllers","projectIndex":{"title":"controllers","url":"https://lusito.github.io/box2d.ts/controllers/"}},{"url":"https://lusito.github.io/box2d.ts/controllers/setup.html","content":"Setup\nThere doesn't seem to be any official documentation for controllers on the original homepage. Make sure to check out the examples instead.\nWe aim to stay true to the original API where we can, but sometimes it's not possible. We'll cover the differences in upcoming tutorials.\nInstall With NPM\nnpm i @box2d/controllers\n\nThis library extends @box2d/core, so make sure to check that out as well.\n","title":"Setup","projectIndex":{"title":"controllers","url":"https://lusito.github.io/box2d.ts/controllers/"}},{"url":"https://lusito.github.io/box2d.ts/lights/","content":"@box2d/lights\n\nA TypeScript port of Kalle Hameleinen's Box2DLights.\n@box2d/lights is a 2D lighting framework that uses @box2d/core for raycasting and WebGL for rendering. This library can be used without @box2d/core, so if your 2D physics library supports raycasting, you might be able to use this as well.\nFeatures\n\nArbitrary number of lights\nGaussian blurred light maps\nPoint light\nCone Light\nDirectional Light\nChain Light (New in 1.3)\nShadows\nDynamic/static/xray light\nCulling\nColored ambient light\nGamma corrected colors\nHandler class to do all the work\nQuery method for testing is point inside of light/shadow\n\nThis library offer easy way to add soft dynamic 2d lights to your physic based game.\nHow to Use\nCheck out the documentation\nAlso checkout the testbed for simple examples.\nThe @box2d Ecosystem\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js\nCheck out demos and compare performance here: https://lusito.github.io/box2d.ts/\nFair Warning: The whole @box2d ecosystem is in an early stage, so it will probably change a lot before we release the first stable version (1.0.0).\nOther packages included in the ecosystem:\n\nBenchmark: Based on bench2d by joelgwebber\nControllers: From the LiquidFun project\nParticles: Also from the LiquidFun project\nLights: ported from LibGDX\nDebugDraw: Debug drawing using a canvas\nTestbed: A set of demos, partially ports of the original projects, partially new ones.\n\nContributing\nWe're looking for contributors to make this the best place to start with box2d on the web.\nCheck out the project page for more information: https://github.com/Lusito/box2d.ts\n","title":"lights","projectIndex":{"title":"lights","url":"https://lusito.github.io/box2d.ts/lights/"}},{"url":"https://lusito.github.io/box2d.ts/lights/custom-viewport.html","content":"Custom Viewport\nThe following is copied from the original documentation and might need to be adjusted for the TypeScript version:\nSupport for custom viewports, e.g. resolution independend viewports is available only since box2dlights 1.3, if you are using older version, please update to at least 1.3 to enable support.\nFor the correct viewport rendering, if not using default values, you should set it manually:\nrayHandler.useCustomViewport(x, y, width, height);\n\nFor example if you use a viewport:\nrayHandler.useCustomViewport(\n viewport.getScreenX(),\n viewport.getScreenY(),\n viewport.getScreenWidth(),\n viewport.getScreenHeight(),\n);\n\nWhere the x, y, width and height are the values of your custom viewport.\nFor correct rendering after the window is resized or viewport sizes changed, you should call this method manually again. E.g. inside of resize(width, height) method.\nWhen no more needed, it could be reset back to default:\nrayHandler.useDefaultViewport();\n\n","title":"Custom Viewport","projectIndex":{"title":"lights","url":"https://lusito.github.io/box2d.ts/lights/"}},{"url":"https://lusito.github.io/box2d.ts/lights/getting-started.html","content":"Getting Started\nPreparations\nSince the @box2d/lights package is written without a dependency to @box2d/core (so it can be used by other physics librares), you'll need to create some glue code. I've preparaed a sample implementation on GitHub. Feel free to just copy/paste that into your code-base.\nSetup\nIn your setup code, write:\nconst rayHandler = new RayHandlerImpl(world, glContext, camera.width / 4, camera.height / 4, viewport.x, viewport.y);\n\nEnable/Disable Shadows\nYou can disable shadows with:\nrayHandler.setShadows(false);\n\nCreating Lights\nAmbient Light\nYou can set the ambient light with\nrayHandler.setAmbientLight(r, g, b, a);\nrayHandler.setBlurNum(3);\n\nPoint Lights\nThis creates a new white point light. RAY_NUM being the number of ray lights (e.g.: 4 is a simple star)\nconst light = new PointLight(rayHandler, RAYS_NUM, new LightColor(1, 1, 1, 1), lightDistance, x, y);\n\nRendering\nIn your render loop after everything is drawn that you want to be lit:\nrayHandler.setCombinedMatrix(camera.combined, center.x, center.y, camera.width, camera.height);\nrayHandler.updateAndRender();\n\nCleanup\nRemember to dispose the ray handler (and lights, etc.) when cleaning up:\nrayHandler.dispose();\n\nExamples\n\nDemos available on GitHub here and here.\nFor more details, check out these examples.\n\n","title":"Getting Started","projectIndex":{"title":"lights","url":"https://lusito.github.io/box2d.ts/lights/"}},{"url":"https://lusito.github.io/box2d.ts/lights/glossary.html","content":"Glossary\nThe following is copied from the original documentation and might need to be adjusted for the TypeScript version:\n\nRay Handler is handler class for all the lights. Ray handler manages updating, rendering and disposing the lights. Ray handler also checks the current OpenGl es version and uses the right mode for rendering. If GLes2.0 is detected: gaussian blurred lights map is enabled and used automatically. GLes2.0 has bigger constant time but it scales better with multiple lights because lights are rendered to small FBO instead of render target and the fragment shader has to do less work.\n\nnew RayHandler(box2dWorld, fboWidth, fboHeight);\n\n\n\nLight Types\n\nPoint Light is first concrete class. It's the simplest and most used light. Point lights have meaningful position and distance and all other lights attributes. Point lights are always circular shaped.\n\nnew PointLight(rayHandler, numRays, color, maxDistance, x, y);\n\n\nCone Light is second concrete class. It's basically a point light but only a sector of the full circle. Cone lights have direction and cone degree as additional parameters. cone degree is aberration of straight line to both directions. Setting Cone Degree to 180 means that you got full circle.\n\nnew ConeLight(rayHandler, numRays, color, maxDistance, x, y, directionDegree, coneDegree);\n\n\nDirectional Light simulate light source that location is at infinite distance. This means that direction and intensity is the same everywhere. -90 direction is straight from up. This type of light is good for simulating the sun.\n\nnew DirectionalLight(rayHandler, numRays, color, directionDegree);\n\n\n\nLight Abstract Classes\n\nLight Abstract class that contain shared parameters and act as "interface" for all the lights. Light is made from a bunch of rays that are tested with raycasting against box2d geometry and from this data the light mesh is constructed and rendered to scene. All lights have meaningful color, number of rays, softShadowLenght and some booleans like active, soft, xray, staticLight.\nPositional Light is also an abstract class. This contain shared parameters between Point- and Cone light. Positional light have position and finite distance.\n\n","title":"Glossary","projectIndex":{"title":"lights","url":"https://lusito.github.io/box2d.ts/lights/"}},{"url":"https://lusito.github.io/box2d.ts/lights/performance-tuning.html","content":"Performance Tuning\nThe following is copied from the original documentation and might need to be adjusted for the TypeScript version:\nObject Creation\n\nRay Handler is heavy weight object. It contains many float arrays and couple FBO's when openGL es 2.0 is available. Ray Handler can be safely used over the application lifetime. Ray Handler need to be disposed when its destroyed.\nLights are moderate weight objects. Every light contain two mesh and if possible try to reuse these lights. Just set light to disabled if you know that you will be needing another that type of light soon. Remove lights if you don't need them anymore or there is big time window between them. Be aware that setActive(false) do not set body reference to null.\n\nPerformance Tips\n\nTips are marked with (CPU | GPU | MEMORY) depending which part of system that tip might help.\nDisabled light do not have any performance overhead. Disable lights are stored on separate list. (CPU & GPU)\nDisable shadows if game is running on platform with GL es 1.0 and where render target do not have alpha channel. Usually this mean android with gles1.0. This maybe can be done automatically in library but don't count on that! (GPU)\nStatic flag make light very cheap. You can use static light even for objects that move but they do it rarely. Static lights can be attached to bodies but they do not follow body movement after initial attach call. Every time you call method that alter static light state it has to be updated. Static lights is wrong choice if light is updated every frame. (CPU)\nXray flag prevent all the raycasting for the light. So this lower cpu load about 80%. Xrays are optimal for small objects and usually dynamic light look better/smoother when combined with xray pointlight. Use this flag allways when you don't need dynamic behavior. (CPU)\nSetting FBO size to small make really big difference with performance. First it helps with light drawing because its use less fillrate. Secondly it help with blurring. One blur pass use 5+5 dynamic texture fetch. With 800x480 screen: using original screen size as fbo size and three blur pass would need over 10 million texture look ups. But using box2dLights defaults settings 200x120(quarter of screen sizes) and one blur pass use only 240 000 texture look ups but yield more smoothed look. (GPU & MEMORY)\nBlur passes are heavyweight, try to smaller fbo combined with more passes if really blurred lights are needed. Idea for blur algorithm can be found here http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ (GPU)\nFor dynamic lights use only that amount rays that are absolute needed. Usually best outcome is somewhere between 5 and 128. For something really accurate and visible you sometimes need more but be careful. For every ray box2dLights have to use one raycast and 1 vertex + 2 for soft light.(CPU & GPU & MEMORY)\nLight distance make light a bit heavier. Ray Cast need to travel bigger distance and more pixels need to drawn. For bigger light try first add color alpha channel and maybe add some white color. If that does not help try to use different fallof scheme(Still WIP).(CPU & GPU)\nSet culling enabled. Culling is very lightweight process but safe all raycast calculations and drawing. Lights are culled automatically if they are so much off screen that even lights tips would never occur in screen. Culled light is still not free so if you know something is behind camera really long time maybe that could be disabled.(CPU & GPU)\nBe reasonable with soft light distance and turn softness off it that is not needed.(CPU & GPU)\nOnly create rayHandler with maxNumberRays that are needed (MEMORY)\nIf physics are stepped more sparsely than game is rendered ray handler update can be skipped if physic did not skipped between frames. If physics are updated more often that game is rendered then calling updateAndRender is best choice.(CPU)\nIf not single lights are rendered blurring and light map blitting is skipped. So cull/disable lights that are not visible (GPU)\n\n","title":"Performance Tuning","projectIndex":{"title":"lights","url":"https://lusito.github.io/box2d.ts/lights/"}},{"url":"https://lusito.github.io/box2d.ts/lights/setup.html","content":"Setup\nThis is a TypeScript port of Box2D Lights from LibGDX.\nEven though it is part of the @box2d ecosystem, you can actually use this one with other physics libraries as well.\nWe aim to stay true to the original API where we can, but sometimes it's not possible. We'll cover the differences in the following tutorials.\nInstall With NPM\nnpm i @box2d/lights\n\nThis library is theoretically physics-engine agnostic, so you should be able to use it with whatever 2D physics library you are using. It was mainly designed to work well with @box2d/core, so make sure to check that out as well.\n","title":"Setup","projectIndex":{"title":"lights","url":"https://lusito.github.io/box2d.ts/lights/"}},{"url":"https://lusito.github.io/box2d.ts/particles/","content":"@box2d/particles\nParticles for @box2d/core\n@box2d/particles is a TypeScript port of Googles LiquidFun particles.\nThis is a fork of box2d.ts from Isaac Burns (flyover) who did a huge job initially porting Box2D and LiquidFun to TypeScript.\nHow to Use\nCheck out the documentation\nThe @box2d Ecosystem\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js\nCheck out demos and compare performance here: https://lusito.github.io/box2d.ts/\nFair Warning: The whole @box2d ecosystem is in an early stage, so it will probably change a lot before we release the first stable version (1.0.0).\nOther packages included in the ecosystem:\n\nBenchmark: Based on bench2d by joelgwebber\nControllers: From the LiquidFun project\nParticles: Also from the LiquidFun project\nLights: ported from LibGDX\nDebugDraw: Debug drawing using a canvas\nTestbed: A set of demos, partially ports of the original projects, partially new ones.\n\nContributing\nWe're looking for contributors to make this the best place to start with box2d on the web.\nCheck out the project page for more information: https://github.com/Lusito/box2d.ts\n","title":"particles","projectIndex":{"title":"particles","url":"https://lusito.github.io/box2d.ts/particles/"}},{"url":"https://lusito.github.io/box2d.ts/particles/setup.html","content":"Setup\nThis is a TypeScript port of the particles code found in LiquidFun by Google. For now, you'll have to read the official documentation and adjust it to the TypeScript version. Make sure to check out the examples as well.\nWe aim to stay true to the original API where we can, but sometimes it's not possible. We'll cover the differences in upcoming tutorials.\nInstall With NPM\nnpm i @box2d/particles\n\nThis library extends @box2d/core, so make sure to check that out as well.\n// TODO: https://github.com/google/liquidfun/tree/master/liquidfun/Box2D/Box2D/Documentation/Programmers-Guide\n","title":"Setup","projectIndex":{"title":"particles","url":"https://lusito.github.io/box2d.ts/particles/"}},{"url":"https://lusito.github.io/box2d.ts/debug-draw/","content":"@box2d/debug-draw\nBox2D is a 2D physics engine for games.\n@box2d/debug-draw is a TypeScript port of Erin Cattos debug drawing helper for Box2D.\nThis is a fork of box2d.ts from Isaac Burns (flyover) who did a huge job initially porting Box2D and LiquidFun to TypeScript.\nHow to Use\nCheck out the documentation\nThe @box2d Ecosystem\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js\nCheck out demos and compare performance here: https://lusito.github.io/box2d.ts/\nFair Warning: The whole @box2d ecosystem is in an early stage, so it will probably change a lot before we release the first stable version (1.0.0).\nOther packages included in the ecosystem:\n\nBenchmark: Based on bench2d by joelgwebber\nControllers: From the LiquidFun project\nParticles: Also from the LiquidFun project\nLights: ported from LibGDX\nDebugDraw: Debug drawing using a canvas\nTestbed: A set of demos, partially ports of the original projects, partially new ones.\n\nContributing\nWe're looking for contributors to make this the best place to start with box2d on the web.\nCheck out the project page for more information: https://github.com/Lusito/box2d.ts\n","title":"debug-draw","projectIndex":{"title":"debug-draw","url":"https://lusito.github.io/box2d.ts/debug-draw/"}},{"url":"https://lusito.github.io/box2d.ts/debug-draw/setup.html","content":"Setup\n@box2d/debug-draw is a TypeScript port of Erin Cattos debug drawing helper for Box2D.\nThis is a fork of box2d.ts from Isaac Burns (flyover) who did a huge job initially porting Box2D and LiquidFun to TypeScript.\nInstall With NPM\nnpm i @box2d/debug-draw\n\nThis library extends @box2d/core, so make sure to check that out as well.\n","title":"Setup","projectIndex":{"title":"debug-draw","url":"https://lusito.github.io/box2d.ts/debug-draw/"}},{"url":"https://lusito.github.io/box2d.ts/debug-draw/usage.html","content":"Usage\nIn order to draw debug graphics, you'll need a canvas element in your HTML. Make sure, that it is an overlay above your game canvas (same position and size). You might also want to set the style pointer-events: none.\nCreate a New Instance\n// Somewhere at the start, find the element and create a new DebugDraw instance:\nfunction initDebugDraw() {\n const canvas = document.querySelector("#debug-canvas") as HTMLCanvasElement | null;\n if (!canvas) throw new Error("Could not find debug canvas!");\n\n const ctx = canvas.getContext("2d");\n if (!ctx) throw new Error("Could not create 2d context for debug-draw");\n return new DebugDraw(ctx);\n}\n\nPrepare Drawing\nimport { DebugDraw } from "@box2d/debug-draw";\nimport { XY, b2AABB, DrawShapes, DrawJoints, DrawAABBs, DrawCenterOfMasses, DrawPairs } from "@box2d/core";\nimport { DrawParticleSystems } from "@box2d/particles";\nimport { DrawControllers } from "@box2d/controllers";\n\n// After you ran your world.Step(), draw the world like this:\nfunction drawDebug(draw: DebugDraw, center: XY, zoom: number, aabb?: b2AABB) {\n draw.Prepare(center.x, center.y, zoom, true);\n\n // Draw whatever you want here:\n DrawShapes(draw, this.m_world, aabb);\n DrawParticleSystems(draw, this.m_world);\n DrawJoints(draw, this.m_world);\n DrawAABBs(draw, this.m_world, aabb);\n DrawPairs(draw, this.m_world);\n DrawCenterOfMasses(draw, this.m_world);\n DrawControllers(draw, this.m_world);\n\n draw.Finish();\n}\n\nNotice how DrawShapes and DrawAABBs have a third, optional parameter of type AABB.\nThis is a performance optimization if you have a big world. Give it the AABB of your camera to reduce the load.\nThe other drawing functions might receive such a parameter in the future as well, but currently don't support it.\nCustom Renderer\nIn case you want to draw to WebGL directly instead of a canvas, you can write your own b2Draw implementation. You won't need this library anymore, but you can take a look at its code for a reference.\n","title":"Usage","projectIndex":{"title":"debug-draw","url":"https://lusito.github.io/box2d.ts/debug-draw/"}}] \ No newline at end of file +[{"url":"https://lusito.github.io/box2d.ts/","content":"@box2d Monorepository\n\n\n\n\n\nWork in Progress of a full Box2D ecosystem for the web.\n\nThis project is kept in sync with the original Box2D project (using a special tool to easily compare differences to upstream)!\nYou only need to install what you actually want. Don't need particles or 2D lights? Then just install the core.\nCheck out the demos and the benchmarks\n\nIncluded Libraries:\n\n@box2d/core, a TypeScript port of Box2D\n@box2d/controllers, a TypeScript port of LiquidFun's controllers\n@box2d/lights, a TypeScript port of Box2D Lights\n@box2d/particles, a TypeScript port of LiquidFun's particles\n@box2d/debug-draw, a TypeScript port Box2D's debug drawing helper\n\nQuick Start\nThis monorepo is in it's early stage, so to get started, you'll have to taka a look at the testbed project for now.\nMonorepo Commands:\nMost important commands to execute from the root folder (you need npm 18 installed):\n\nnpm ci -> install dependencies\nnpm run build -> build all projects\nnpm run build:libs -> build only the libraries\nnpm run build:testbed -> build the testbed\nnpm run credit "<username>" <type> -> Add user to all contributors list. Use quotes, as otherwise wrong people get added.\nnpm run start -> Run testbed locally\nnpm run bench -> Run the benchmark using node.js\nnpm run bench:web -> Start a webserver for running the benchmarks using a browser,\nnpm run lint -> Run linters, formatters, etc.\nnpm run lint:fix -> Run linters, formatters, etc. and autofix if possible\nnpm run release -> Release one of the libraries\n\nThe @box2d Ecosystem\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js\nCheck out demos and compare performance here: https://lusito.github.io/box2d.ts/\nFair Warning: The whole @box2d ecosystem is in an early stage, so it will probably change a lot before we release the first stable version (1.0.0).\nOther packages included in the ecosystem:\n\nBenchmark: Based on bench2d by joelgwebber\nControllers: From the LiquidFun project\nParticles: Also from the LiquidFun project\nLights: ported from LibGDX\nDebugDraw: Debug drawing using a canvas\nTestbed: A set of demos, partially ports of the original projects, partially new ones.\n\nContributing\nWe're looking for contributors to make this the best place to start with box2d on the web.\nCheck out the project page for more information: https://github.com/Lusito/box2d.ts\nContributors ✨\nThanks goes to these wonderful people (emoji key):\n\n\n\n\n\n \n Erin Catto💻\n Isaac Burns💻 📦\n Maxime Veber💻\n finscn💻\n lusito💻 🚧\n Daniel Zhang🤔\n \n\n\n\n\n\nThis project follows the all-contributors specification. Contributions of any kind welcome!\n","title":"@box2d Monorepository"},{"url":"https://lusito.github.io/box2d.ts/core/","content":"@box2d/core\nBox2D is a 2D physics engine for games.\n@box2d/core is a TypeScript port of Erin Cattos Box2D.\nThis is a fork of box2d.ts from Isaac Burns (flyover) who did a huge job initially porting Box2D and LiquidFun to TypeScript.\nHow to Use\nCheck out the documentation\nThe @box2d Ecosystem\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js\nCheck out demos and compare performance here: https://lusito.github.io/box2d.ts/\nFair Warning: The whole @box2d ecosystem is in an early stage, so it will probably change a lot before we release the first stable version (1.0.0).\nOther packages included in the ecosystem:\n\nBenchmark: Based on bench2d by joelgwebber\nControllers: From the LiquidFun project\nParticles: Also from the LiquidFun project\nLights: ported from LibGDX\nDebugDraw: Debug drawing using a canvas\nTestbed: A set of demos, partially ports of the original projects, partially new ones.\n\nContributing\nWe're looking for contributors to make this the best place to start with box2d on the web.\nCheck out the project page for more information: https://github.com/Lusito/box2d.ts\n","title":"core","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/faq.html","content":"FAQ\nWhat Is Box2D?\nBox2D is a feature rich 2D rigid body physics engine, written in C++ by Erin Catto. It has been used in many games, including Crayon Physics Deluxe, winner of the 2008 Independant Game Festival Grand Prize.\nBox2D uses the MIT license license and can be used free of charge.\nWhat Is @box2d?\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js.\nIt includes more libraries to enable light rendering, liquid simulation and more.\n@box2d/core represents the original Box2D part.\nWho Makes It?\nErin Catto is the driving force behind Box2D, with various others supporting the ports. Box2D is an open source project, and accepts community feedback.\nSanto Pfingsten is the author of @box2d, building on previous work by from Isaac Burns (flyover).\n@box2d is also an open source project, partially on different licenses (depending on which part). This was not an active choice,\nbut was due to the fact, that other libraries, which have been ported to TypeScript have been released under a different license.\nCheck out each package for their license to see what is being used. @box2d/core for example uses the original MIT license from Box2D.\nHow Do I Get Help?\nYou should read the documentation and the rest of this FAQ first. Also, you should study the examples included in the source distribution. Then you can visit the subreddit to ask any remaining questions.\nPlease to not PM or email Erin Catto for support. It is best to ask questions in the forum so that everyone can benefit from the discussion.\nDocumentation\nWhy Isn't Feature Foo Documented?\nIf you grab the latest code from the git master branch you will likely find features that are not documented in the manual. New features are added to the manual after they are mature and a new point release is imminent. However, all major features added to Box2D are accompanied by example code in the testbed to test the feature and show the intended usage.\nPrerequisites\nProgramming\nYou should have a working knowledge of TypeScript or at least JavaScript before you use Box2D. You should understand classes, inheritance, references, and the implications of garbage collection. There are plenty of resources on the web for learning TypeScript/JavaScript. You should also understand your development environment: bundling, the dev-server, and debugging.\nMath and Physics\nYou should have a basic knowledge of rigid bodies, force, torque, and impulses. If you come across a math or physics concept you don't understand, please read about it on Wikipedia. Visit this page if you want a deeper knowledge of the algorithms used in Box2D.\nAPI\nWhat Units Does Box2D Use?\nBox2D is tuned for meters-kilograms-seconds (MKS). Your moving objects should be between 0.1 - 10 meters. Do not use pixels as units! You will get a jittery simulation.\nHow Do I Convert Pixels to Meters?\nSuppose you have a sprite for a character that is 100x100 pixels. You decide to use a scaling factor that is 0.01. This will make the character physics box 1m x 1m. So go make a physics box that is 1x1. Now suppose the character starts out at pixel coordinate (345,679). So position the physics box at (3.45,6.79). Now simulate the physics world. Suppose the character physics box moves to (2.31,4.98), so move your character sprite to pixel coordinates (231,498).\nNow the only tricky part is choosing a scaling factor. This really depends on your game. You should try to get your moving objects in the range 0.1 - 10 meters, with 1 meter being the sweet spot.\nCan I Use @box2d With Just JavaScript\nLike most npm packages written in TypeScript, @box2d is transpiled to JavaScript + types, so you can use it with JavaScript. You will get a better experience with TypeScript though.\nRendering\nWhat Are Box2D's Rendering Capabilities?\nBox2D is only a physics engine. How you draw stuff is up to you.\nBut The Testbed Draws Stuff?\nVisualization is very important for debugging collision and physics. I wrote the test bed to help me test Box2D and give you examples of how to use Box2D. The TestBed is not part of the Box2D library.\nHow Do I Draw Shapes?\nDrawing shapes is not supported and shape internal data is likely to change. Instead you should implement the b2Draw interface.\nYou can use the @core/debug-draw library for simple debugging on a canvas or use it as a reference for your own renderer.\nAccuracy\nBox2D uses approximate methods for a few reasons.\n\nPerformance\nSome differential equations don't have known solutions\nSome constraints cannot be determined uniquely\n\nWhat this means is that constraints are not perfectly rigid and sometimes you will see some bounce even when the restitution is zero.\nBox2D uses Gauss-Seidel to approximately solve constraints.\nBox2D also uses Semi-implicit Euler to approximately solve the differential equations.\nBox2D also does not have exact collision. Polygons are covered with a thin skin (around 0.5cm thick) to avoid numerical problems. This can sometimes lead to unexpected contact normals. Also, some shapes may begin to overlap and then be pushed apart by the solver.\nMaking Games\nWorms Clones\nMaking a worms clone requires arbitrarily destructible terrain. This is beyond the scope of Box2D, so you will have to figure out how to do this on your own.\nTile Based Environment\nUsing many boxes for your terrain may not work well because box-like characters can get snagged on internal corners. A future update to Box2D should allow for smooth motion over edge chains. In general you should avoid using a rectangular character because collision tolerances will still lead to undesirable snagging.\nAsteroid Type Coordinate Systems\nBox2D does not have any support for coordinate frame wrapping. You would likely need to customize Box2D for this purpose. You may need to use a different broad-phase for this to work.\nDeterminism\nIs Box2D Deterministic?\nFor the same input, and same browser, @box2d will reproduce any simulation. Box2D does not use any random numbers nor base any computation on random events (such as timers, etc).\nHowever, people often want more stringent determinism. People often want to know if Box2D can produce identical results on different browsers and on different platforms. The answer is no. The reason for this answer has to do with how floating point math is implemented in many compilers and processors. I recommend reading this article if you are curious: http://www.yosefk.com/blog/consistency-how-to-defeat-the-purpose-of-ieee-floating-point.html\nBut I Really Want Determinism\nThis naturally leads to the question of fixed-point math. Box2D does not support fixed-point math. In the past Box2D was ported to the NDS in fixed-point and apparently it worked okay. Fixed-point math is slower and more tedious to develop, so I have chosen not to use fixed-point for the development of Box2D.\nWhy Is The Restitution/Friction Mixing Inaccurate?\nA physically correct restitution value must be measured in experiments. But as soon as you change the geometry from the experiment then the value is wrong. Next, adding simultaneous collision makes the answer worse. We've been down this road before.\nSo the question of accuracy has been answered: failure.\nThe only remaining question is how do we make it convenient. On this opinions may vary.\nb2Settings is just that. Settings you can adjust if you know what you are doing. See Settings for more details.\nWhat Are The Biggest Mistakes Made By New Users?\n\nUsing pixels for length instead of meters.\nExpecting Box2D to give pixel perfect results.\nUsing b2Polygon to create concave polygons.\nNot learning TypeScript/JavaScript before using Box2D.\nNot reading this FAQ. :)\n\n","title":"FAQ","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/collision.html","content":"Collision Module\nThe Collision module contains shapes and functions that operate on them.\nThe module also contains a dynamic tree and broad-phase to acceleration\ncollision processing of large systems.\nThe collision module is designed to be usable outside of the dynamic\nsystem. For example, you can use the dynamic tree for other aspects of\nyour game besides physics.\nHowever, the main purpose of Box2D is to provide a rigid body physics\nengine, so the using the collision module by itself may feel limited for\nsome applications. Likewise, I will not make a strong effort to document\nit or polish the APIs.\nShapes\nShapes describe collision geometry and may be used independently of\nphysics simulation. At a minimum, you should understand how to create\nshapes that can be later attached to rigid bodies.\nBox2D shapes implement the b2Shape base class. The base class defines\nfunctions to:\n\nTest a point for overlap with the shape.\nPerform a ray cast against the shape.\nCompute the shape's AABB.\nCompute the mass properties of the shape.\n\nIn addition, each shape has a type member and a radius. The radius even\napplies to polygons, as discussed below.\nKeep in mind that a shape does not know about bodies and stand apart\nfrom the dynamics system. Shapes are stored in a compact form that is\noptimized for size and performance. As such, shapes are not easily moved\naround. You have to manually set the shape vertex positions to move a\nshape. However, when a shape is attached to a body using a fixture, the\nshapes move rigidly with the host body. In summary:\n\nWhen a shape is not attached to a body, you can view it's vertices as being expressed in world-space.\nWhen a shape is attached to a body, you can view it's vertices as being expressed in local coordinates.\n\nCircle Shapes\nCircle shapes have a position and radius. Circles are solid. You cannot\nmake a hollow circle using the circle shape.\nconst circle = new b2CircleShape();\nconst radius = 0.5;\ncircle.Set({ x: 2, y: 3 }, radius);\n\nPolygon Shapes\nPolygon shapes are solid convex polygons. A polygon is convex when all\nline segments connecting two points in the interior do not cross any\nedge of the polygon. Polygons are solid and never hollow. A polygon must\nhave 3 or more vertices.\n\nPolygons vertices are stored with a counter clockwise winding (CCW). We\nmust be careful because the notion of CCW is with respect to a\nright-handed coordinate system with the z-axis pointing out of the\nplane. This might turn out to be clockwise on your screen, depending on\nyour coordinate system conventions.\n\nThe polygon members are public, but you should use initialization\nfunctions to create a polygon. The initialization functions create\nnormal vectors and perform validation.\nYou can create a polygon shape by passing in a vertex array. The maximal\nsize of the array is controlled by b2_maxPolygonVertices which has a\ndefault value of 8. This is sufficient to describe most convex polygons.\nThe b2PolygonShape::Set function automatically computes the convex hull\nand establishes the proper winding order. This function is fast when the\nnumber of vertices is low. If you increase b2_maxPolygonVertices, then\nthe convex hull computation might become slow. Also note that the convex\nhull function may eliminate and/or re-order the points you provide.\nVertices that are closer than b2_linearSlop may be merged.\n// This defines a triangle in CCW order.\nconst vertices: XY[] = [\n { x: 0, y: 0 },\n { x: 1, y: 0 },\n { x: 0, y: 1 },\n];\n\nconst polygon = new b2PolygonShape();\npolygon.Set(vertices, vertices.length);\n\nThe polygon shape has a convenience function to create boxes.\n// on b2PolygonShape\npublic SetAsBox(hx: number, hy: number, center?: XY, angle = 0): b2PolygonShape;\n\nPolygons inherit a radius from b2Shape. The radius creates a skin around\nthe polygon. The skin is used in stacking scenarios to keep polygons\nslightly separated. This allows continuous collision to work against the\ncore polygon.\n\nThe polygon skin helps prevent tunneling by keeping the polygons\nseparated. This results in small gaps between the shapes. Your visual\nrepresentation can be larger than the polygon to hide any gaps.\n\nNote that polygon skin is only provided to help with continuous collision.\nThe purpose is not to simulate rounded polygons.\nEdge Shapes\nEdge shapes are line segments. These are provided to assist in making a\nfree-form static environment for your game. A major limitation of edge\nshapes is that they can collide with circles and polygons but not with\nthemselves. The collision algorithms used by Box2D require that at least\none of two colliding shapes have volume. Edge shapes have no volume, so\nedge-edge collision is not possible.\n// This an edge shape.\nconst v1: XY = { x: 0, y: 0 };\nconst v2: XY = { x: 1, y: 0 };\n\nconst edge = new b2EdgeShape();\nedge.SetTwoSided(v1, v2);\n\nIn many cases a game environment is constructed by connecting several\nedge shapes end-to-end. This can give rise to an unexpected artifact\nwhen a polygon slides along the chain of edges. In the figure below we\nsee a box colliding with an internal vertex. These ghost collisions\nare caused when the polygon collides with an internal vertex generating\nan internal collision normal.\n\nIf edge1 did not exist this collision would seem fine. With edge1\npresent, the internal collision seems like a bug. But normally when\nBox2D collides two shapes, it views them in isolation.\nFortunately, the edge shape provides a mechanism for eliminating ghost\ncollisions by storing the adjacent ghost vertices. Box2D uses these\nghost vertices to prevent internal collisions.\n\nThe Box2D algorithm for dealing with ghost collisions only supports\none-sided collision. The front face is to the right when looking from the first\nvertex towards the second vertex. This matches the CCW winding order\nused by polygons.\n// This is an edge shape with ghost vertices.\nconst v0: XY = { x: 1.7, y: 0 };\nconst v1: XY = { x: 1, y: 0.25 };\nconst v2: XY = { x: 0, y: 0 };\nconst v3: XY = { x: -1.7, y: 0.4 };\n\nconst edge = new b2EdgeShape();\nedge.SetOneSided(v0, v1, v2, v3);\n\nIn general stitching edges together this way is a bit wasteful and\ntedious. This brings us to chain shapes.\nChain Shapes\nThe chain shape provides an efficient way to connect many edges together\nto construct your static game worlds. Chain shapes automatically\neliminate ghost collisions and provide one-sided collision. The collision is\none-sided to eliminate ghost collisions.\nIf you don't care about ghost collisions, you can just create a bunch of\ntwo-sided edge shapes. The efficiency is similar.\nThe simplest way to use chain shapes is to create loops. Simply provide an\narray of vertices.\nconst vs: XY[] = [\n { x: 1.7, y: 0 },\n { x: 1, y: 0.25 },\n { x: 0, y: 0 },\n { x: -1.7, y: 0.4 },\n];\n\nconst chain = new b2ChainShape();\nchain.CreateLoop(vs, vs.length);\n\nThe edge normal depends on the winding order. A counter-clockwise winding order orients the normal outwards and a clockwise winding order orients the normal inwards.\n\n\nYou may have a scrolling game world and would like to connect several chains together.\nYou can connect chains together using ghost vertices, like we did with b2EdgeShape.\n\n// on b2ChainShape\npublic CreateChain(\n vertices: XY[],\n count: number,\n prevVertex: Readonly<XY>,\n nextVertex: Readonly<XY>,\n ): b2ChainShape {\n\nSelf-intersection of chain shapes is not supported. It might work, it\nmight not. The code that prevents ghost collisions assumes there are no\nself-intersections of the chain. Also, very close vertices can cause\nproblems. Make sure all your edges are longer than b2_linearSlop (5mm).\n\nEach edge in the chain is treated as a child shape and can be accessed\nby index. When a chain shape is connected to a body, each edge gets its\nown bounding box in the broad-phase collision tree.\n// Visit each child edge.\nfor (let i = 0; i < chain.GetChildCount(); ++i) {\n const edge = new b2EdgeShape();\n chain.GetChildEdge(edge, i);\n\n ...\n}\n\nGeometric Queries\nYou can perform a couple geometric queries on a single shape.\nShape Point Test\nYou can test a point for overlap with a shape. You provide a transform\nfor the shape and a world point.\nconst transform = new b2Transform();\ntransform.SetIdentity();\nconst point: XY = { x: 5, 2 };\n\nconst hit = shape.TestPoint(transform, point);\n\nEdge and chain shapes always return false, even if the chain is a loop.\nShape Ray Cast\nYou can cast a ray at a shape to get the point of first intersection and normal vector. A child index is included for chain shapes because the ray cast will only check a single edge at a time.\n\nCaution:\nNo hit will register if the ray starts inside a convex shape like a circle or polygon. This is consistent with Box2D treating convex shapes as solid.\n\nconst transform = new b2Transfrom();\ntransform.SetIdentity();\n\nconst input = new b2RayCastInput();\ninput.p1.Set(0, 0);\ninput.p2.Set(1, 0);\ninput.maxFraction = 1;\nconst childIndex = 0;\n\nconst output = new b2RayCastOutput();\nconst hit = shape.RayCast(output, input, transform, childIndex);\n\nif (hit) {\n const hitPoint = input.p1.Clone().AddScaled(output.fraction, Math.Subtract(input.p2, input.p1, new b2Vec2()));\n // Keep in mind, that the above line might be bad for garbage collection. Avoid Clone() and new b2Vec2() for code that runs often!\n ...\n}\n\nPairwise Functions\nThe Collision module contains functions that take a pair of shapes and compute some results. These include:\n\nOverlap\nContact manifolds\nDistance\nTime of impact\n\nOverlap\nYou can test two shapes for overlap using this function:\nconst xfA: b2Transform = ..., xfB: b2Transform = ...;\nconst overlap = b2TestOverlap(shapeA, indexA, shapeB, indexB, xfA, xfB);\n\nAgain you must provide child indices to for the case of chain shapes.\nContact Manifolds\nBox2D has functions to compute contact points for overlapping shapes. If\nwe consider circle-circle or circle-polygon, we can only get one contact\npoint and normal. In the case of polygon-polygon we can get two points.\nThese points share the same normal vector so Box2D groups them into a\nmanifold class. The contact solver takes advantage of this to\nimprove stacking stability.\n\nNormally you don't need to compute contact manifolds directly, however\nyou will likely use the results produced in the simulation.\nThe b2Manifold class holds a normal vector and up to two contact\npoints. The normal and points are held in local coordinates. As a\nconvenience for the contact solver, each point stores the normal and\ntangential (friction) impulses.\nThe data stored in b2Manifold is optimized for internal use. If you need\nthis data, it is usually best to use the b2WorldManifold class to\ngenerate the world coordinates of the contact normal and points. You\nneed to provide a b2Manifold and the shape transforms and radii.\nconst worldManifold = new b2WorldManifold();\nworldManifold.Initialize(manifold, transformA, shapeA.m_radius,\ntransformB, shapeB.m_radius);\n\nfor (let i = 0; i < manifold.pointCount; ++i) {\n const point = worldManifold.points[i];\n ...\n}\n\nNotice that the world manifold uses the point count from the original\nmanifold.\nDuring simulation shapes may move and the manifolds may change. Points\nmay be added or removed. You can detect this using b2GetPointStates.\nconst state1: b2PointState[] = [];\nconst state2: b2PointState[] = [];\nb2GetPointStates(state1, state2, manifold1, manifold2);\n\nif (state1[0] === b2_removeState) {\n // process event\n}\n\nDistance\nThe b2Distance function can be used to compute the distance between two\nshapes. The distance function needs both shapes to be converted into a\nb2DistanceProxy. There is also some caching used to warm start the\ndistance function for repeated calls.\n\nTime of Impact\nIf two shapes are moving fast, they may tunnel through each other in a\nsingle time step.\n\nThe b2TimeOfImpact function is used to determine the time when two\nmoving shapes collide. This is called the time of impact (TOI). The\nmain purpose of b2TimeOfImpact is for tunnel prevention. In particular,\nit is designed to prevent moving objects from tunneling outside of\nstatic level geometry.\nThis function accounts for rotation and translation of both shapes,\nhowever if the rotations are large enough, then the function may miss a\ncollision. However the function will still report a non-overlapped time\nand will capture all translational collisions.\nThe time of impact function identities an initial separating axis and\nensures the shapes do not cross on that axis. This might miss collisions\nthat are clear at the final positions. While this approach may miss some\ncollisions, it is very fast and adequate for tunnel prevention.\n\n\nIt is difficult to put a restriction on the rotation magnitude. There\nmay be cases where collisions are missed for small rotations. Normally,\nthese missed rotational collisions should not harm game play. They tend\nto be glancing collisions.\nThe function requires two shapes (converted to b2DistanceProxy) and two\nb2Sweep instances. The sweep class defines the initial and final\ntransforms of the shapes.\nYou can use fixed rotations to perform a shape cast. In this case, the\ntime of impact function will not miss any collisions.\nDynamic Tree\nThe b2DynamicTree class is used by Box2D to organize large numbers of\nshapes efficiently. The class does not know about shapes. Instead it\noperates on axis-aligned bounding boxes (AABBs) with user data pointers.\nThe dynamic tree is a hierarchical AABB tree. Each internal node in the\ntree has two children. A leaf node is a single user AABB. The tree uses\nrotations to keep the tree balanced, even in the case of degenerate\ninput.\nThe tree structure allows for efficient ray casts and region queries.\nFor example, you may have hundreds of shapes in your scene. You could\nperform a ray cast against the scene in a brute force manner by ray\ncasting each shape. This would be inefficient because it does not take\nadvantage of shapes being spread out. Instead, you can maintain a\ndynamic tree and perform ray casts against the tree. This traverses the\nray through the tree skipping large numbers of shapes.\nA region query uses the tree to find all leaf AABBs that overlap a query\nAABB. This is faster than a brute force approach because many shapes can\nbe skipped.\n\n\nNormally you will not use the dynamic tree directly. Rather you will go\nthrough the b2World class for ray casts and region queries. If you plan\nto instantiate your own dynamic tree, you can learn how to use it by\nlooking at how Box2D uses it.\nBroad-Phase\nCollision processing in a physics step can be divided into narrow-phase\nand broad-phase. In the narrow-phase we compute contact points between\npairs of shapes. Imagine we have N shapes. Using brute force, we would\nneed to perform the narrow-phase for N*N/2 pairs.\nThe b2BroadPhase class reduces this load by using a dynamic tree for\npair management. This greatly reduces the number of narrow-phase calls.\nNormally you do not interact with the broad-phase directly. Instead,\nBox2D creates and manages a broad-phase internally. Also, b2BroadPhase\nis designed with Box2D's simulation loop in mind, so it is likely not\nsuited for other use cases.\n","title":"Collision Module","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/common.html","content":"Common Module\nThe Common module contains settings, helpers, and vector math.\nSettings\nBox2D defines several constants. These are all documented in\nb2_settings.ts. Normally you do not need to adjust these constants.\nBox2D uses floating point math for collision and simulation. Due to\nround-off error some numerical tolerances are defined. Some tolerances\nare absolute and some are relative. Absolute tolerances use MKS units.\nAdjusting Settings\nIn order to adjust the above settings, you'll need to create a new file in your code box2d_config.ts with the following content:\nimport { configure } from "@box2d/core/config";\n\nconfigure({\n lengthUnitsPerMeter: 2, // default 1\n maxPolygonVertices: 10, // default 8\n});\n\nThis file must be imported before any box2d imports. Ideally at the top of your entrypoint. For example:\n// index.ts\nimport "./box2d_config.ts";\n// ... other imports\nimport { b2_lengthUnitsPerMeter, b2_maxPolygonVertices } from "@box2d/core";\n\n// The following should show your changes from above:\nconsole.log({\n b2_lengthUnitsPerMeter,\n b2_maxPolygonVertices,\n});\n\nVersion\nThe b2_version constant from b2_common.ts holds the current C++ reference version. It does not (yet) correlate to the current @box2d version.\nYou should ignore this for now. This might change in the future.\nMath\nBox2D includes a simple small vector and matrix module. This has been\ndesigned to suit the internal needs of Box2D and the API. All the\nmembers are exposed, so you may use them freely in your application.\nThe math library is kept simple to make Box2D easy to port and maintain.\nReadonly Types\nIn order to avoid mistakes like adding to a vector that wasn't meant to be modified, we've introduced a type helper b2Readonly.\nIt strips away all the modifying methods of @box2d/cores math types.\nExample\nBefore b2Readonly was introduced, you might have accidentally done something like this:\nfunction getOffsetPosition(body: b2Body) {\n return body.GetPosition().Add({x: 10, y: 20);\n}\n\nTypeScript would not complain about this, but the body position would have been modified, as b2Vec2::Add does not return a new vector, but instead modifies its own values.\nThe return type of GetPosition was Readonly<b2Vec2>, but that only changed the properties to readonly and did not remove the methods that might internally modify the properties.\nNow, getPosition returns b2Readonly<b2Vec2>, which only contains readonly properties and methods that do not modify the state of the class instance.\nSo the above code will return an error: Property 'Add' does not exist on type 'Readonly<...>.\nb2Readonly has been configured to work with all @box2d/cores math types. Other types won't work by default, as TypeScript doesn't give enough information to do this automatically.\nExtending b2Readonly\nHowever, if you feel like you want to use b2Readonly for your types as well, there is a way to do that.\nLet's say you have this custom rectangle type:\nclass Rectangle {\n public position: b2Vec2;\n\n public width: number;\n\n public height: number;\n\n public constructor(x: number, y: number, width: number, height: number) {\n this.position = new b2Vec2(x, y);\n this.width = width;\n this.height = height;\n }\n\n public setSize(width: number, height: number) {\n this.width = width;\n this.height = height;\n }\n\n public getArea() {\n return this.width * this.height;\n }\n}\n\nIn order for b2Readonly<Rectangle> to return the desired type, you'll need to do some declaration merging:\ndeclare module "@box2d/core" {\n export interface b2ReadonlyTypes {\n // <name>: [type, picked properties, manual properties]\n Rectangle: [Rectangle, "width" | "height" | "getArea", { position: b2Readonly<b2Vec2> }];\n }\n}\n\nThe parts explained:\n\nname is just there to uniquely identify your type. You might want to avoid conflicts if working with other libraries.\ntype is obviously the type you want to make compatible with b2Readonly\npicked properties are the properties (and methods) you want to add to the readonly type based on your type.\nmanual properties is a way to specify objects within your type, which also might need to be b2Readonly.\n\nThis might be a bit cumbersome compared to just adding the name to the picked properties, but the reward is better intellisense.\nIf you don't have any of those kinds of properties, you can just pass unknown.\n\n\n\nExample for a rectangle which has x/y properties instead of position:\ndeclare module "@box2d/core" {\n export interface b2ReadonlyTypes {\n Rectangle: [Rectangle, "x" | "y" | "width" | "height" | "getArea", unknown];\n }\n}\n\n","title":"Common Module","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/dynamics.html","content":"Dynamics Module\nThe Dynamics module is the most complex part of Box2D and is the part\nyou likely interact with the most. The Dynamics module sits on top of\nthe Common and Collision modules, so you should be somewhat familiar\nwith those by now.\nThe Dynamics module contains:\n\nfixture class\nrigid body class\ncontact class\njoint classes\nworld class\nlistener classes\n\nThere are many dependencies between these classes so it is difficult to\ndescribe one class without referring to another. In the following, you\nmay see some references to classes that have not been described yet.\nTherefore, you may want to quickly skim this chapter before reading it\nclosely.\nThe dynamics module is covered in the following chapters.\nBodies\nBodies have position and velocity. You can apply forces, torques, and\nimpulses to bodies. Bodies can be static, kinematic, or dynamic. Here\nare the body type definitions:\nb2_staticBody\nA static body does not move under simulation and behaves as if it has\ninfinite mass. Internally, Box2D stores zero for the mass and the\ninverse mass. Static bodies can be moved manually by the user. A static\nbody has zero velocity. Static bodies do not collide with other static\nor kinematic bodies.\nb2_kinematicBody\nA kinematic body moves under simulation according to its velocity.\nKinematic bodies do not respond to forces. They can be moved manually by\nthe user, but normally a kinematic body is moved by setting its\nvelocity. A kinematic body behaves as if it has infinite mass, however,\nBox2D stores zero for the mass and the inverse mass. Kinematic bodies do\nnot collide with other kinematic or static bodies.\nb2_dynamicBody\nA dynamic body is fully simulated. They can be moved manually by the\nuser, but normally they move according to forces. A dynamic body can\ncollide with all body types. A dynamic body always has finite, non-zero\nmass. If you try to set the mass of a dynamic body to zero, it will\nautomatically acquire a mass of one kilogram and it won't rotate.\nBodies are the backbone for fixtures (shapes). Bodies carry fixtures and\nmove them around in the world. Bodies are always rigid bodies in Box2D.\nThat means that two fixtures attached to the same rigid body never move\nrelative to each other and fixtures attached to the same body don't\ncollide.\nFixtures have collision geometry and density. Normally, bodies acquire\ntheir mass properties from the fixtures. However, you can override the\nmass properties after a body is constructed.\nYou usually keep references to all the bodies you create. This way you can\nquery the body positions to update the positions of your graphical\nentities. You should also keep body references so you can destroy them\nwhen you are done with them.\nBody Definition\nBefore a body is created you must create a body definition (b2BodyDef).\nThe body definition holds the data needed to create and initialize a\nbody.\nBox2D copies the data out of the body definition; it does not keep a\nreference to the body definition. This means you can recycle a body\ndefinition to create multiple bodies.\nLet's go over some of the key members of the body definition.\nBody Type\nAs discussed at the beginning of this chapter, there are three different\nbody types: static, kinematic, and dynamic. You should establish the\nbody type at creation because changing the body type later is expensive.\nconst bodyDef: b2BodyDef = {\n type: b2BodyType.b2_dynamicBody,\n};\n\nSetting the body type is mandatory.\nPosition and Angle\nThe body definition gives you the chance to initialize the position of\nthe body on creation. This has far better performance than creating the\nbody at the world origin and then moving the body.\n\nCaution:\nDo not create a body at the origin and then move it. If you create\nseveral bodies at the origin, then performance will suffer.\n\nA body has two main points of interest. The first point is the body's\norigin. Fixtures and joints are attached relative to the body's origin.\nThe second point of interest is the center of mass. The center of mass\nis determined from mass distribution of the attached shapes or is\nexplicitly set with b2MassData. Much of Box2D's internal computations\nuse the center of mass position. For example b2Body stores the linear\nvelocity for the center of mass.\nWhen you are building the body definition, you may not know where the\ncenter of mass is located. Therefore you specify the position of the\nbody's origin. You may also specify the body's angle in radians, which\nis not affected by the position of the center of mass. If you later\nchange the mass properties of the body, then the center of mass may move\non the body, but the origin position does not change and the attached\nshapes and joints do not move.\nconst bodyDef: b2BodyDef = {\n // ...\n position: { x: 0, y: 2 }, // the body's origin position.\n angle: 0.25 * Math.PI, // the body's angle in radians.\n};\n\nA rigid body is also a frame of reference. You can define fixtures and\njoints in that frame. Those fixtures and joint anchors never move in the\nlocal frame of the body.\nDamping\nDamping is used to reduce the world velocity of bodies. Damping is\ndifferent than friction because friction only occurs with contact.\nDamping is not a replacement for friction and the two effects should be\nused together.\nDamping parameters should be between 0 and infinity, with 0 meaning no\ndamping, and infinity meaning full damping. Normally you will use a\ndamping value between 0 and 0.1. I generally do not use linear damping\nbecause it makes bodies look like they are floating.\nconst bodyDef: b2BodyDef = {\n // ...\n linearDamping: 0,\n angularDamping: 0.01,\n};\n\nDamping is approximated for stability and performance. At small damping\nvalues the damping effect is mostly independent of the time step. At\nlarger damping values, the damping effect will vary with the time step.\nThis is not an issue if you use a fixed time step (recommended).\nGravity Scale\nYou can use the gravity scale to adjust the gravity on a single body. Be\ncareful though, increased gravity can decrease stability.\n// Set the gravity scale to zero so this body will float\nconst bodyDef: b2BodyDef = {\n // ...\n gravityScale: 0,\n};\n\nSleep Parameters\nWhat does sleep mean? Well it is expensive to simulate bodies, so the\nless we have to simulate the better. When a body comes to rest we would\nlike to stop simulating it.\nWhen Box2D determines that a body (or group of bodies) has come to rest,\nthe body enters a sleep state which has very little CPU overhead. If a\nbody is awake and collides with a sleeping body, then the sleeping body\nwakes up. Bodies will also wake up if a joint or contact attached to\nthem is destroyed. You can also wake a body manually.\nThe body definition lets you specify whether a body can sleep and\nwhether a body is created sleeping.\nconst bodyDef: b2BodyDef = {\n // ...\n allowSleep: true,\n awake: true,\n};\n\nFixed Rotation\nYou may want a rigid body, such as a character, to have a fixed\nrotation. Such a body should not rotate, even under load. You can use\nthe fixed rotation setting to achieve this:\nconst bodyDef: b2BodyDef = {\n // ...\n fixedRotation: true,\n};\n\nThe fixed rotation flag causes the rotational inertia and its inverse to\nbe set to zero.\nBullets\nGame simulation usually generates a sequence of images that are played\nat some frame rate. This is called discrete simulation. In discrete\nsimulation, rigid bodies can move by a large amount in one time step. If\na physics engine doesn't account for the large motion, you may see some\nobjects incorrectly pass through each other. This effect is called\ntunneling.\nBy default, Box2D uses continuous collision detection (CCD) to prevent\ndynamic bodies from tunneling through static bodies. This is done by\nsweeping shapes from their old position to their new positions. The\nengine looks for new collisions during the sweep and computes the time\nof impact (TOI) for these collisions. Bodies are moved to their first\nTOI and then the solver performs a sub-step to complete the full time\nstep. There may be additional TOI events within a sub-step.\nNormally CCD is not used between dynamic bodies. This is done to keep\nperformance reasonable. In some game scenarios you need dynamic bodies\nto use CCD. For example, you may want to shoot a high speed bullet at a\nstack of dynamic bricks. Without CCD, the bullet might tunnel through\nthe bricks.\nFast moving objects in Box2D can be labeled as bullets. Bullets will\nperform CCD with both static and dynamic bodies. You should decide what\nbodies should be bullets based on your game design. If you decide a body\nshould be treated as a bullet, use the following setting.\nconst bodyDef: b2BodyDef = {\n // ...\n bullet: true,\n};\n\nThe bullet flag only affects dynamic bodies.\nActivation\nYou may wish a body to be created but not participate in collision or\ndynamics. This state is similar to sleeping except the body will not be\nwoken by other bodies and the body's fixtures will not be placed in the\nbroad-phase. This means the body will not participate in collisions, ray\ncasts, etc.\nYou can create a body in an inactive state and later re-activate it.\nconst bodyDef: b2BodyDef = {\n // ...\n active: true,\n};\n\nJoints may be connected to inactive bodies. These joints will not be\nsimulated. You should be careful when you activate a body that its\njoints are not distorted.\nNote that activating a body is almost as expensive as creating the body\nfrom scratch. So you should not use activation for streaming worlds. Use\ncreation/destruction for streaming worlds to save memory.\nUser Data\nUser data is a Record of references. This gives you a hook to link your\napplication objects to bodies, fixtures and joints.\nCheck loose ends for more details.\nconst bodyDef: b2BodyDef = {\n // ...\n userData: {\n actor: myActor,\n },\n};\n\nBody Factory\nBodies are created and destroyed using a body factory provided by the\nworld class. This lets the world create the body and add the body to the world data structure.\nconst myWorld: b2World = ...;\nconst dynamicBody: b2Body = myWorld.createBody(bodyDef);\n\n// ... do stuff ...\n\nmyWorld.DestroyBody(dynamicBody);\n// stop using dynamicBody from this point on!\n\n\nCaution:\nYou should never use new to create a body. The world won't\nknow about the body and the body won't be properly initialized.\n\nBox2D does not keep a reference to the body definition or any of the\ndata it holds (except references in user data records).\nSo you can create temporary body definitions and reuse the same body definitions.\nBox2D allows you to avoid destroying bodies by deleting your b2World\nobject, which does all the cleanup work for you. However, you should be\nmindful to remove body references that you keep in your game engine.\nWhen you destroy a body, the attached fixtures and joints are\nautomatically destroyed. This has important implications for how you\nmanage shape and joint references.\nUsing a Body\nAfter creating a body, there are many operations you can perform on the\nbody. These include setting mass properties, accessing position and\nvelocity, applying forces, and transforming points and vectors.\nMass Data\nA body has mass (scalar), center of mass (2-vector), and rotational\ninertia (scalar). For static bodies, the mass and rotational inertia are\nset to zero. When a body has fixed rotation, its rotational inertia is\nzero.\nNormally the mass properties of a body are established automatically\nwhen fixtures are added to the body. You can also adjust the mass of a\nbody at run-time. This is usually done when you have special game\nscenarios that require altering the mass.\n// on b2Body:\npublic SetMassData(massData: b2MassData): void;\n\nAfter setting a body's mass directly, you may wish to revert to the\nnatural mass dictated by the fixtures. You can do this with:\n// on b2Body:\npublic ResetMassData(): void;\n\nThe body's mass data is available through the following functions:\n// on b2Body:\npublic GetMass(): number;\npublic GetInertia(): number;\npublic GetLocalCenter(): b2Readonly<b2Vec2>;\npublic GetMassData(data: b2MassData): b2MassData;\n\nState Information\nThere are many aspects to the body's state. You can access this state\ndata efficiently through the following functions:\n// on b2Body:\npublic SetType(type: b2BodyType): void;\npublic GetType(): b2BodyType;\npublic SetBullet(flag: boolean): void;\npublic IsBullet(): boolean;\npublic SetSleepingAllowed(flag: boolean): void;\npublic IsSleepingAllowed(): boolean;\npublic SetAwake(flag: boolean): void;\npublic IsAwake(): boolean;\npublic SetEnabled(flag: boolean): void;\npublic IsEnabled(): boolean;\npublic SetFixedRotation(flag: boolean): void;\npublic IsFixedRotation(): boolean;\n\nPosition and Velocity\nYou can access the position and rotation of a body. This is common when\nrendering your associated game actor. You can also set the position,\nalthough this is less common since you will normally use Box2D to\nsimulate movement.\n// on b2Body:\npublic SetTransformVec(position: XY, angle: number): void;\npublic SetTransformXY(x: number, y: number, angle: number): void\npublic GetTransform(): b2Readonly<b2Transform>;\npublic GetPosition(): b2Readonly<b2Vec2>;\npublic GetAngle(): number;\n\nYou can access the center of mass position in local and world\ncoordinates. Much of the internal simulation in Box2D uses the center of\nmass. However, you should normally not need to access it. Instead you\nwill usually work with the body transform. For example, you may have a\nbody that is square. The body origin might be a corner of the square,\nwhile the center of mass is located at the center of the square.\npublic GetWorldCenter(): b2Readonly<b2Vec2>;\npublic GetLocalCenter(): b2Readonly<b2Vec2>;\n\nYou can access the linear and angular velocity. The linear velocity is\nfor the center of mass. Therefore, the linear velocity may change if the\nmass properties change.\nForces and Impulses\nYou can apply forces, torques, and impulses to a body. When you apply a\nforce or an impulse, you provide a world point where the load is\napplied. This often results in a torque about the center of mass.\npublic ApplyForce(force: XY, point: XY, wake = true): void;\npublic ApplyTorque(torque: number, wake = true): void;\npublic ApplyLinearImpulse(impulse: XY, point: XY, wake = true): void;\npublic ApplyAngularImpulse(impulse: number, wake = true): void ;\n\nApplying a force, torque, or impulse wakes the body. Sometimes this is\nundesirable. For example, you may be applying a steady force and want to\nallow the body to sleep to improve performance. In this case you can use\nthe following code.\nif (myBody.IsAwake() === true) {\n myBody.ApplyForce(myForce, myPoint);\n}\n\nCoordinate Transformations\nThe body class has some utility functions to help you transform points\nand vectors between local and world space. If you don't understand\nthese concepts, please read "Essential Mathematics for Games and\nInteractive Applications" by Jim Van Verth and Lars Bishop. These\nfunctions are efficient (when inlined).\npublic GetWorldPoint<T extends XY>(localPoint: Readonly<XY>, out: T): T;\npublic GetWorldVector<T extends XY>(localVector: Readonly<XY>, out: T): T;\npublic GetLocalPoint<T extends XY>(worldPoint: Readonly<XY>, out: T): T;\npublic GetLocalVector<T extends XY>(worldVector: Readonly<XY>, out: T): T;\n\nAcessing Fixtures, Joints, and Contacts\nYou can iterate over a body's fixtures. This is mainly useful if you\nneed to access the fixture's user data.\nfor (let f: b2Fixture | null = b.GetFixtureList(); f; f = f.GetNext()) {\n const data: b2FixtureUserData = f.GetUserData();\n // do something with data ...\n}\n\nYou can similarly iterate over the body's joint list.\nThe body also provides a list of associated contacts. You can use this\nto get information about the current contacts. Be careful, because the\ncontact list may not contain all the contacts that existed during the\nprevious time step.\nFixtures\nRecall that shapes don't know about bodies and may be used independently\nof the physics simulation. Therefore Box2D provides the b2Fixture class\nto attach shapes to bodies. A body may have zero or more fixtures. A\nbody with multiple fixtures is sometimes called a compound body.\nFixtures hold the following:\n\na single shape\nbroad-phase proxies\ndensity, friction, and restitution\ncollision filtering flags\nback reference to the parent body\nuser data\nsensor flag\n\nThese are described in the following sections.\nFixture Creation\nFixtures are created by initializing a fixture definition and then\npassing the definition to the parent body.\nconst myBody: b2Body = ...;\nconst fixtureDef: b2FixtureDef = {\n shape: myShape;\n density: 1,\n};\nconst myFixture: b2Fixture = myBody.CreateFixture(fixtureDef);\n\nThis creates the fixture and attaches it to the body. You do not need to\nstore the fixture reference since the fixture will automatically be\ndestroyed when the parent body is destroyed. You can create multiple\nfixtures on a single body.\nYou can destroy a fixture on the parent body. You may do this to model a\nbreakable object. Otherwise you can just leave the fixture alone and let\nthe body destruction take care of destroying the attached fixtures.\nmyBody.DestroyFixture(myFixture);\n\nDensity\nThe fixture density is used to compute the mass properties of the parent\nbody. The density can be zero or positive. You should generally use\nsimilar densities for all your fixtures. This will improve stacking\nstability.\nThe mass of a body is not adjusted when you set the density. You must\ncall ResetMassData for this to occur.\nconst fixture: b2Fixture = ...;\nfixture.SetDensity(5);\nconst body: b2Body = ...;\nbody.ResetMassData();\n\nFriction\nFriction is used to make objects slide along each other realistically.\nBox2D supports static and dynamic friction, but uses the same parameter\nfor both. Friction is simulated accurately in Box2D and the friction\nstrength is proportional to the normal force (this is called Coulomb\nfriction). The friction parameter is usually set between 0 and 1, but\ncan be any non-negative value. A friction value of 0 turns off friction\nand a value of 1 makes the friction strong. When the friction force is\ncomputed between two shapes, Box2D must combine the friction parameters\nof the two parent fixtures. This is done with the geometric mean:\nconst fixtureA: b2Fixture = ...;\nconst fixtureB: b2Fixture = ...;\n\nconst friction: number = Math.sqrt(fixtureA.friction * fixtureB.friction);\n\nSo if one fixture has zero friction then the contact will have zero\nfriction.\nYou can override the default mixed friction using\nb2Contact::SetFriction. This is usually done in the b2ContactListener\ncallback.\nRestitution\nRestitution is used to make objects bounce. The restitution value is\nusually set to be between 0 and 1. Consider dropping a ball on a table.\nA value of zero means the ball won't bounce. This is called an\ninelastic collision. A value of one means the ball's velocity will be\nexactly reflected. This is called a perfectly elastic collision.\nRestitution is combined using the following formula.\nconst fixtureA: b2Fixture = ...;\nconst fixtureB: b2Fixture = ...;\nconst restitution: number = b2Max(fixtureA.restitution, fixtureB.restitution);\n\nRestitution is combined this way so that you can have a bouncy super\nball without having a bouncy floor.\nYou can override the default mixed restitution using\nb2Contact::SetRestitution. This is usually done in the b2ContactListener\ncallback.\nWhen a shape develops multiple contacts, restitution is simulated\napproximately. This is because Box2D uses an iterative solver. Box2D\nalso uses inelastic collisions when the collision velocity is small.\nThis is done to prevent jitter. See b2ContactVelocityConstraint::threshold.\nFiltering\nCollision filtering allows you to prevent collision between fixtures.\nFor example, say you make a character that rides a bicycle. You want the\nbicycle to collide with the terrain and the character to collide with\nthe terrain, but you don't want the character to collide with the\nbicycle (because they must overlap). Box2D supports such collision\nfiltering using categories and groups.\nBox2D supports 16 collision categories. For each fixture you can specify\nwhich category it belongs to. You also specify what other categories\nthis fixture can collide with. For example, you could specify in a\nmultiplayer game that all players don't collide with each other and\nmonsters don't collide with each other, but players and monsters should\ncollide. This is done with masking bits. For example:\nconst playerFixtureDef: b2FixtureDef = {\n categoryBits: 0x0002,\n maskBits: 0x0004,\n};\nconst monsterFixtureDef: b2FixtureDef = {\n categoryBits: 0x0004,\n maskBits: 0x0002,\n};\n\nHere is the rule for a collision to occur:\nconst catA: number = fixtureA.filter.categoryBits;\nconst maskA: number = fixtureA.filter.maskBits;\nconst catB: number = fixtureB.filter.categoryBits;\nconst maskB: number = fixtureB.filter.maskBits;\n\nif ((catA & maskB) !== 0 && (catB & maskA) !== 0) {\n // fixtures can collide\n}\n\nCollision groups let you specify an integral group index. You can have\nall fixtures with the same group index always collide (positive index)\nor never collide (negative index). Group indices are usually used for\nthings that are somehow related, like the parts of a bicycle. In the\nfollowing example, fixture1 and fixture2 always collide, but fixture3\nand fixture4 never collide.\nfixture1Def.filter.groupIndex = 2;\nfixture2Def.filter.groupIndex = 2;\nfixture3Def.filter.groupIndex = -8;\nfixture4Def.filter.groupIndex = -8;\n\nCollisions between fixtures of different group indices are filtered\naccording the category and mask bits. In other words, group filtering\nhas higher precedence than category filtering.\nNote that additional collision filtering occurs in Box2D. Here is a\nlist:\n\nA fixture on a static body can only collide with a dynamic body.\nA fixture on a kinematic body can only collide with a dynamic body.\nFixtures on the same body never collide with each other.\nYou can optionally enable/disable collision between fixtures on bodies connected by a joint.\n\nSometimes you might need to change collision filtering after a fixture\nhas already been created. You can get and set the b2Filter class on\nan existing fixture using b2Fixture::GetFilterData and\nb2Fixture::SetFilterData. Note that changing the filter data will not\nadd or remove contacts until the next time step (see the World class).\nSensors\nSometimes game logic needs to know when two fixtures overlap yet there\nshould be no collision response. This is done by using sensors. A sensor\nis a fixture that detects collision but does not produce a response.\nYou can flag any fixture as being a sensor. Sensors may be static,\nkinematic, or dynamic. Remember that you may have multiple fixtures per\nbody and you can have any mix of sensors and solid fixtures. Also,\nsensors only form contacts when at least one body is dynamic, so you\nwill not get a contact for kinematic versus kinematic, kinematic versus\nstatic, or static versus static.\nSensors do not generate contact points. There are two ways to get the\nstate of a sensor:\n\nb2Contact::IsTouching\nb2ContactListener::BeginContact and b2ContactListener::EndContact\n\nJoints\nJoints are used to constrain bodies to the world or to each other.\nTypical examples in games include ragdolls, teeters, and pulleys. Joints\ncan be combined in many different ways to create interesting motions.\nSome joints provide limits so you can control the range of motion. Some\njoint provide motors which can be used to drive the joint at a\nprescribed speed until a prescribed force/torque is exceeded.\nJoint motors can be used in many ways. You can use motors to control\nposition by specifying a joint velocity that is proportional to the\ndifference between the actual and desired position. You can also use\nmotors to simulate joint friction: set the joint velocity to zero and\nprovide a small, but significant maximum motor force/torque. Then the\nmotor will attempt to keep the joint from moving until the load becomes\ntoo strong.\nJoint Definition\nEach joint type has a definition that derives from b2JointDef. All\njoints are connected between two different bodies. One body may be static.\nJoints between static and/or kinematic bodies are allowed, but have no\neffect and use some processing time.\nYou can specify user data for any joint type and you can provide a flag\nto prevent the attached bodies from colliding with each other. This is\nactually the default behavior and you must set the collideConnected\nboolean to allow collision between to connected bodies.\nMany joint definitions require that you provide some geometric data.\nOften a joint will be defined by anchor points. These are points fixed\nin the attached bodies. Box2D requires these points to be specified in\nlocal coordinates. This way the joint can be specified even when the\ncurrent body transforms violate the joint constraint --- a common\noccurrence when a game is saved and reloaded. Additionally, some joint\ndefinitions need to know the default relative angle between the bodies.\nThis is necessary to constrain rotation correctly.\nInitializing the geometric data can be tedious, so many joints have\ninitialization functions that use the current body transforms to remove\nmuch of the work. However, these initialization functions should usually\nonly be used for prototyping. Production code should define the geometry\ndirectly. This will make joint behavior more robust.\nThe rest of the joint definition data depends on the joint type. We\ncover these now.\nJoint Factory\nJoints are created and destroyed using the world factory methods. This\nbrings up an old issue:\n\nCaution:\nDon't try to create a joint using new.\nYou must create and destroy bodies and joints using the create\nand destroy methods of the b2World class.\n\nHere's an example of the lifetime of a revolute joint:\nconst myWorld: b2World = ...;\nconst jointDef = new b2RevoluteJointDef();\njointDef.initialize(myBodyA, myBodyB, myBodyA.GetCenterPosition());\n\nconst joint: b2RevoluteJoint = myWorld.CreateJoint(jointDef);\n\n// ... do stuff ...\n\nmyWorld.DestroyJoint(joint);\n// stop using the joint from this point on!\n\nIt is always good to remove your reference after they are destroyed. This\nwill make the program behave unexpectedly if you try to reuse\nthe reference.\nThe lifetime of a joint is not simple. Heed this warning well:\n\nCaution:\nJoints are destroyed when an attached body is destroyed.\n\nThis precaution is not always necessary. You may organize your game\nengine so that joints are always destroyed before the attached bodies.\nIn this case you don't need to implement the listener class. See the\nsection on Implicit Destruction for details.\nUsing Joints\nMany simulations create the joints and don't access them again until\nthey are destroyed. However, there is a lot of useful data contained in\njoints that you can use to create a rich simulation.\nFirst of all, you can get the bodies, anchor points, and user data from\na joint.\n// on b2Joint:\npublic GetBodyA(): b2Body;\npublic GetBodyB(): bn2Body;\npublic GetAnchorA<T extends XY>(out: T): T;\npublic GetAnchorB<T extends XY>(out: T): T;\npublic GetUserData(): b2JointUserData;\n\nAll joints have a reaction force and torque. This the reaction force\napplied to body 2 at the anchor point. You can use reaction forces to\nbreak joints or trigger other game events. These functions may do some\ncomputations, so don't call them if you don't need the result.\npublic GetReactionForce<T extends XY>(inv_dt: number, out: T): T;\npublic GetReactionTorque(inv_dt: number): number;\n\nDistance Joint\nOne of the simplest joint is a distance joint which says that the\ndistance between two points on two bodies must be constant. When you\nspecify a distance joint the two bodies should already be in place. Then\nyou specify the two anchor points in world coordinates. The first anchor\npoint is connected to body 1, and the second anchor point is connected\nto body 2. These points imply the length of the distance constraint.\n\nHere is an example of a distance joint definition. In this case we\ndecide to allow the bodies to collide.\nconst jointDef = new b2DistanceJointDef();\njointDef.Initialize(myBodyA, myBodyB, worldAnchorOnBodyA, worldAnchorOnBodyB);\njointDef.collideConnected = true;\n\nThe distance joint can also be made soft, like a spring-damper\nconnection. See the Web example in the testbed to see how this behaves.\nSoftness is achieved by tuning two constants in the definition:\nstiffness and damping. It can be non-intuitive setting these values directly\nsince they have units in terms on Newtons. Box2D provides an API to compute\nthese values in terms of frequency and damping ratio.\nfunction b2LinearStiffness(\n def: { stiffness: number; damping: number },\n frequencyHertz: number,\n dampingRatio: number,\n bodyA: b2Body,\n bodyB: b2Body,\n): void;\n\nThink of the frequency as the frequency of a harmonic oscillator (like a\nguitar string). The frequency is specified in Hertz. Typically the frequency\nshould be less than a half the frequency of the time step. So if you are using\na 60Hz time step, the frequency of the distance joint should be less than 30Hz.\nThe reason is related to the Nyquist frequency.\nThe damping ratio is non-dimensional and is typically between 0 and 1,\nbut can be larger. At 1, the damping is critical (all oscillations\nshould vanish).\nconst frequencyHz = 4;\nconst dampingRatio = 0.5;\nb2LinearStiffness(jointDef, frequencyHz, dampingRatio, jointDef.bodyA, jointDef.bodyB);\n\nIt is also possible to define a minimum and maximum length for the distance joint.\nSee b2DistanceJointDef for details.\nRevolute Joint\nA revolute joint forces two bodies to share a common anchor point, often\ncalled a hinge point. The revolute joint has a single degree of freedom:\nthe relative rotation of the two bodies. This is called the joint angle.\n\nTo specify a revolute you need to provide two bodies and a single anchor\npoint in world space. The initialization function assumes that the\nbodies are already in the correct position.\nIn this example, two bodies are connected by a revolute joint at the\nfirst body's center of mass.\nconst jointDef = new b2RevoluteJointDef();\njointDef.Initialize(myBodyA, myBodyB, myBodyA.GetWorldCenter());\n\nThe revolute joint angle is positive when bodyB rotates CCW about the\nangle point. Like all angles in Box2D, the revolute angle is measured in\nradians. By convention the revolute joint angle is zero when the joint\nis created using Initialize(), regardless of the current rotation of the\ntwo bodies.\nIn some cases you might wish to control the joint angle. For this, the\nrevolute joint can optionally simulate a joint limit and/or a motor.\nA joint limit forces the joint angle to remain between a lower and upper\nbound. The limit will apply as much torque as needed to make this\nhappen. The limit range should include zero, otherwise the joint will\nlurch when the simulation begins.\nA joint motor allows you to specify the joint speed (the time derivative\nof the angle). The speed can be negative or positive. A motor can have\ninfinite force, but this is usually not desirable. Recall the eternal\nquestion:\n\nWhat happens when an irresistible force meets an immovable object?\n\nI can tell you it's not pretty. So you can provide a maximum torque for\nthe joint motor. The joint motor will maintain the specified speed\nunless the required torque exceeds the specified maximum. When the\nmaximum torque is exceeded, the joint will slow down and can even\nreverse.\nYou can use a joint motor to simulate joint friction. Just set the joint\nspeed to zero, and set the maximum torque to some small, but significant\nvalue. The motor will try to prevent the joint from rotating, but will\nyield to a significant load.\nHere's a revision of the revolute joint definition above; this time the\njoint has a limit and a motor enabled. The motor is setup to simulate\njoint friction.\nconst jointDef = new b2RevoluteJointDef();\njointDef.Initialize(bodyA, bodyB, myBodyA.GetWorldCenter());\njointDef.lowerAngle = -0.5 * Math.PI; // -90 degrees\njointDef.upperAngle = 0.25 * Math.PI; // 45 degrees\njointDef.enableLimit = true;\njointDef.maxMotorTorque = 10.0;\njointDef.motorSpeed = 0.0;\njointDef.enableMotor = true;\n\nYou can access a revolute joint's angle, speed, and motor torque.\n// on b2RevoluteJoint:\npublic GetJointAngle(): number;\npublic GetJointSpeed(): number;\npublic GetMotorTorque(inv_dt: number): number;\n\nYou also update the motor parameters each step.\n// on b2RevoluteJoint:\npublic SetMotorSpeed(speed: number): number;\npublic SetMaxMotorTorque(torque: number): void;\n\nJoint motors have some interesting abilities. You can update the joint\nspeed every time step so you can make the joint move back-and-forth like\na sine-wave or according to whatever function you want.\n// ... Game Loop Begin ...\n\nmyJoint.SetMotorSpeed(Math.cos(0.5 * time));\n\n// ... Game Loop End ...\n\nYou can also use joint motors to track a desired joint angle. For example:\n// ... Game Loop Begin ...\n\nconst angleError = myJoint.GetJointAngle() - angleTarget;\nconst gain = 0.1;\nmyJoint.SetMotorSpeed(-gain * angleError);\n\n// ... Game Loop End ...\n\nGenerally your gain parameter should not be too large. Otherwise your\njoint may become unstable.\nPrismatic Joint\nA prismatic joint allows for relative translation of two bodies along a\nspecified axis. A prismatic joint prevents relative rotation. Therefore,\na prismatic joint has a single degree of freedom.\n\nThe prismatic joint definition is similar to the revolute joint\ndescription; just substitute translation for angle and force for torque.\nUsing this analogy provides an example prismatic joint definition with a\njoint limit and a friction motor:\nconst jointDef = new b2PrismaticJointDef();\nconst worldAxis: XY = { x: 1, y: 0 };\njointDef.Initialize(myBodyA, myBodyB, myBodyA.GetWorldCenter(), worldAxis);\njointDef.lowerTranslation = -5;\njointDef.upperTranslation = 2.5;\njointDef.enableLimit = true;\njointDef.maxMotorForce = 1;\njointDef.motorSpeed = 0;\njointDef.enableMotor = true;\n\nThe revolute joint has an implicit axis coming out of the screen. The\nprismatic joint needs an explicit axis parallel to the screen. This axis\nis fixed in the two bodies and follows their motion.\nLike the revolute joint, the prismatic joint translation is zero when\nthe joint is created using Initialize(). So be sure zero is between your\nlower and upper translation limits.\nUsing a prismatic joint is similar to using a revolute joint. Here are\nthe relevant member functions:\n// on b2PrismaticJoint\npublic GetJointTranslation(): number;\npublic GetJointSpeed(): number;\npublic GetMotorForce(inv_dt: number): number;\npublic SetMotorSpeed(speed: number): number;\npublic SetMaxMotorForce(force: number): void;\n\nPulley Joint\nA pulley is used to create an idealized pulley. The pulley connects two\nbodies to ground and to each other. As one body goes up, the other goes\ndown. The total length of the pulley rope is conserved according to the\ninitial configuration.\nlength1 + length2 === constant;\n\nYou can supply a ratio that simulates a block and tackle. This causes\none side of the pulley to extend faster than the other. At the same time\nthe constraint force is smaller on one side than the other. You can use\nthis to create mechanical leverage.\nlength1 + ratio * length2 === constant;\n\nFor example, if the ratio is 2, then length1 will vary at twice the rate\nof length2. Also the force in the rope attached to body1 will have half\nthe constraint force as the rope attached to body2.\n\nPulleys can be troublesome when one side is fully extended. The rope on\nthe other side will have zero length. At this point the constraint\nequations become singular (bad). You should configure collision shapes\nto prevent this.\nHere is an example pulley definition:\nconst anchor1 = myBody1.GetWorldCenter();\nconst anchor2 = myBody2.GetWorldCenter();\n\nconst groundAnchor1: XY = { x: p1.x, y: p1.y + 10 };\nconst groundAnchor2: XY = { x: p2.x, y: p2.y + 12 };\n\nconst ratio = 1;\n\nconst jointDef = new b2PulleyJointDef();\njointDef.Initialize(myBody1, myBody2, groundAnchor1, groundAnchor2, anchor1, anchor2, ratio);\n\nPulley joints provide the current lengths.\n// on b2PulleyJoint:\npublic GetLengthA(): number;\npublic GetLengthB(): number;\n\nGear Joint\nIf you want to create a sophisticated mechanical contraption you might\nwant to use gears. In principle you can create gears in Box2D by using\ncompound shapes to model gear teeth. This is not very efficient and\nmight be tedious to author. You also have to be careful to line up the\ngears so the teeth mesh smoothly. Box2D has a simpler method of creating\ngears: the gear joint.\n\nThe gear joint can only connect revolute and/or prismatic joints.\nLike the pulley ratio, you can specify a gear ratio. However, in this\ncase the gear ratio can be negative. Also keep in mind that when one\njoint is a revolute joint (angular) and the other joint is prismatic\n(translation), and then the gear ratio will have units of length or one\nover length.\ncoordinate1 + ratio * coordinate2 === constant;\n\nHere is an example gear joint. The bodies myBodyA and myBodyB are any\nbodies from the two joints, as long as they are not the same bodies.\nconst jointDef = new b2GearJointDef();\njointDef.bodyA = myBodyA;\njointDef.bodyB = myBodyB;\njointDef.joint1 = myRevoluteJoint;\njointDef.joint2 = myPrismaticJoint;\njointDef.ratio = (2 * Math.PI) / myLength;\n\nNote that the gear joint depends on two other joints. This creates a\nfragile situation. What happens if those joints are destroyed?\n\nCaution:\nAlways destroy gear joints before the revolute/prismatic joints on the\ngears. Otherwise you will see unexpected behavior due to the orphaned\njoint references in the gear joint. You should also destroy the gear joint\nbefore you destroy any of the bodies involved.\n\nMouse Joint\nThe mouse joint is used in the testbed to manipulate bodies with the\nmouse. It attempts to drive a point on a body towards the current\nposition of the cursor. There is no restriction on rotation.\nThe mouse joint definition has a target point, maximum force, frequency,\nand damping ratio. The target point initially coincides with the body's\nanchor point. The maximum force is used to prevent violent reactions\nwhen multiple dynamic bodies interact. You can make this as large as you\nlike. The frequency and damping ratio are used to create a spring/damper\neffect similar to the distance joint.\nMany users have tried to adapt the mouse joint for game play. Users\noften want to achieve precise positioning and instantaneous response.\nThe mouse joint doesn't work very well in that context. You may wish to\nconsider using kinematic bodies instead.\nWheel Joint\nThe wheel joint restricts a point on bodyB to a line on bodyA. The wheel\njoint also provides a suspension spring. See b2_wheel_joint.ts and car.ts\nfor details.\n\nWeld Joint\nThe weld joint attempts to constrain all relative motion between two\nbodies. See the cantilever.ts in the testbed to see how the weld joint\nbehaves.\nIt is tempting to use the weld joint to define breakable structures.\nHowever, the Box2D solver is iterative so the joints are a bit soft. So\nchains of bodies connected by weld joints will flex.\nInstead it is better to create breakable bodies starting with a single\nbody with multiple fixtures. When the body breaks, you can destroy a\nfixture and recreate it on a new body. See the Breakable example in the\ntestbed.\nFriction Joint\nThe friction joint is used for top-down friction. The joint provides 2D\ntranslational friction and angular friction. See b2_friction_joint.ts and\napply_force.ts for details.\nMotor Joint\nA motor joint lets you control the motion of a body by specifying target\nposition and rotation offsets. You can set the maximum motor force and\ntorque that will be applied to reach the target position and rotation.\nIf the body is blocked, it will stop and the contact forces will be\nproportional the maximum motor force and torque. See b2MotorJoint and\nmotor_joint.ts for details.\nWheel Joint\nThe wheel joint is designed specifically for vehicles. It provides a translation\nand rotation. The translation has a spring and damper to simulate the vehicle\nsuspension. The rotation allows the wheel to rotate. You can specify an rotational\nmotor to drive the wheel and to apply braking. See b2WheelJoint, wheel_joint.ts,\nand car.ts for details.\nContacts\nContacts are objects created by Box2D to manage collision between two\nfixtures. If the fixture has children, such as a chain shape, then a\ncontact exists for each relevant child. There are different kinds of\ncontacts, derived from b2Contact, for managing contact between different\nkinds of fixtures. For example there is a contact class for managing\npolygon-polygon collision and another contact class for managing\ncircle-circle collision.\nHere is some terminology associated with contacts.\nContact Point\nA contact point is a point where two shapes touch. Box2D approximates\ncontact with a small number of points.\nContact Normal\nA contact normal is a unit vector that points from one shape to another.\nBy convention, the normal points from fixtureA to fixtureB.\nContact Separation\nSeparation is the opposite of penetration. Separation is negative when\nshapes overlap. It is possible that future versions of Box2D will create\ncontact points with positive separation, so you may want to check the\nsign when contact points are reported.\nContact Manifold\nContact between two convex polygons may generate up to 2 contact points.\nBoth of these points use the same normal, so they are grouped into a\ncontact manifold, which is an approximation of a continuous region of\ncontact.\nNormal Impulse\nThe normal force is the force applied at a contact point to prevent the\nshapes from penetrating. For convenience, Box2D works with impulses. The\nnormal impulse is just the normal force multiplied by the time step.\nTangent Impulse\nThe tangent force is generated at a contact point to simulate friction.\nFor convenience, this is stored as an impulse.\nContact Ids\nBox2D tries to re-use the contact force results from a time step as the\ninitial guess for the next time step. Box2D uses contact ids to match\ncontact points across time steps. The ids contain geometric features\nindices that help to distinguish one contact point from another.\nContacts are created when two fixture's AABBs overlap. Sometimes\ncollision filtering will prevent the creation of contacts. Contacts are\ndestroyed with the AABBs cease to overlap.\nSo you might gather that there may be contacts created for fixtures that\nare not touching (just their AABBs). Well, this is correct. It's a\n"chicken or egg" problem. We don't know if we need a contact object\nuntil one is created to analyze the collision. We could delete the\ncontact right away if the shapes are not touching, or we can just wait\nuntil the AABBs stop overlapping. Box2D takes the latter approach\nbecause it lets the system cache information to improve performance.\nContact Class\nAs mentioned before, the contact class is created and destroyed by\nBox2D. Contact objects are not created by the user. However, you are\nable to access the contact class and interact with it.\nYou can access the raw contact manifold:\n// on b2Contact:\npublic GetManifold(): b2Manifold;\n\nYou can potentially modify the manifold, but this is generally not\nsupported and is for advanced usage.\nThere is a helper function to get the b2WorldManifold:\n// on b2Contact:\npublic GetWorldManifold(worldManifold: b2WorldManifold): void;\n\nThis uses the current positions of the bodies to compute world positions\nof the contact points.\nSensors do not create manifolds, so for them use:\nconst touching = sensorContact.IsTouching();\n\nThis function also works for non-sensors.\nYou can get the fixtures from a contact. From those you can get the\nbodies.\nconst fixtureA: b2Fixture = myContact.GetFixtureA();\nconst bodyA: b2Body = fixtureA.GetBody();\n\nYou can disable a contact. This only works inside the\nb2ContactListener::PreSolve event, discussed below.\nAccessing Contacts\nYou can get access to contacts in several ways. You can access the\ncontacts directly on the world and body structures. You can also\nimplement a contact listener.\nYou can iterate over all contacts in the world:\nfor (let c: b2Contact | null = myWorld.GetContactList(); c; c = c.GetNext()) {\n // process c\n}\n\nYou can also iterate over all the contacts on a body. These are stored\nin a graph using a contact edge class.\nfor (let ce: b2ContactEdge | null = myBody.GetContactList(); ce; ce = ce.next) {\n const c: b2Contact = ce.contact;\n // process c\n}\n\nYou can also access contacts using the contact listener that is\ndescribed below.\n\nCaution:\nAccessing contacts off b2World and b2Body may miss some transient\ncontacts that occur in the middle of the time step. Use\nb2ContactListener to get the most accurate results.\n\nContact Listener\nYou can receive contact data by extending b2ContactListener. The\ncontact listener supports several events: begin, end, pre-solve, and\npost-solve.\nclass MyContactListener extends b2ContactListener {\n public BeginContact(contact: b2Contact): void {\n /* handle begin event */\n }\n\n public EndContact(contact: b2Contact): void {\n /* handle end event */\n }\n\n public PreSolve(contact: b2Contact, oldManifold: b2Manifold): void {\n /* handle pre-solve event */\n }\n\n public PostSolve(contact: b2Contact, impulse: b2ContactImpulse): void {\n /* handle post-solve event */\n }\n}\n\n\nCaution:\nDo not keep a reference to the references sent to b2ContactListener.\nInstead make a deep copy of the contact point data into your own buffer.\nThe example below shows one way of doing this.\n\nAt run-time you can create an instance of the listener and register it\nwith b2World::SetContactListener.\nBegin Contact Event\nThis is called when two fixtures begin to overlap. This is called for\nsensors and non-sensors. This event can only occur inside the time step.\nEnd Contact Event\nThis is called when two fixtures cease to overlap. This is called for\nsensors and non-sensors. This may be called when a body is destroyed, so\nthis event can occur outside the time step.\nPre-Solve Event\nThis is called after collision detection, but before collision\nresolution. This gives you a chance to disable the contact based on the\ncurrent configuration. For example, you can implement a one-sided\nplatform using this callback and calling b2Contact::SetEnabled(false).\nThe contact will be re-enabled each time through collision processing,\nso you will need to disable the contact every time-step. The pre-solve\nevent may be fired multiple times per time step per contact due to\ncontinuous collision detection.\npublic PreSolve(contact: b2Contact, oldManifold: b2Manifold): void {\n const worldManifold = new b2WorldManifold();\n contact.GetWorldManifold(worldManifold);\n if (worldManifold.normal.y < -0.5) {\n contact.SetEnabled(false);\n }\n}\n\nThe pre-solve event is also a good place to determine the point state\nand the approach velocity of collisions.\npublic PreSolve(contact: b2Contact, oldManifold: b2Manifold): void {\n const worldManifold = new b2WorldManifold();\n contact.GetWorldManifold(worldManifold);\n\n const state1: b2PointState[] = [];\n const state2: b2PointState[] = [];\n b2GetPointStates(state1, state2, oldManifold, contact.GetManifold());\n\n if (state2[0] === b2_addState) {\n const bodyA: b2Body = contact.GetFixtureA().GetBody();\n const bodyB: b2Body = contact.GetFixtureB().GetBody();\n const point: b2Vec2 = worldManifold.points[0];\n const vA: b2Vec2 = bodyA.GetLinearVelocityFromWorldPoint(point, new b2Vec2());\n const vB: b2Vec2 = bodyB.GetLinearVelocityFromWorldPoint(point, new b2Vec2());\n\n const approachVelocity: number = b2Vec2.Dot(b2Vec2.Subtract(vB, vA, new b2Vec2()), worldManifold.normal);\n\n if (approachVelocity > 1) {\n MyPlayCollisionSound();\n }\n }\n}\n\nPost-Solve Event\nThe post solve event is where you can gather collision impulse results.\nIf you don't care about the impulses, you should probably just implement\nthe pre-solve event.\nIt is tempting to implement game logic that alters the physics world\ninside a contact callback. For example, you may have a collision that\napplies damage and try to destroy the associated actor and its rigid\nbody. However, Box2D does not allow you to alter the physics world\ninside a callback because you might destroy objects that Box2D is\ncurrently processing, leading to orphaned references.\nThe recommended practice for processing contact points is to buffer all\ncontact data that you care about and process it after the time step. You\nshould always process the contact points immediately after the time\nstep; otherwise some other client code might alter the physics world,\ninvalidating the contact buffer. When you process the contact buffer you\ncan alter the physics world, but you still need to be careful that you\ndon't orphan references stored in the contact point buffer. The testbed\nhas example contact point processing that is safe from orphaned\nreferences.\nThis code from the CollisionProcessing test shows how to handle orphaned\nbodies when processing the contact buffer. Here is an excerpt. Be sure\nto read the comments in the listing. This code assumes that all contact\npoints have been buffered in the b2ContactPoint array m_points.\n// We are going to destroy some bodies according to contact\n// points. We must buffer the bodies that should be destroyed\n// because they may belong to multiple contact points.\nconst k_maxNuke = 6;\n// Using a set to prevent duplicates\nconst nuke = new Set<b2Body>();\nlet nukeCount = 0;\n\n// Traverse the contact buffer. Destroy bodies that\n// are touching heavier bodies.\nfor (const point of m_points) {\n const bodyA: b2Body = point.fixtureA.GetBody();\n const bodyB: b2Body = point.FixtureB.GetBody();\n const massA: number = bodyA.GetMass();\n const massB: number = bodyB.GetMass();\n\n if (massA > 0 && massB > 0) {\n if (massB > massA) {\n nuke.add(bodyA);\n } else {\n nuke.add(bodyB);\n }\n\n if (nukeCount === k_maxNuke) {\n break;\n }\n }\n}\n\n// Destroy the bodies\nnuke.forEach((b) => m_world.DestroyBody(b));\n\nContact Filtering\nOften in a game you don't want all objects to collide. For example, you\nmay want to create a door that only certain characters can pass through.\nThis is called contact filtering, because some interactions are filtered\nout.\nBox2D allows you to achieve custom contact filtering by implementing a\nb2ContactFilter class. This class requires you to implement a\nShouldCollide function that receives two b2Shape references. Your function\nreturns true if the shapes should collide.\nThe default implementation of ShouldCollide uses the b2FilterData\ndefined in Chapter 6, Fixtures.\npublic ShouldCollide(fixtureA: b2Fixture, fixtureB: b2Fixture): boolean {\n const filterA: Readonly<b2Filter> = fixtureA.GetFilterData();\n const filterB: Readonly<b2Filter> = fixtureB.GetFilterData();\n\n if (filterA.groupIndex === filterB.groupIndex && filterA.groupIndex !== 0) {\n return filterA.groupIndex > 0;\n }\n\n const collideA = (filterA.maskBits & filterB.categoryBits) !== 0;\n const collideB = (filterA.categoryBits & filterB.maskBits) !== 0\n const collide = collideA && collideB;\n return collide;\n}\n\nAt run-time you can create an instance of your contact filter and\nregister it with b2World::SetContactFilter.\nconst filter = new MyContactFilter();\nworld.SetContactFilter(filter);\n\nWorld\nThe b2World class contains the bodies and joints. It manages all aspects\nof the simulation and allows for asynchronous queries (like AABB queries\nand ray-casts). Much of your interactions with Box2D will be with a\nb2World object.\nCreating and Destroying a World\nCreating a world is fairly simple. You just need to provide a gravity\nvector. Usually you will create a world like this:\nconst myWorld = new b2World.Create(gravity);\n\n// ... do stuff ...\n\nUnlike the C++, there is no destroying a world. Just remove all references and the garbage collector will take care of the rest.\nUsing a World\nThe world class contains factories for creating and destroying bodies\nand joints. These factories are discussed later in the sections on\nbodies and joints. There are some other interactions with b2World that I\nwill cover now.\nSimulation\nThe world class is used to drive the simulation. You specify a time step\nand a velocity and position iteration count. For example:\nconst timeStep = 1 / 60;\nconst stepConfig: b2StepConfig = {\n velocityIterations: 10,\n positionIterations: 8,\n};\nmyWorld.Step(timeStep, stepConfig);\n\nAfter the time step you can examine your bodies and joints for\ninformation. Most likely you will grab the position off the bodies so\nthat you can update your actors and render them. You can perform the\ntime step anywhere in your game loop, but you should be aware of the\norder of things. For example, you must create bodies before the time\nstep if you want to get collision results for the new bodies in that\nframe.\nAs I discussed above in the HelloWorld tutorial, you should use a fixed\ntime step. By using a larger time step you can improve performance in\nlow frame rate scenarios. But generally you should use a time step no\nlarger than 1/30 seconds. A time step of 1/60 seconds will usually\ndeliver a high quality simulation.\nThe iteration count controls how many times the constraint solver sweeps\nover all the contacts and joints in the world. More iteration always\nyields a better simulation. But don't trade a small time step for a\nlarge iteration count. 60Hz and 10 iterations is far better than 30Hz\nand 20 iterations.\nAfter stepping, you should clear any forces you have applied to your\nbodies. This is done with the command b2World::ClearForces. This lets\nyou take multiple sub-steps with the same force field.\nmyWorld.ClearForces();\n\nExploring the World\nThe world is a container for bodies, contacts, and joints. You can grab\nthe body, contact, and joint lists off the world and iterate over them.\nFor example, this code wakes up all the bodies in the world:\nfor (let b: b2Body | null = myWorld.GetBodyList(); b; b = b.GetNext()) {\n b.SetAwake(true);\n}\n\nIn the C++ version, it would have been problematic to destroy a body during iteration.\nIn the TypeScript version, there should be no such issue, since the body is only garbage\ncollected when no more references exist.\nAABB Queries\nSometimes you want to determine all the shapes in a region. The b2World\nclass has a fast log(N) method for this using the broad-phase data\nstructure. You provide an AABB in world coordinates and an\nimplementation of b2QueryCallback. The world calls your class with each\nfixture whose AABB overlaps the query AABB. Return true to continue the\nquery, otherwise return false. For example, the following code finds all\nthe fixtures that potentially intersect a specified AABB and wakes up\nall of the associated bodies.\nconst myQueryCallback: b2QueryCallback = (fixture: b2Fixture): boolean => {\n const body: b2Body = fixture.GetBody();\n body.SetAwake(true);\n\n // Return true to continue the query.\n return true;\n};\n\n// Elsewhere ...\nconst aabb = new b2AABB();\n\naabb.lowerBound.Set(-1, -1);\naabb.upperBound.Set(1, 1);\nmyWorld.QueryAABB(aabb, myQueryCallback);\n\nYou cannot make any assumptions about the order of the callbacks.\nRay Casts\nYou can use ray casts to do line-of-sight checks, fire guns, etc. You\nperform a ray cast by implementing a callback class and providing the\nstart and end points. The world class calls your class with each fixture\nhit by the ray. Your callback is provided with the fixture, the point of\nintersection, the unit normal vector, and the fractional distance along\nthe ray. You cannot make any assumptions about the order of the\ncallbacks.\nYou control the continuation of the ray cast by returning a fraction.\nReturning a fraction of zero indicates the ray cast should be\nterminated. A fraction of one indicates the ray cast should continue as\nif no hit occurred. If you return the fraction from the argument list,\nthe ray will be clipped to the current intersection point. So you can\nray cast any shape, ray cast all shapes, or ray cast the closest shape\nby returning the appropriate fraction.\nYou may also return of fraction of -1 to filter the fixture. Then the\nray cast will proceed as if the fixture does not exist.\nHere is an example:\n// This class captures the closest hit shape.\n// Note that you don't need a class for this, you can do it with functions (and closures) as well!\nclass MyRayCastCallback {\n public fixture: b2Fixture | null = null;\n public readonly point = new b2Vec2();\n public readonly normal = new b2Vec2();\n public fraction = 0;\n\n public callback: b2RayCastCallback = (\n fixture: b2Fixture,\n point: b2Readonly<b2Vec2>,\n normal: b2Readonly<b2Vec2>,\n fraction: number,\n ): number => {\n this.fixture = fixture;\n this.point.Copy(point);\n this.normal.Copy(normal);\n this.fraction = fraction;\n return fraction;\n };\n}\n\n// Elsewhere ...\nconst myCallback = new MyRayCastCallback();\nconst point1: XY = { x: -1, y: 0 };\nconst point2: XY = { x: 3, y: 1 };\nmyWorld.RayCast(point1, point2, myCallback.callback);\nconst { fixture, point, normal, fraction } = myCallback;\n// ...\n\n\nCaution:\nDue to round-off errors, ray casts can sneak through small cracks\nbetween polygons in your static environment. If this is not acceptable\nin your application, trying slightly overlapping your polygons.\n\n","title":"Dynamics Module","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/hello.html","content":"Hello Box2D\nIn the distribution of Box2D is a Hello World project. The program\ncreates a large ground box and a small dynamic box. This code does not\ncontain any graphics. All you will see is text output in the console of\nthe box's position over time.\nThis is a good example of how to get up and running with Box2D.\nCreating a World\nEvery Box2D program begins with the creation of a b2World object.\nb2World is the physics hub that manages objects and simulation.\nIt is easy to create a Box2D world. First, we define the gravity vector.\nconst gravity: XY = { x: 0, y: -10 };\n\nNow we create the world object.\nconst world = b2World.Create(gravity);\n\nSo now we have our physics world, let's start adding some stuff to it.\nCreating a Ground Box\nBodies are built using the following steps:\n\nDefine a body with position, damping, etc.\nUse the world object to create the body.\nDefine fixtures with a shape, friction, density, etc.\nCreate fixtures on the body.\n\nFor step 1 we create the ground body. For this we need a body\ndefinition. With the body definition we specify the initial position of\nthe ground body.\nconst groundBodyDef: b2BodyDef = {\n position: { x: 0, y: -10 },\n};\n\nFor step 2 the body definition is passed to the world object to create\nthe ground body. The world object does not keep a reference to the body\ndefinition. Bodies are static by default. Static bodies don't collide\nwith other static bodies and are immovable.\nconst body = world.CreateBody(groundBodyDef);\n\nFor step 3 we create a ground polygon. We use the SetAsBox shortcut to\nform the ground polygon into a box shape, with the box centered on the\norigin of the parent body.\nconst box = new b2PolygonShape();\nbox.SetAsBox(50, 10);\n\nThe SetAsBox function takes the half-width and\nhalf-height (extents). So in this case the ground box is 100\nunits wide (x-axis) and 20 units tall (y-axis). Box2D is tuned for\nmeters, kilograms, and seconds. So you can consider the extents to be in\nmeters. Box2D generally works best when objects are the size of typical\nreal world objects. For example, a barrel is about 1 meter tall. Due to\nthe limitations of floating point arithmetic, using Box2D to model the\nmovement of glaciers or dust particles is not a good idea.\nWe finish the ground body in step 4 by creating the shape fixture.\nA b2FixtureDef has lots of optional properties. For now, we only care\nabout shape. Later we will see how to use the other properties.\nFor a static body we don't need anything more right now.\ngroundBody.CreateFixture({ shape: groundBox });\n\nBox2D does not keep a reference to the shape. It clones the data into a\nnew b2Shape object.\nNote that every fixture must have a parent body, even fixtures that are\nstatic. However, you can attach all static fixtures to a single static\nbody.\nWhen you attach a shape to a body using a fixture, the shape's\ncoordinates become local to the body. So when the body moves, so does\nthe shape. A fixture's world transform is inherited from the parent\nbody. A fixture does not have a transform independent of the body. So we\ndon't move a shape around on the body. Moving or modifying a shape that\nis on a body is not supported. The reason is simple: a body with\nmorphing shapes is not a rigid body, but Box2D is a rigid body engine.\nMany of the assumptions made in Box2D are based on the rigid body model.\nIf this is violated many things will break\nCreating a Dynamic Body\nSo now we have a ground body. We can use the same technique to create a\ndynamic body. The main difference, besides dimensions, is that we must\nestablish the dynamic body's mass properties.\nFirst we create the body using CreateBody. By default bodies are static,\nso we should set the b2BodyType at construction time to make the body\ndynamic.\nconst bodyDef: b2BodyDef = {\n type: b2BodyType.b2_dynamicBody,\n position: { x: 0, y: 4 },\n};\nconst body = world.CreateBody(bodyDef);\n\n\nCaution:\nYou must set the body type to b2_dynamicBody if you want the body to\nmove in response to forces.\n\nNext we create and attach a polygon shape using a fixture definition.\nFirst we create a box shape:\nconst dynamicBox = new b2PolygonShape();\ndynamicBox.SetAsBox(1, 1);\n\nNext we create a fixture definition using the box. Notice that we set\ndensity to 1. The default density is zero. Also, the friction on the\nshape is set to 0.3.\nconst fixtureDef: b2FixtureDef = {\n shape: groundBox,\n density: 1,\n friction: 0.3,\n};\n\n\nCaution:\nA dynamic body should have at least one fixture with a non-zero density.\nOtherwise you will get strange behavior.\n\nUsing the fixture definition we can now create the fixture. This\nautomatically updates the mass of the body. You can add as many fixtures\nas you like to a body. Each one contributes to the total mass.\nbody.CreateFixture(fixtureDef);\n\nThat's it for initialization. We are now ready to begin simulating.\nSimulating the World\nSo we have initialized the ground box and a dynamic box. Now we are\nready to set Newton loose to do his thing. We just have a couple more\nissues to consider.\nBox2D uses a computational algorithm called an integrator. Integrators\nsimulate the physics equations at discrete points of time. This goes\nalong with the traditional game loop where we essentially have a flip\nbook of movement on the screen. So we need to pick a time step for\nBox2D. Generally physics engines for games like a time step at least as\nfast as 60Hz or 1/60 seconds. You can get away with larger time steps,\nbut you will have to be more careful about setting up the definitions\nfor your world. We also don't like the time step to change much. A\nvariable time step produces variable results, which makes it difficult\nto debug. So don't tie the time step to your frame rate (unless you\nreally, really have to). Without further ado, here is the time step.\nconst timeStep = 1 / 60;\n\nIn addition to the integrator, Box2D also uses a larger bit of code\ncalled a constraint solver. The constraint solver solves all the\nconstraints in the simulation, one at a time. A single constraint can be\nsolved perfectly. However, when we solve one constraint, we slightly\ndisrupt other constraints. To get a good solution, we need to iterate\nover all constraints a number of times.\nThere are two phases in the constraint solver: a velocity phase and a\nposition phase. In the velocity phase the solver computes the impulses\nnecessary for the bodies to move correctly. In the position phase the\nsolver adjusts the positions of the bodies to reduce overlap and joint\ndetachment. Each phase has its own iteration count. In addition, the\nposition phase may exit iterations early if the errors are small.\nThe suggested iteration count for Box2D is 8 for velocity and 3 for\nposition. You can tune this number to your liking, just keep in mind\nthat this has a trade-off between performance and accuracy. Using fewer\niterations increases performance but accuracy suffers. Likewise, using\nmore iterations decreases performance but improves the quality of your\nsimulation. For this simple example, we don't need much iteration. Here\nare our chosen iteration counts.\nconst stepConfig: b2StepConfig = {\n velocityIterations: 6,\n positionIterations: 2,\n};\n\nNote that the time step and the iteration count are completely\nunrelated. An iteration is not a sub-step. One solver iteration is a\nsingle pass over all the constraints within a time step. You can have\nmultiple passes over the constraints within a single time step.\nWe are now ready to begin the simulation loop. In your game the\nsimulation loop can be merged with your game loop. In each pass through\nyour game loop you call b2World::Step. Just one call is usually enough,\ndepending on your frame rate and your physics time step.\nThe Hello World program was designed to be simple, so it has no\ngraphical output. The code prints out the position and rotation of the\ndynamic body. Here is the simulation loop that simulates 60 time steps\nfor a total of 1 second of simulated time.\nfor (let i = 0; i < 60; ++i) {\n world.Step(timeStep, stepConfig);\n const position = body.GetPosition();\n const angle = body.GetAngle();\n console.log(`${position.x.toFixed(2)} ${position.y.toFixed(2)} ${angle.toFixed(2)}\\n`);\n}\n\nThe output shows the box falling and landing on the ground box. Your\noutput should look like this:\n0.00 4.00 0.00\n0.00 3.99 0.00\n0.00 3.98 0.00\n...\n0.00 1.25 0.00\n0.00 1.13 0.00\n0.00 1.01 0.00\n\nCleanup\nUnlike the C++, there is no destroying a world. Just remove all references and the garbage collector will take care of the rest.\nHowever, you will need to remove any body, fixture, or joint references you have\nbecause will prevent the garbage collector from doing its thing.\n","title":"Hello Box2D","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/key-differences.html","content":"Key Differences to C++\nThe TypeScript API differs in a few points from the original C++ API.\nLuckily, TypeScript will help you with the API as you use it, so you should be able to spot the changes quickly.\nNevertheless, make sure you read the following points to understand how to handle use this library correctly:\nNo Class Instances on the Stack\nThis one is obvious for anyone who worked with JS before:\nYou can't create class instances on the stack. This means, that you'll need to explicitly create them with new:\n// b2PolygonShape groundBox;\nconst groundBox = new b2PolygonShape();\n\nGarbage Collector Considerations\nSince we can't have class instances on the stack, we need to make sure we don't spam the garbage collector with lots & lots of new objects on every iteration.\nThe usual approach is to have temporary objects, which you can reuse.\n// Bad:\nexport function doStuff(xx: number, xy: number, yx: number, yy: number) {\n const a = new b2Vec2(xx, xy);\n const b = new b2Vec2(yx, yy);\n //...\n return b2Vec2.Clone(a).add(b);\n}\n\n// Better:\nconst temp = {\n a: new b2Vec2(),\n b: new b2Vec2(),\n};\nexport function doStuffRight(xx: number, xy: number, yx: number, yy: number, out: b2Vec2) {\n a.Set(xx, xy);\n b.Set(yx, yy);\n //...\n return out.Copy(a).add(b);\n}\n\nExamples in this documentation might not reflect the above pattern in order to stay small and understandable.\nOperators\nIn C++ you can use operators (+, -, etc.) on classes. There is no equivalent in TypeScript (or JavaScript). Instead, you'll find methods, which we tried to name as best as possible:\nconst temp = {\n sum: new b2Vec2(),\n};\nfunction doStuff(a: b2Readonly<b2Vec2>, b: b2Readonly<b2Vec2>) {\n // b2Vec2 sum = a + b;\n const sum = sum.Copy(a).add(b);\n //...\n}\n\nBe sure to check out all methods and static methods of the classes you are interested in to see all possibilities.\nMath Operations\nAs you might have noticed, math operations return a reference to itself, so you can use chaining. Due to garbage collector considerations, there is no automatic cloning going on!\nVec2 vs XY\nYou might have noticed the type XY, which seems to be in places, where the C++ version of Box2D used b2Vec2.\nThis change has been introduced to simplify the code, as in most cases, we only care about the x/y properties of the vector.\nSo in some cases, you can just use { x: 1, y: 2 } instead of new b2Vec2(1, 2)\nOverloads\nJavaScript has no overloads and while TypeScript supports overload method definitions, we'd have to check the parameters in runtime to make this work (not good for performance). That's why we try to avoid them and supply alternatively named functions instead:\nconst temp = {\n sum: new b2Vec2(),\n};\nfunction doStuff(a: b2Readonly<b2Vec2>, x: number, y: number) {\n // b2Vec2 sum = a + b2Vec2(x, y);\n const sum = sum.Copy(a).addXY(x, y);\n //...\n}\n\nNotice how add(v) accepts one parameter of type b2Readonly<b2Vec2>, while addXY(x, y) accepts two parameters of type number.\nFactory Functions vs Constructors\nIn order to be able to split the extension packages from the core package, we needed to intercept the constructor call of certain classes. Since this is (to my knowledge) not possible in JavaScript, I've introduced factory functions for these cases. In these cases, you'll find that the constructor is private and you can't create an instance. In this case, look for a static Create method:\n// b2World world(gravity);\nconst world = b2World.Create(gravity);\n\nUser Data\nUser data in the c++ version is a pointer to an object you specify.\nIn the TypeScript Version, it's a record of properties. This helps to keep things type safe.\nSee loose ends for more details.\n","title":"Key Differences to C++","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/overview.html","content":"Overview\nBox2D is a 2D rigid body simulation library for games. Programmers can\nuse it in their games to make objects move in realistic ways and make\nthe game world more interactive. From the game engine's point of view,\na physics engine is just a system for procedural animation.\n@box2d is written in TypeScript. Most of the types defined in the\nengine begin with the b2 prefix, so that existing code and examples\nfrom the C++ version are easily ported.\nPrerequisites\nIn this manual I'll assume you are familiar with basic physics\nconcepts, such as mass, force, torque, and impulses. If not, please\nfirst consult Google search and Wikipedia.\nBox2D was created as part of a physics tutorial at the Game Developer\nConference. You can get these tutorials from the download section of\nbox2d.org.\n@box2d is a TypeScript port of Erin Cattos Box2D.\nThis is a fork of box2d.ts from Isaac Burns (flyover)\nwho did a huge job initially porting Box2D and LiquidFun to TypeScript.\nIn order to avoid changing the whole documentation wording from Box2D to @box2d,\nplease assume them to be synonymous within this documentation unless specified otherwise.\nSince Box2D is written in TypeScript, you are expected to be experienced\nin TypeScript or at least JavaScript programming.\nBox2D should not be your first TypeScript/JavaScript programming project! You\nshould be comfortable with bundling, creating a dev-server, and debugging.\n\nCaution:\nBox2D should not be your first TypeScript/JavaScript project. Please learn TS/JS\nprogramming, bundling and debugging before working with\nBox2D. There are many resources for this on the net.\n\nScope\nThis manual covers the majority of the Box2D API. However, not every\naspect is covered. Please look at the testbed included\nwith Box2D to learn more.\nThis manual is updated with every commit in the master branch,\nso it might be newer than the version you currently have installed.\nNonetheless, since there is no major work going on with the API,\nit should not change too much.\nFeedback and Bugs\nPlease file bugs and feature requests here:\n@box2d Issues\nYou can help to ensure your issue gets fixed if you provide sufficient\ndetail. A testbed example that reproduces the problem is ideal. You can\nread about the testbed later in this document.\nThere is also a Discord server and a\nsubreddit for the C++ version of Box2D\nif you need general help with Box2D (rather than the TypeScript port).\nCore Concepts\nBox2D works with several fundamental concepts and objects. We briefly\ndefine these objects here and more details are given later in this\ndocument.\nShape\nA shape is 2D geometrical object, such as a circle or polygon.\nRigid Body\nA chunk of matter that is so strong that the distance between any two\nbits of matter on the chunk is constant. They are hard like a diamond.\nIn the following discussion we use body interchangeably with rigid body.\nFixture\nA fixture binds a shape to a body and adds material properties such as\ndensity, friction, and restitution. A fixture puts a shape into the\ncollision system (broad-phase) so that it can collide with other shapes.\nConstraint\nA constraint is a physical connection that removes degrees of freedom\nfrom bodies. A 2D body has 3 degrees of freedom (two translation\ncoordinates and one rotation coordinate). If we take a body and pin it\nto the wall (like a pendulum) we have constrained the body to the wall.\nAt this point the body can only rotate about the pin, so the constraint\nhas removed 2 degrees of freedom.\nContact Constraint\nA special constraint designed to prevent penetration of rigid bodies and\nto simulate friction and restitution. You do not create contact\nconstraints; they are created automatically by Box2D.\nJoint\nThis is a constraint used to hold two or more bodies together. Box2D\nsupports several joint types: revolute, prismatic, distance, and more.\nSome joints may have limits and motors.\nJoint Limit\nA joint limit restricts the range of motion of a joint. For example, the\nhuman elbow only allows a certain range of angles.\nJoint Motor\nA joint motor drives the motion of the connected bodies according to the\njoint's degrees of freedom. For example, you can use a motor to drive\nthe rotation of an elbow.\nWorld\nA physics world is a collection of bodies, fixtures, and constraints\nthat interact together. Box2D supports the creation of multiple worlds,\nbut this is usually not necessary or desirable.\nSolver\nThe physics world has a solver that is used to advance time and to\nresolve contact and joint constraints. The Box2D solver is a high\nperformance iterative solver that operates in order N time, where N is\nthe number of constraints.\nContinuous Collision\nThe solver advances bodies in time using discrete time steps. Without\nintervention this can lead to tunneling.\n\nBox2D contains specialized algorithms to deal with tunneling. First, the\ncollision algorithms can interpolate the motion of two bodies to find\nthe first time of impact (TOI). Second, there is a sub-stepping solver\nthat moves bodies to their first time of impact and then resolves the\ncollision.\nModules\nBox2D is composed of three modules: Common, Collision, and Dynamics. The\nCommon module has code for augmentation, math, and settings. The Collision\nmodule defines shapes, a broad-phase, and collision functions/queries.\nFinally the Dynamics module provides the simulation world, bodies,\nfixtures, and joints.\n\nUnits\nBox2D works with floating point numbers and tolerances have to be used\nto make Box2D perform well. These tolerances have been tuned to work\nwell with meters-kilogram-second (MKS) units. In particular, Box2D has\nbeen tuned to work well with moving shapes between 0.1 and 10 meters. So\nthis means objects between soup cans and buses in size should work well.\nStatic shapes may be up to 50 meters long without trouble.\nBeing a 2D physics engine, it is tempting to use pixels as your units.\nUnfortunately this will lead to a poor simulation and possibly weird\nbehavior. An object of length 200 pixels would be seen by Box2D as the\nsize of a 45 story building.\n\nCaution:\nBox2D is tuned for MKS units. Keep the size of moving objects roughly\nbetween 0.1 and 10 meters. You'll need to use some scaling system when\nyou render your environment and actors. The Box2D testbed does this by\nusing an OpenGL viewport transform. DO NOT USE PIXELS.\n\nIt is best to think of Box2D bodies as moving billboards upon which you\nattach your artwork. The billboard may move in a unit system of meters,\nbut you can convert that to pixel coordinates with a simple scaling\nfactor. You can then use those pixel coordinates to place your sprites,\netc. You can also account for flipped coordinate axes.\nAnother limitation to consider is overall world size. If your world units\nbecome larger than 2 kilometers or so, then the lost precision can affect\nstability.\n\nCaution:\nBox2D works best with world sizes less than 2 kilometers. Use\nb2World::ShiftOrigin to support larger worlds.\n\nIf you need to have a larger game world, consider using\nb2World::ShiftOrigin to keep the world origin close to your player. I recommend\nto use grid lines along with some hysteresis for triggering calls to ShiftOrigin.\nThis call should be made infrequently because it is has CPU cost. You may\nneed to store a physics offset when translating between game units and Box2D units.\nBox2D uses radians for angles. The body rotation is stored in radians\nand may grow unbounded. Consider normalizing the angle of your bodies if\nthe magnitude of the angle becomes too large (use b2Body::SetTransform).\n\nCaution:\nBox2D uses radians, not degrees.\n\nChanging The Length Units\nAdvanced users may change the length unit.\nFactories and Definitions\nFast memory management plays a central role in the design of the Box2D\nAPI. So when you create a b2Body or a b2Joint, you need to call the\nfactory functions on b2World. You should never try to allocate these\ntypes in another manner.\nThere are creation functions:\nexport class b2World {\n // ...\n public CreateBody(def: b2BodyDef = {}): b2Body;\n // ...\n // CreateJoint has a couple of overloads:\n public CreateJoint(def: b2IAreaJointDef): b2AreaJoint;\n public CreateJoint(def: b2IDistanceJointDef): b2DistanceJoint;\n public CreateJoint(def: b2IFrictionJointDef): b2FrictionJoint;\n public CreateJoint(def: b2IGearJointDef): b2GearJoint;\n public CreateJoint(def: b2IMotorJointDef): b2MotorJoint;\n public CreateJoint(def: b2IMouseJointDef): b2MouseJoint;\n public CreateJoint(def: b2IPrismaticJointDef): b2PrismaticJoint;\n public CreateJoint(def: b2IPulleyJointDef): b2PulleyJoint;\n public CreateJoint(def: b2IRevoluteJointDef): b2RevoluteJoint;\n public CreateJoint(def: b2IWeldJointDef): b2WeldJoint;\n public CreateJoint(def: b2IWheelJointDef): b2WheelJoint;\n // ...\n\nAnd there are corresponding destruction functions:\nexport class b2World {\n // ...\n public DestroyBody(b: b2Body): void;\n public DestroyJoint(j: b2Joint): void;\n // ...\n\nWhen you create a body or joint, you need to provide a definition. These\ndefinitions contain all the information needed to build the body or\njoint. By using this approach we can prevent construction errors, keep\nthe number of function parameters small, provide sensible defaults, and\nreduce the number of accessors.\nSince fixtures (shapes) must be parented to a body, they are created and\ndestroyed using a factory method on b2Body:\nexport class b2Body {\n // ...\n public CreateFixture(def: b2FixtureDef): b2Fixture;\n public DestroyFixture(fixture: b2Fixture): void;\n // ...\n\nFactories do not retain references to the definitions. So you can reuse\nthem for other further Create* calls.\n","title":"Overview","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/loose-ends.html","content":"Loose Ends\nUser Data\nThe b2Fixture, b2Body, and b2Joint classes allow you to attach user data.\nThis is handy when you are examining Box2D data structures and you want to\ndetermine how they relate to the objects in your game engine.\nFor example, it is typical to attach an actor reference to the rigid body\non that actor. This sets up a circular reference. If you have the actor,\nyou can get the body. If you have the body, you can get the actor.\nconst actor = GameCreateActor();\nconst bodyDef: b2BodyDef = {\n userData: { actor },\n};\nactor.body = myWorld.CreateBody(bodyDef);\n\nYou can also use this to hold an primitive value rather than a reference.\nHere are some examples of cases where you would need the user data:\n\nApplying damage to an actor using a collision result.\nPlaying a scripted event if the player is inside an axis-aligned box.\nAccessing a game structure when Box2D notifies you that a joint is\ngoing to be destroyed.\n\nKeep in mind that user data is optional and you can put anything in it.\nUser data records are empty by default.\nFor fixtures you might consider defining a user data structure that lets\nyou store game specific information, such as material type, effects\nhooks, sound hooks, etc.\nconst fixtureDef: b2FixtureDef = {\n shape: someShape,\n userData: {\n materialIndex: 2,\n },\n};\n\nconst fixture = body.CreateFixture(fixtureDef);\n\nUser Data Types\nUser data is a Record of references. You can specify the types of user data\nrecords for bodies, fixtures and joints separately.\n// box2dconfig.d.ts\nimport "@box2d/core";\n\ndeclare module "@box2d/core" {\n export interface b2BodyUserDataMap {\n actor: MyActor;\n }\n export interface b2FixtureUserDataMap {\n materialIndex: number;\n }\n export interface b2JointUserDataMap {\n bar: MyBar;\n }\n}\n\nAny property you specify in these b2*UserDataMap interfaces will be optionally available in the respective user data record.\nSince specifying user data properties is optional, you'll need to check if it exists when reading the user data:\nconst myActor: MyActor | undefined = myBody.GetUserData().actor;\nif (myActor !== undefined) {\n // work with the data..\n}\n\nImplicit Destruction\n@box2d is in the JavaScript environment and as such, the garbage collector\nis responsible for removing unused objects. For that to be possible, you need\nto make sure you have no references left to those objects.\nIf you destroy a Box2D entity, it is up to you to make sure you remove\nall references to the destroyed object. This is easy if you only have a\nsingle reference to the entity. If you have multiple references, you\nmight consider implementing a handle class to wrap the reference.\nOften when using Box2D you will create and destroy many bodies, shapes,\nand joints. Managing these entities is somewhat automated by Box2D. If\nyou destroy a body then all associated shapes and joints are\nautomatically destroyed. This is called implicit destruction.\nWhen you destroy a body, all its attached shapes, joints, and contacts\nare destroyed. This is called implicit destruction. Any body connected\nto one of those joints and/or contacts is woken. This process is usually\nconvenient. However, you must be aware of one crucial issue:\n\nCaution:\nWhen a body is destroyed, all fixtures and joints attached to the body\nare automatically destroyed. You must remove any references you have to\nthose shapes and joints. Otherwise, your program will behave strange if\nyou try to access or destroy those shapes or joints later.\n\nTo help you remove your joint references, Box2D provides a listener class\nnamed b2DestructionListener that you can implement and provide to your\nworld object. Then the world object will notify you when a joint is\ngoing to be implicitly destroyed\nNote that there no notification when a joint or fixture is explicitly\ndestroyed. In this case ownership is clear and you can perform the\nnecessary cleanup on the spot. If you like, you can call your own\nimplementation of b2DestructionListener to keep cleanup code\ncentralized.\nImplicit destruction is a great convenience in many cases. It can also\nmake your program fall apart. You may store references to shapes and\njoints somewhere in your code. These references become orphaned when an\nassociated body is destroyed. The situation becomes worse when you\nconsider that joints are often created by a part of the code unrelated\nto management of the associated body. For example, the testbed creates a\nb2MouseJoint for interactive manipulation of bodies on the screen.\nBox2D provides a callback mechanism to inform your application when\nimplicit destruction occurs. This gives your application a chance to\nremove the orphaned references. This callback mechanism is described\nlater in this manual.\nYou can implement a b2DestructionListener that allows b2World to inform\nyou when a shape or joint is implicitly destroyed because an associated\nbody was destroyed. This will help prevent your code from accessing\norphaned references.\nclass MyDestructionListener extends b2DestructionListener {\n override void SayGoodbye(joint: b2Joint) {\n // remove all references to joint.\n }\n};\n\nYou can then register an instance of your destruction listener with your\nworld object. You should do this during world initialization.\nmyWorld.SetDestructionListener(myDestructionListener);\n\nPixels and Coordinate Systems\nRecall that Box2D uses MKS (meters, kilograms, and seconds) units and\nradians for angles. You may have trouble working with meters because\nyour game is expressed in terms of pixels. To deal with this in the\ntestbed I have the whole game work in meters and just use an OpenGL\nviewport transformation to scale the world into screen space.\nconst lowerX = -25,\n upperX = 25,\n lowerY = -5,\n upperY = 25;\nyourOrtho2D(lowerX, upperX, lowerY, upperY);\n\nIf your game must work in pixel units then you should convert your\nlength units from pixels to meters when passing values from Box2D.\nLikewise you should convert the values received from Box2D from meters\nto pixels. This will improve the stability of the physics simulation.\nYou have to come up with a reasonable conversion factor. I suggest\nmaking this choice based on the size of your characters. Suppose you\nhave determined to use 50 pixels per meter (because your character is 75\npixels tall). Then you can convert from pixels to meters using these\nformulas:\nxMeters = 0.02 * xPixels;\nyMeters = 0.02 * yPixels;\n\nIn reverse:\nxPixels = 50 * xMeters;\nyPixels = 50 * yMeters;\n\nYou should consider using MKS units in your game code and just convert\nto pixels when you render. This will simplify your game logic and reduce\nthe chance for errors since the rendering conversion can be isolated to\na small amount of code.\nIf you use a conversion factor, you should try tweaking it globally to\nmake sure nothing breaks. You can also try adjusting it to improve\nstability.\nDebug Drawing\nYou can implement the b2Draw interface to get detailed drawing of the\nphysics world. Here are the available entities:\n\nshape outlines\njoint connectivity\nbroad-phase axis-aligned bounding boxes (AABBs)\ncenter of mass\n\n\nThis is the preferred method of drawing these physics entities, rather\nthan accessing the data directly. The reason is that much of the\nnecessary data is internal and subject to change.\nThe testbed draws physics entities using the debug draw facility and the\ncontact listener, so it serves as the primary example of how to\nimplement debug drawing as well as how to draw contact points.\nLimitations\nBox2D uses several approximations to simulate rigid body physics\nefficiently. This brings some limitations.\nHere are the current limitations:\n\nStacking heavy bodies on top of much lighter bodies is not stable. Stability degrades as the mass ratio passes 10:1.\nChains of bodies connected by joints may stretch if a lighter body is supporting a heavier body. For example, a wrecking ball connect to a chain of light weight bodies may not be stable. Stability degrades as the mass ratio passes 10:1.\nThere is typically around 0.5cm of slop in shape versus shape collision.\nContinuous collision does not handle joints. So you may see joint stretching on fast moving objects.\nBox2D uses the symplectic Euler integration scheme. It does not reproduce parabolic motion of projectiles and has only first-order accuracy. However it is fast and has good stability.\nBox2D uses an iterative solver to provide real-time performance. You will not get precisely rigid collisions or pixel perfect accuracy. Increasing the iterations will improve accuracy.\n\n","title":"Loose Ends","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/references.html","content":"References\n\nErin Catto's Publications\nCollision Detection in Interactive 3D Environments, Gino van den Bergen, 2004\nReal-Time Collision Detection, Christer Ericson, 2005\n\n","title":"References","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/setup.html","content":"Setup\nThis is a TypeScript port of Box2D by Erin Catto, which means that for the most part you can use the documentation found at the official homepage. If you need examples, you can find them on GitHub.\nWe aim to stay true to the original API where we can, but sometimes it's not possible. We'll cover the differences in the following tutorials.\nInstall With NPM\nnpm i @box2d/core\n\nTypeScript vs JavaScript\nA note on the documentation on this page:\nAll examples are shown in TypeScript, but you can use @box2d with JavaScript as well. Just remove the type information from the examples and you are good to go.\n","title":"Setup","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/core/testbed.html","content":"Testbed\nOnce you have conquered the HelloWorld example, you should start looking\nat Box2D's testbed. The testbed is a testing framework and demo\nenvironment. Here are some of the features:\n\nCamera with pan and zoom.\nMouse picking of shapes attached to dynamic bodies.\nExtensible set of tests.\nGUI for selecting tests, parameter tuning, and debug drawing options.\nPause and single step simulation.\nText rendering.\n\n\nThe above screenshot is from the C++ version. You can try the @box2d version online:\nhttps://lusito.github.io/box2d.ts/testbed/\nThe testbed has many examples of Box2D usage in the test cases and the\nframework itself. I encourage you to explore and tinker with the testbed\nas you learn Box2D.\nNote: the testbed is written using React.\nThe testbed is not part of the Box2D library.\nThe Box2D library is agnostic about rendering. As shown by\nthe HelloWorld example, you don't need a renderer to use Box2D.\n","title":"Testbed","projectIndex":{"title":"core","url":"https://lusito.github.io/box2d.ts/core/"}},{"url":"https://lusito.github.io/box2d.ts/controllers/","content":"@box2d/controllers\nControllers for @box2d/core\n@box2d/controllers is a TypeScript port of Googles LiquidFun controllers.\nThis is a fork of box2d.ts from Isaac Burns (flyover) who did a huge job initially porting Box2D and LiquidFun to TypeScript.\nHow to Use\nCheck out the documentation\nThe @box2d Ecosystem\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js\nCheck out demos and compare performance here: https://lusito.github.io/box2d.ts/\nFair Warning: The whole @box2d ecosystem is in an early stage, so it will probably change a lot before we release the first stable version (1.0.0).\nOther packages included in the ecosystem:\n\nBenchmark: Based on bench2d by joelgwebber\nControllers: From the LiquidFun project\nParticles: Also from the LiquidFun project\nLights: ported from LibGDX\nDebugDraw: Debug drawing using a canvas\nTestbed: A set of demos, partially ports of the original projects, partially new ones.\n\nContributing\nWe're looking for contributors to make this the best place to start with box2d on the web.\nCheck out the project page for more information: https://github.com/Lusito/box2d.ts\n","title":"controllers","projectIndex":{"title":"controllers","url":"https://lusito.github.io/box2d.ts/controllers/"}},{"url":"https://lusito.github.io/box2d.ts/controllers/setup.html","content":"Setup\nThere doesn't seem to be any official documentation for controllers on the original homepage. Make sure to check out the examples instead.\nWe aim to stay true to the original API where we can, but sometimes it's not possible. We'll cover the differences in upcoming tutorials.\nInstall With NPM\nnpm i @box2d/controllers\n\nThis library extends @box2d/core, so make sure to check that out as well.\n","title":"Setup","projectIndex":{"title":"controllers","url":"https://lusito.github.io/box2d.ts/controllers/"}},{"url":"https://lusito.github.io/box2d.ts/lights/","content":"@box2d/lights\n\nA TypeScript port of Kalle Hameleinen's Box2DLights.\n@box2d/lights is a 2D lighting framework that uses @box2d/core for raycasting and WebGL for rendering. This library can be used without @box2d/core, so if your 2D physics library supports raycasting, you might be able to use this as well.\nFeatures\n\nArbitrary number of lights\nGaussian blurred light maps\nPoint light\nCone Light\nDirectional Light\nChain Light (New in 1.3)\nShadows\nDynamic/static/xray light\nCulling\nColored ambient light\nGamma corrected colors\nHandler class to do all the work\nQuery method for testing is point inside of light/shadow\n\nThis library offer easy way to add soft dynamic 2d lights to your physic based game.\nHow to Use\nCheck out the documentation\nAlso checkout the testbed for simple examples.\nThe @box2d Ecosystem\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js\nCheck out demos and compare performance here: https://lusito.github.io/box2d.ts/\nFair Warning: The whole @box2d ecosystem is in an early stage, so it will probably change a lot before we release the first stable version (1.0.0).\nOther packages included in the ecosystem:\n\nBenchmark: Based on bench2d by joelgwebber\nControllers: From the LiquidFun project\nParticles: Also from the LiquidFun project\nLights: ported from LibGDX\nDebugDraw: Debug drawing using a canvas\nTestbed: A set of demos, partially ports of the original projects, partially new ones.\n\nContributing\nWe're looking for contributors to make this the best place to start with box2d on the web.\nCheck out the project page for more information: https://github.com/Lusito/box2d.ts\n","title":"lights","projectIndex":{"title":"lights","url":"https://lusito.github.io/box2d.ts/lights/"}},{"url":"https://lusito.github.io/box2d.ts/lights/custom-viewport.html","content":"Custom Viewport\nThe following is copied from the original documentation and might need to be adjusted for the TypeScript version:\nSupport for custom viewports, e.g. resolution independend viewports is available only since box2dlights 1.3, if you are using older version, please update to at least 1.3 to enable support.\nFor the correct viewport rendering, if not using default values, you should set it manually:\nrayHandler.useCustomViewport(x, y, width, height);\n\nFor example if you use a viewport:\nrayHandler.useCustomViewport(\n viewport.getScreenX(),\n viewport.getScreenY(),\n viewport.getScreenWidth(),\n viewport.getScreenHeight(),\n);\n\nWhere the x, y, width and height are the values of your custom viewport.\nFor correct rendering after the window is resized or viewport sizes changed, you should call this method manually again. E.g. inside of resize(width, height) method.\nWhen no more needed, it could be reset back to default:\nrayHandler.useDefaultViewport();\n\n","title":"Custom Viewport","projectIndex":{"title":"lights","url":"https://lusito.github.io/box2d.ts/lights/"}},{"url":"https://lusito.github.io/box2d.ts/lights/getting-started.html","content":"Getting Started\nPreparations\nSince the @box2d/lights package is written without a dependency to @box2d/core (so it can be used by other physics librares), you'll need to create some glue code. I've preparaed a sample implementation on GitHub. Feel free to just copy/paste that into your code-base.\nSetup\nIn your setup code, write:\nconst rayHandler = new RayHandlerImpl(world, glContext, camera.width / 4, camera.height / 4, viewport.x, viewport.y);\n\nEnable/Disable Shadows\nYou can disable shadows with:\nrayHandler.setShadows(false);\n\nCreating Lights\nAmbient Light\nYou can set the ambient light with\nrayHandler.setAmbientLight(r, g, b, a);\nrayHandler.setBlurNum(3);\n\nPoint Lights\nThis creates a new white point light. RAY_NUM being the number of ray lights (e.g.: 4 is a simple star)\nconst light = new PointLight(rayHandler, RAYS_NUM, new LightColor(1, 1, 1, 1), lightDistance, x, y);\n\nRendering\nIn your render loop after everything is drawn that you want to be lit:\nrayHandler.setCombinedMatrix(camera.combined, center.x, center.y, camera.width, camera.height);\nrayHandler.updateAndRender();\n\nCleanup\nRemember to dispose the ray handler (and lights, etc.) when cleaning up:\nrayHandler.dispose();\n\nExamples\n\nDemos available on GitHub here and here.\nFor more details, check out these examples.\n\n","title":"Getting Started","projectIndex":{"title":"lights","url":"https://lusito.github.io/box2d.ts/lights/"}},{"url":"https://lusito.github.io/box2d.ts/lights/glossary.html","content":"Glossary\nThe following is copied from the original documentation and might need to be adjusted for the TypeScript version:\n\nRay Handler is handler class for all the lights. Ray handler manages updating, rendering and disposing the lights. Ray handler also checks the current OpenGl es version and uses the right mode for rendering. If GLes2.0 is detected: gaussian blurred lights map is enabled and used automatically. GLes2.0 has bigger constant time but it scales better with multiple lights because lights are rendered to small FBO instead of render target and the fragment shader has to do less work.\n\nnew RayHandler(box2dWorld, fboWidth, fboHeight);\n\n\n\nLight Types\n\nPoint Light is first concrete class. It's the simplest and most used light. Point lights have meaningful position and distance and all other lights attributes. Point lights are always circular shaped.\n\nnew PointLight(rayHandler, numRays, color, maxDistance, x, y);\n\n\nCone Light is second concrete class. It's basically a point light but only a sector of the full circle. Cone lights have direction and cone degree as additional parameters. cone degree is aberration of straight line to both directions. Setting Cone Degree to 180 means that you got full circle.\n\nnew ConeLight(rayHandler, numRays, color, maxDistance, x, y, directionDegree, coneDegree);\n\n\nDirectional Light simulate light source that location is at infinite distance. This means that direction and intensity is the same everywhere. -90 direction is straight from up. This type of light is good for simulating the sun.\n\nnew DirectionalLight(rayHandler, numRays, color, directionDegree);\n\n\n\nLight Abstract Classes\n\nLight Abstract class that contain shared parameters and act as "interface" for all the lights. Light is made from a bunch of rays that are tested with raycasting against box2d geometry and from this data the light mesh is constructed and rendered to scene. All lights have meaningful color, number of rays, softShadowLenght and some booleans like active, soft, xray, staticLight.\nPositional Light is also an abstract class. This contain shared parameters between Point- and Cone light. Positional light have position and finite distance.\n\n","title":"Glossary","projectIndex":{"title":"lights","url":"https://lusito.github.io/box2d.ts/lights/"}},{"url":"https://lusito.github.io/box2d.ts/lights/performance-tuning.html","content":"Performance Tuning\nThe following is copied from the original documentation and might need to be adjusted for the TypeScript version:\nObject Creation\n\nRay Handler is heavy weight object. It contains many float arrays and couple FBO's when openGL es 2.0 is available. Ray Handler can be safely used over the application lifetime. Ray Handler need to be disposed when its destroyed.\nLights are moderate weight objects. Every light contain two mesh and if possible try to reuse these lights. Just set light to disabled if you know that you will be needing another that type of light soon. Remove lights if you don't need them anymore or there is big time window between them. Be aware that setActive(false) do not set body reference to null.\n\nPerformance Tips\n\nTips are marked with (CPU | GPU | MEMORY) depending which part of system that tip might help.\nDisabled light do not have any performance overhead. Disable lights are stored on separate list. (CPU & GPU)\nDisable shadows if game is running on platform with GL es 1.0 and where render target do not have alpha channel. Usually this mean android with gles1.0. This maybe can be done automatically in library but don't count on that! (GPU)\nStatic flag make light very cheap. You can use static light even for objects that move but they do it rarely. Static lights can be attached to bodies but they do not follow body movement after initial attach call. Every time you call method that alter static light state it has to be updated. Static lights is wrong choice if light is updated every frame. (CPU)\nXray flag prevent all the raycasting for the light. So this lower cpu load about 80%. Xrays are optimal for small objects and usually dynamic light look better/smoother when combined with xray pointlight. Use this flag allways when you don't need dynamic behavior. (CPU)\nSetting FBO size to small make really big difference with performance. First it helps with light drawing because its use less fillrate. Secondly it help with blurring. One blur pass use 5+5 dynamic texture fetch. With 800x480 screen: using original screen size as fbo size and three blur pass would need over 10 million texture look ups. But using box2dLights defaults settings 200x120(quarter of screen sizes) and one blur pass use only 240 000 texture look ups but yield more smoothed look. (GPU & MEMORY)\nBlur passes are heavyweight, try to smaller fbo combined with more passes if really blurred lights are needed. Idea for blur algorithm can be found here http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ (GPU)\nFor dynamic lights use only that amount rays that are absolute needed. Usually best outcome is somewhere between 5 and 128. For something really accurate and visible you sometimes need more but be careful. For every ray box2dLights have to use one raycast and 1 vertex + 2 for soft light.(CPU & GPU & MEMORY)\nLight distance make light a bit heavier. Ray Cast need to travel bigger distance and more pixels need to drawn. For bigger light try first add color alpha channel and maybe add some white color. If that does not help try to use different fallof scheme(Still WIP).(CPU & GPU)\nSet culling enabled. Culling is very lightweight process but safe all raycast calculations and drawing. Lights are culled automatically if they are so much off screen that even lights tips would never occur in screen. Culled light is still not free so if you know something is behind camera really long time maybe that could be disabled.(CPU & GPU)\nBe reasonable with soft light distance and turn softness off it that is not needed.(CPU & GPU)\nOnly create rayHandler with maxNumberRays that are needed (MEMORY)\nIf physics are stepped more sparsely than game is rendered ray handler update can be skipped if physic did not skipped between frames. If physics are updated more often that game is rendered then calling updateAndRender is best choice.(CPU)\nIf not single lights are rendered blurring and light map blitting is skipped. So cull/disable lights that are not visible (GPU)\n\n","title":"Performance Tuning","projectIndex":{"title":"lights","url":"https://lusito.github.io/box2d.ts/lights/"}},{"url":"https://lusito.github.io/box2d.ts/lights/setup.html","content":"Setup\nThis is a TypeScript port of Box2D Lights from LibGDX.\nEven though it is part of the @box2d ecosystem, you can actually use this one with other physics libraries as well.\nWe aim to stay true to the original API where we can, but sometimes it's not possible. We'll cover the differences in the following tutorials.\nInstall With NPM\nnpm i @box2d/lights\n\nThis library is theoretically physics-engine agnostic, so you should be able to use it with whatever 2D physics library you are using. It was mainly designed to work well with @box2d/core, so make sure to check that out as well.\n","title":"Setup","projectIndex":{"title":"lights","url":"https://lusito.github.io/box2d.ts/lights/"}},{"url":"https://lusito.github.io/box2d.ts/particles/","content":"@box2d/particles\nParticles for @box2d/core\n@box2d/particles is a TypeScript port of Googles LiquidFun particles.\nThis is a fork of box2d.ts from Isaac Burns (flyover) who did a huge job initially porting Box2D and LiquidFun to TypeScript.\nHow to Use\nCheck out the documentation\nThe @box2d Ecosystem\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js\nCheck out demos and compare performance here: https://lusito.github.io/box2d.ts/\nFair Warning: The whole @box2d ecosystem is in an early stage, so it will probably change a lot before we release the first stable version (1.0.0).\nOther packages included in the ecosystem:\n\nBenchmark: Based on bench2d by joelgwebber\nControllers: From the LiquidFun project\nParticles: Also from the LiquidFun project\nLights: ported from LibGDX\nDebugDraw: Debug drawing using a canvas\nTestbed: A set of demos, partially ports of the original projects, partially new ones.\n\nContributing\nWe're looking for contributors to make this the best place to start with box2d on the web.\nCheck out the project page for more information: https://github.com/Lusito/box2d.ts\n","title":"particles","projectIndex":{"title":"particles","url":"https://lusito.github.io/box2d.ts/particles/"}},{"url":"https://lusito.github.io/box2d.ts/particles/setup.html","content":"Setup\nThis is a TypeScript port of the particles code found in LiquidFun by Google. For now, you'll have to read the official documentation and adjust it to the TypeScript version. Make sure to check out the examples as well.\nWe aim to stay true to the original API where we can, but sometimes it's not possible. We'll cover the differences in upcoming tutorials.\nInstall With NPM\nnpm i @box2d/particles\n\nThis library extends @box2d/core, so make sure to check that out as well.\n// TODO: https://github.com/google/liquidfun/tree/master/liquidfun/Box2D/Box2D/Documentation/Programmers-Guide\n","title":"Setup","projectIndex":{"title":"particles","url":"https://lusito.github.io/box2d.ts/particles/"}},{"url":"https://lusito.github.io/box2d.ts/debug-draw/","content":"@box2d/debug-draw\nBox2D is a 2D physics engine for games.\n@box2d/debug-draw is a TypeScript port of Erin Cattos debug drawing helper for Box2D.\nThis is a fork of box2d.ts from Isaac Burns (flyover) who did a huge job initially porting Box2D and LiquidFun to TypeScript.\nHow to Use\nCheck out the documentation\nThe @box2d Ecosystem\n@box2d is a full-blown ecosystem for box2d for the JavaScript/TypeScript world. It can be used both in the browser and in node.js\nCheck out demos and compare performance here: https://lusito.github.io/box2d.ts/\nFair Warning: The whole @box2d ecosystem is in an early stage, so it will probably change a lot before we release the first stable version (1.0.0).\nOther packages included in the ecosystem:\n\nBenchmark: Based on bench2d by joelgwebber\nControllers: From the LiquidFun project\nParticles: Also from the LiquidFun project\nLights: ported from LibGDX\nDebugDraw: Debug drawing using a canvas\nTestbed: A set of demos, partially ports of the original projects, partially new ones.\n\nContributing\nWe're looking for contributors to make this the best place to start with box2d on the web.\nCheck out the project page for more information: https://github.com/Lusito/box2d.ts\n","title":"debug-draw","projectIndex":{"title":"debug-draw","url":"https://lusito.github.io/box2d.ts/debug-draw/"}},{"url":"https://lusito.github.io/box2d.ts/debug-draw/setup.html","content":"Setup\n@box2d/debug-draw is a TypeScript port of Erin Cattos debug drawing helper for Box2D.\nThis is a fork of box2d.ts from Isaac Burns (flyover) who did a huge job initially porting Box2D and LiquidFun to TypeScript.\nInstall With NPM\nnpm i @box2d/debug-draw\n\nThis library extends @box2d/core, so make sure to check that out as well.\n","title":"Setup","projectIndex":{"title":"debug-draw","url":"https://lusito.github.io/box2d.ts/debug-draw/"}},{"url":"https://lusito.github.io/box2d.ts/debug-draw/usage.html","content":"Usage\nIn order to draw debug graphics, you'll need a canvas element in your HTML. Make sure, that it is an overlay above your game canvas (same position and size). You might also want to set the style pointer-events: none.\nCreate a New Instance\n// Somewhere at the start, find the element and create a new DebugDraw instance:\nfunction initDebugDraw() {\n const canvas = document.querySelector("#debug-canvas") as HTMLCanvasElement | null;\n if (!canvas) throw new Error("Could not find debug canvas!");\n\n const ctx = canvas.getContext("2d");\n if (!ctx) throw new Error("Could not create 2d context for debug-draw");\n return new DebugDraw(ctx);\n}\n\nPrepare Drawing\nimport { DebugDraw } from "@box2d/debug-draw";\nimport { XY, b2AABB, DrawShapes, DrawJoints, DrawAABBs, DrawCenterOfMasses, DrawPairs } from "@box2d/core";\nimport { DrawParticleSystems } from "@box2d/particles";\nimport { DrawControllers } from "@box2d/controllers";\n\n// After you ran your world.Step(), draw the world like this:\nfunction drawDebug(draw: DebugDraw, center: XY, zoom: number, aabb?: b2AABB) {\n draw.Prepare(center.x, center.y, zoom, true);\n\n // Draw whatever you want here:\n DrawShapes(draw, this.m_world, aabb);\n DrawParticleSystems(draw, this.m_world);\n DrawJoints(draw, this.m_world);\n DrawAABBs(draw, this.m_world, aabb);\n DrawPairs(draw, this.m_world);\n DrawCenterOfMasses(draw, this.m_world);\n DrawControllers(draw, this.m_world);\n\n draw.Finish();\n}\n\nNotice how DrawShapes and DrawAABBs have a third, optional parameter of type AABB.\nThis is a performance optimization if you have a big world. Give it the AABB of your camera to reduce the load.\nThe other drawing functions might receive such a parameter in the future as well, but currently don't support it.\nCustom Renderer\nIn case you want to draw to WebGL directly instead of a canvas, you can write your own b2Draw implementation. You won't need this library anymore, but you can take a look at its code for a reference.\n","title":"Usage","projectIndex":{"title":"debug-draw","url":"https://lusito.github.io/box2d.ts/debug-draw/"}}] \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index dfaaeb75..2a3c1efb 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -12,8 +12,8 @@ https://lusito.github.io/box2d.ts/core/dynamics.html https://lusito.github.io/box2d.ts/core/hello.html https://lusito.github.io/box2d.ts/core/key-differences.html - https://lusito.github.io/box2d.ts/core/loose-ends.html https://lusito.github.io/box2d.ts/core/overview.html + https://lusito.github.io/box2d.ts/core/loose-ends.html https://lusito.github.io/box2d.ts/core/references.html https://lusito.github.io/box2d.ts/core/setup.html https://lusito.github.io/box2d.ts/core/testbed.html diff --git a/testbed/index.js b/testbed/index.js index 7946757d..4d0ed337 100644 --- a/testbed/index.js +++ b/testbed/index.js @@ -174,7 +174,7 @@ uniform sampler2D u_texture; void main() { gl_FragColor = texture2D(u_texture, v_texCoords); } -`;function _x(i){return(0,Zn.createShaderProgram)(i,nA,lA,{a_position:"vertexAttribPointer",a_texCoord:"vertexAttribPointer",u_texture:"uniform1i",ambient:"uniform4f"})}var pu=class{constructor(t,e,r){this.lightMapDrawingDisabled=!1;this.rayHandler=t,this.gl=t.gl,this.vertBuffer=new si(this.gl,this.gl.STATIC_DRAW,this.createVertices()),this.uvBuffer=new si(this.gl,this.gl.STATIC_DRAW,this.createUvCoords()),e<=0&&(e=1),r<=0&&(r=1),this.frameBuffer=new Yn(this.gl,e,r),this.pingPongBuffer=new Yn(this.gl,e,r),this.shadowShader=bx(this.gl),this.diffuseShader=dx(this.gl),this.withoutShadowShader=_x(this.gl),this.blurShader=hx(this.gl,e,r,fe.isDiffuse)}render(){let t=this.rayHandler.lightRenderedLastFrame>0;if(!this.lightMapDrawingDisabled){if(this.frameBuffer.bindTexture(),this.rayHandler.shadows){let e=this.rayHandler.ambientLight,r=this.shadowShader;fe.isDiffuse?(r=this.diffuseShader,r.use(),this.rayHandler.diffuseBlendFunc.apply(),r.ambient.set(e.r,e.g,e.b,e.a)):(r.use(),this.rayHandler.shadowBlendFunc.apply(),r.ambient.set(e.r*e.a,e.g*e.a,e.b*e.a,1-e.a)),this.renderTriangleFan(r)}else t&&(this.rayHandler.simpleBlendFunc.apply(),this.withoutShadowShader.use(),this.renderTriangleFan(this.withoutShadowShader));this.gl.disable(this.gl.BLEND)}}renderTriangleFan(t){this.vertBuffer.bind(),t.a_position.enable(),t.a_position.set(2,this.gl.FLOAT,!1,0,0),this.vertBuffer.unbind(),this.uvBuffer.bind(),t.a_texCoord.enable(),t.a_texCoord.set(2,this.gl.FLOAT,!1,0,0),this.uvBuffer.unbind(),this.gl.drawArrays(this.gl.TRIANGLE_FAN,0,4),t.a_position.disable(),t.a_texCoord.disable()}gaussianBlur(){this.gl.disable(this.gl.BLEND);for(let t=0;tt-r&&this.y1e-r}updateAndRender(){this.update(),this.render()}update(){for(let t of this.lightList)t.update()}prepareRender(){this.lightRenderedLastFrame=0,this.gl.depthMask(!1),this.gl.enable(this.gl.BLEND),this.simpleBlendFunc.apply();let t=this.shadows||this.blur;t&&(this.lightMap.frameBuffer.begin(),this.gl.clearColor(0,0,0,0),this.gl.clear(this.gl.COLOR_BUFFER_BIT));let e=this.customLightShader??this.lightShader;e.use(),e.u_projTrans.set(!1,this.combined),this.customLightShader&&this.updateLightShader();for(let r of this.lightList)this.customLightShader&&this.updateLightShaderPerLight(r),r.render();t&&(this.customViewport?this.lightMap.frameBuffer.end(this.viewportX,this.viewportY,this.viewportWidth,this.viewportHeight):this.lightMap.frameBuffer.endSimple(),this.lightRenderedLastFrame>0&&this.blur&&this.lightMap.gaussianBlur())}render(){this.prepareRender(),this.lightMap.render()}renderOnly(){this.lightMap.render()}updateLightShader(){}updateLightShaderPerLight(t){}pointAtLight(t,e){return this.lightList.some(r=>r.contains(t,e))}pointAtShadow(t,e){return!this.lightList.some(r=>r.contains(t,e))}dispose(){this.removeAll(),this.lightMap&&this.lightMap.dispose(),this.lightShader&&this.lightShader.dispose()}removeAll(){this.lightList.forEach(t=>t.dispose()),this.lightList.length=0,this.disabledLights.forEach(t=>t.dispose()),this.disabledLights.length=0}removeLight(t){let e=t.isActive()?this.lightList:this.disabledLights;zn(e,t)}setLightShader(t){this.customLightShader=t}setCulling(t){this.culling=t}setBlur(t){this.blur=t}setBlurNum(t){this.blurNum=t}setShadows(t){this.shadows=t}setAmbientLightBrightness(t){this.ambientLight.a=Math.max(0,Math.min(1,t))}setAmbientLight(t,e,r,o){this.ambientLight.set(t,e,r,o)}setAmbientLightC(t){this.ambientLight.copy(t)}useCustomViewport(t,e,r,o){this.customViewport=!0,this.viewportX=t,this.viewportY=e,this.viewportWidth=r,this.viewportHeight=o}useDefaultViewport(){this.customViewport=!1}setLightMapRendering(t){this.lightMap.lightMapDrawingDisabled=!t}getLightMapBuffer(){return this.lightMap.frameBuffer}};function _o(i){i.setColor(Math.random(),Math.random(),Math.random(),1)}var yx=[{x1:0,x2:1.625,y1:-14.0615415625,y2:-11.9990415625},{x1:2.5,x2:4.75,y1:-11.1240415625,y2:-8.6865415625},{x1:5.75,x2:7.875,y1:-7.9365415625,y2:-6.0615415625},{x1:8.6875,x2:10.5625,y1:-5.3115415625,y2:-3.5615415624999986},{x1:11.1875,x2:12.625,y1:-2.4365415624999986,y2:-.24904156249999865},{x1:13.1875,x2:14,y1:1.2509584375000014,y2:3.6259584375000014},{x1:14.1875,x2:13.875,y1:4.813458437500001,y2:7.3759584375000005},{x1:13.25,x2:11.75,y1:8.6884584375,y2:10.4384584375},{x1:10.625,x2:8.8125,y1:11.3759584375,y2:11.8759584375},{x1:7.508101249999999,x2:5.239583124999999,y1:11.9990415625,y2:11.9527453125},{x1:4.54513875,x2:2.6932868749999983,y1:11.8138565625,y2:10.8879309375},{x1:1.9988424999999985,x2:.9340274999999991,y1:10.5638565625,y2:9.4064490625},{x1:.33217562499999964,x2:.008101249999999283,y1:8.6194121875,y2:6.9990415625},{x1:0,x2:-1.625,y1:-14.0615415625,y2:-11.9990415625},{x1:-2.5,x2:-4.75,y1:-11.1240415625,y2:-8.6865415625},{x1:-5.75,x2:-7.875,y1:-7.9365415625,y2:-6.0615415625},{x1:-8.6875,x2:-10.5625,y1:-5.3115415625,y2:-3.5615415624999986},{x1:-11.1875,x2:-12.625,y1:-2.4365415624999986,y2:-.24904156249999865},{x1:-13.1875,x2:-14,y1:1.2509584375000014,y2:3.6259584375000014},{x1:-14.1875,x2:-13.875,y1:4.813458437500001,y2:7.3759584375000005},{x1:-13.25,x2:-11.75,y1:8.6884584375,y2:10.4384584375},{x1:-10.625,x2:-8.8125,y1:11.3759584375,y2:11.8759584375},{x1:-7.508101249999999,x2:-5.239583124999999,y1:11.9990415625,y2:11.9527453125},{x1:-4.54513875,x2:-2.6932868749999983,y1:11.8138565625,y2:10.8879309375},{x1:-1.9988424999999985,x2:-.9340274999999991,y1:10.5638565625,y2:9.4064490625},{x1:-.33217562499999964,x2:-.008101249999999283,y1:8.6194121875,y2:6.9990415625}];var hu=class extends du{constructor(t,e,r,o,s,l){super(e,r,o,s,l),this.world=t}createRayCastCallback(t){return(e,r)=>this.world.RayCast(e,r,(o,s,l,a)=>t.reportFixture(o.GetFilterData(),o.GetBody(),s,a))}getBodyPosition(t){return t.GetPosition()}getBodyAngle(t){return t.GetAngle()}};var gs=class extends R{constructor(e){super({x:0,y:0});this.gl=e;this.drawDebugLight=!1;this.soft=!0;this.mode="Default";this.blendFunc=new zr(e,e.SRC_ALPHA,e.ONE_MINUS_SRC_ALPHA),fe.gammaCorrection=.625,fe.isDiffuse=!0;let r=this.getViewportSize();this.rayHandler=new hu(this.m_world,e,ot.getWidth()/4,ot.getHeight()/4,r.x,r.y),this.rayHandler.setAmbientLight(0,0,0,.5),this.rayHandler.setBlurNum(3)}setupControls(){this.addTestControlGroup("Light",this.getLightControls())}getLightControls(){return[rr("Blend Mode",["Default","Over-Burn","Some Other"],this.mode,e=>{this.setBlending(e)}),jt("Debug Light Shapes",this.drawDebugLight,e=>{this.drawDebugLight=e}),jt("Soft Shadows",this.soft,e=>{this.soft=e,this.setSoft(e)})]}Resize(e,r){this.rayHandler.resizeFBO(e/4,r/4)}Destroy(){super.Destroy(),this.rayHandler.dispose(),Gt.setGlobalContactFilter(null)}setBlending(e){this.mode=e,e==="Over-Burn"?this.rayHandler.diffuseBlendFunc.set(this.gl.DST_COLOR,this.gl.SRC_COLOR):e==="Some Other"?this.rayHandler.diffuseBlendFunc.set(this.gl.SRC_COLOR,this.gl.DST_COLOR):this.rayHandler.diffuseBlendFunc.reset()}Step(e,r){return super.Step(e,r),this.clearGlCanvas(),this.blendFunc.apply(),r}clearGlCanvas(){ds(this.gl,0,0,0,1)}renderLights(e,r){let o=this.getViewportSize();if(this.rayHandler.setCombinedMatrix(ot.combined,o.x/2,o.y/2,o.x,o.y),r>0&&this.rayHandler.update(),this.rayHandler.render(),this.drawDebugLight){let s=e.m_debugDraw,l=s.DrawPolygon.bind(s);for(let a of this.rayHandler.lightList)a.debugRender(l)}}};var mA=512,pA=32,xh=class extends gs{constructor({gl:e,shader:r,textures:o}){super(e);this.lights=[];this.currentEdgeFixture=null;this.currentEdgeBody=null;this.dragStart=new n;this.gl=e,this.shader=r,this.textures=o,this.mouseLight=this.createLight(),this.mouseLight.setColor(1,0,0,1);let s=this.m_world.CreateBody();for(let l of yx){let a=new I;a.SetTwoSided({x:l.x1,y:l.y1},{x:l.x2,y:l.y2}),s.CreateFixture({shape:a,density:0,restitution:0})}}getViewportSize(){return{x:ot.getWidth(),y:ot.getHeight()}}setSoft(e){this.mouseLight.setSoft(e);for(let r of this.lights)r.setSoft(e)}createLight(){let e=new xs(this.rayHandler,mA,Gt.DefaultColor,pA,0,0);return _o(e),e.setPositionV(this.m_mouseWorld),e.setSoft(this.soft),e}GetDefaultViewZoom(){return 30}getHotkeys(){return[G("a","Place current light",()=>{this.lights.push(this.mouseLight),this.mouseLight=this.createLight()})]}MouseDown(e){super.MouseDown(e),this.dragStart.Copy(e)}MouseUp(e){super.MouseUp(e),this.currentEdgeFixture=null,this.currentEdgeBody=null}MouseMove(e,r){if(super.MouseMove(e,r),r){if(!this.currentEdgeBody){let s=1/ot.getZoom();this.currentEdgeBody=this.m_world.CreateBody({position:{x:-s,y:s}})}this.currentEdgeFixture&&this.currentEdgeBody.DestroyFixture(this.currentEdgeFixture);let o=new I;o.SetTwoSided(this.dragStart,e),this.currentEdgeFixture=this.currentEdgeBody.CreateFixture({shape:o,density:0,restitution:0})}}Step(e,r){super.Step(e,r),this.addText("Left drag to draw lines"),this.mouseLight.setPositionV(this.m_mouseWorld);let o=ot.getCenter();return this.rayHandler.setCombinedMatrix(ot.combined,o.x,o.y,ot.getWidth(),ot.getHeight()),this.renderLights(e,r),r}clearGlCanvas(){ds(this.gl,1,1,1,1)}};P("Lights","Draw World",xh);function xx(i,t,e,r,o){let s=t+r,l=e+o;return i.set([t,l,t,e,s,l,s,l,t,e,s,e]),i}function gx(i,t,e,r,o){return i.set([o,r,o,t,e,r,e,r,o,t,e,t]),i}function Sx(i,t,e,r,o,s,l,a,c=1){let u=t+l,m=e+a,p=-l*c,d=-a*c,h=(r-l)*c,f=(o-a)*c,b=s*fs,x=Math.cos(b),_=Math.sin(b),y=x*p-_*f+u,g=_*p+x*f+m,S=x*p-_*d+u,w=_*p+x*d+m,B=x*h-_*f+u,C=_*h+x*f+m,A=S+(B-y),k=C-(g-w);return i.set([y,g,S,w,B,C,B,C,S,w,A,k]),i}var fu=new Float32Array(12),Qn=class{constructor(t,e,r){this.uvOffsetX=0;this.uvOffsetY=0;this.gl=t,this.shader=e,this.texture=r,this.vertBuffer=new si(t,t.DYNAMIC_DRAW,fu),this.uvBuffer=new si(t,t.STATIC_DRAW,gx(fu,0,1,1,0))}setUvOffset(t,e){this.uvOffsetX=t,this.uvOffsetY=e}destroy(){this.vertBuffer.dispose(),this.uvBuffer.dispose()}isDone(){return!0}setRect(t,e,r,o){this.vertBuffer.setData(xx(fu,t,e,r,o))}setRotatedRect(t,e,r,o,s,l,a,c=1){this.vertBuffer.setData(Sx(fu,t,e,r,o,s,l,a,c))}render(){this.vertBuffer.bind(),this.shader.position.enable(),this.shader.position.set(2,this.gl.FLOAT,!1,0,0),this.vertBuffer.unbind(),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.uvBuffer.bind(),this.shader.uv.enable(),this.shader.uv.set(2,this.gl.FLOAT,!1,0,0),this.uvBuffer.unbind(),this.shader.opacity.set(1),this.shader.uvOffset.set(this.uvOffsetX,this.uvOffsetY),this.shader.textureID.set(0),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}};var gh=i=>1<{let c=this.m_world.CreateBody({type:2,position:{x:4+Math.random()*40,y:4+Math.random()*25}});return c.CreateFixture(l).SetFilterData({categoryBits:Hr.MARBLE,maskBits:Sh.MARBLE}),new wh(new Qn(e,r,o.marble.texture),c)};this.marbles=Array.from({length:5},a),this.bg.setRect(0,0,Wn,Kn),Gt.setGlobalContactFilterBits(Hr.LIGHT,0,Sh.LIGHT),this.setLightsType(this.lightsType)}getLightControls(){return[rr("Lights Type",["Point","Cone","Chain","Directional"],this.lightsType,e=>{this.setLightsType(e)}),...super.getLightControls()]}getViewportSize(){return{x:Wn,y:Kn}}setSoft(e){this.directionalLight?.setSoft(e);for(let r of this.marbles)r.light?.setSoft(e)}GetDefaultViewZoom(){return 20}getCenter(){return{x:Wn/2,y:Kn/2}}Destroy(){this.bg.destroy();for(let e of this.marbles)e.light.dispose(),e.sprite.destroy();super.Destroy()}setLightsType(e){switch(e){case"Point":this.initPointLights();break;case"Cone":this.initConeLights();break;case"Chain":this.initChainLights();break;case"Directional":this.initDirectionalLight();break}this.lightsType=e}getHotkeys(){return[G("c","Random Light Colors",()=>{for(let e of this.marbles)_o(e.light)}),G("d","Random Light Distance",()=>{for(let e of this.marbles)e.light.setDistance($n(qn*.5,qn*2))})]}Step(e,r){super.Step(e,r),this.bg.render();for(let o of this.marbles)o.render();return this.directionalLight&&(this.sunDirection+=r*.008,this.directionalLight.setDirection(this.sunDirection)),this.renderLights(e,r),r}createBoundary(){let e=new dt;e.CreateLoop([new n,new n(0,Kn),new n(Wn,Kn),new n(Wn,0)]),this.groundBody=this.m_world.CreateBody({type:0}),this.groundBody.CreateFixture({shape:e,density:0}).SetFilterData({categoryBits:Hr.WORLD,maskBits:Sh.WORLD})}clearLights(){this.directionalLight&&(this.directionalLight.remove(),this.directionalLight=null);for(let e of this.marbles)e.light?.remove()}initPointLights(){this.clearLights();for(let e of this.marbles){let r=new xs(this.rayHandler,bu,Gt.DefaultColor,qn,0,0);r.attachToBody(e.body,Gi/2,Gi/2),_o(r),e.light=r,r.setSoft(this.soft)}}initConeLights(){this.clearLights();for(let e of this.marbles){let r=new uu(this.rayHandler,bu,Gt.DefaultColor,qn,0,0,0,$n(15,40));r.attachToBody(e.body,Gi/2,Gi/2,$n(0,360)),_o(r),e.light=r,r.setSoft(this.soft)}}initChainLights(){this.clearLights();for(let e of this.marbles){let r=new cu(this.rayHandler,bu,Gt.DefaultColor,qn,1,[-5,0,0,3,5,0]);r.attachToBody(e.body,$n(0,360)),_o(r),e.light=r,r.setSoft(this.soft)}}initDirectionalLight(){this.clearLights(),this.sunDirection=$n(0,360),this.directionalLight=new mu(this.rayHandler,4*bu,Gt.DefaultColor,this.sunDirection),this.directionalLight.setSoftnessLength(5),this.directionalLight.setSoft(this.soft)}};P("Lights","Official Demo",vh);var Bh=class extends R{constructor(){super(),this.m_bodies=[];let t=new hs;this.m_controller=t,t.normal.Set(0,1),t.offset=20,t.density=2,t.linearDrag=5,t.angularDrag=2;let e=this.m_world.CreateBody();{let r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r}),r.SetTwoSided(new n(-40,0),new n(-40,25)),e.CreateFixture({shape:r}),r.SetTwoSided(new n(40,0),new n(40,25)),e.CreateFixture({shape:r})}for(let r=0;r<5;r++){let o=this.m_world.CreateBody({type:2,position:{x:Math.random()*40-20,y:Math.random()*15+5},angle:Math.random()*Math.PI}),s=new v;s.SetAsBox(Math.random()*.5+1,Math.random()*.5+1),o.CreateFixture({density:1,friction:.3,restitution:.1,shape:s}),this.m_bodies.push(o)}for(let r=0;r<5;r++){let o=this.m_world.CreateBody({type:2,position:{x:Math.random()*40-20,y:Math.random()*15+5},angle:Math.random()*Math.PI});o.CreateFixture({density:1,friction:.3,restitution:.1,shape:new V(Math.random()*.5+1)}),this.m_bodies.push(o)}for(let r=0;r<15;r++){let o=this.m_world.CreateBody({type:2,position:{x:Math.random()*40-20,y:Math.random()*15+5},angle:Math.random()*Math.PI}),s=new v;if(Math.random()>.66)s.Set([new n(-1-Math.random()*1,1+Math.random()*1),new n(-.5-Math.random()*1,-1-Math.random()*1),new n(.5+Math.random()*1,-1-Math.random()*1),new n(1+Math.random()*1,1+Math.random()*1)]);else if(Math.random()>.5){let l=[];l[0]=new n(0,1+Math.random()*1),l[2]=new n(-.5-Math.random()*1,-1-Math.random()*1),l[3]=new n(.5+Math.random()*1,-1-Math.random()*1),l[1]=new n(l[0].x+l[2].x,l[0].y+l[2].y),l[1].Scale(Math.random()/2+.8),l[4]=new n(l[3].x+l[0].x,l[3].y+l[0].y),l[4].Scale(Math.random()/2+.8),s.Set(l)}else s.Set([new n(0,1+Math.random()*1),new n(-.5-Math.random()*1,-1-Math.random()*1),new n(.5+Math.random()*1,-1-Math.random()*1)]);o.CreateFixture({density:1,friction:.3,restitution:.1,shape:s}),this.m_bodies.push(o)}{let r=this.m_world.CreateBody({type:2,position:{x:0,y:40},angle:0}),o=new v;o.SetAsBox(4,1),r.CreateFixture({density:3,shape:o}),this.m_bodies.push(r)}{let r=this.m_world.CreateBody({type:2,position:{x:0,y:30}}),o=new V(.7),s={density:2,shape:o};o.m_p.Set(3,0),r.CreateFixture(s),o.m_p.Set(-3,0),r.CreateFixture(s),o.m_p.Set(0,3),r.CreateFixture(s),o.m_p.Set(0,-3),r.CreateFixture(s),s.density=2;let l=new v;s.shape=l,l.SetAsBox(3,.2),r.CreateFixture(s),l.SetAsBox(.2,3),r.CreateFixture(s),this.m_bodies.push(r)}for(let r of this.m_bodies)this.m_controller.AddBody(r);this.m_world.AddController(this.m_controller)}getCenter(){return{x:0,y:10}}};P("Controllers","Boyancy Test",Bh);var _u=class{constructor(){this.gainP=1;this.gainI=1;this.gainD=1;this.currentError=0;this.previousError=0;this.integral=0;this.output=0}step(t){this.integral=t*(this.integral+this.currentError);let e=1/t*(this.currentError-this.previousError);this.output=this.gainP*this.currentError+this.gainI*this.integral+this.gainD*e,this.previousError=this.currentError}},Ch=class i extends R{constructor(){super({x:0,y:-30});this.targetPosition=10;this.targetPositionInterval=0;this.posAvg=0;this.angleController=new _u;this.positionController=new _u;this.angleController.gainP=1e3,this.angleController.gainI=0,this.angleController.gainD=250,this.positionController.gainP=.5,this.positionController.gainI=0,this.positionController.gainD=1.5,this.pendulumBody=this.m_world.CreateBody({type:2,position:{x:0,y:2+.5*i.PENDULUM_LENGTH}});let e=new v;e.SetAsBox(.5,.5*i.PENDULUM_LENGTH);let r={shape:e,density:1/(1*i.PENDULUM_LENGTH)};this.pendulumBody.CreateFixture(r),this.wheelBody=this.m_world.CreateBody({type:2,position:n.UNITY});let o=new V;o.m_radius=.6,r.shape=o,r.density=1/(Math.PI*.6*.6),r.friction=10,this.wheelBody.CreateFixture(r);let s=new q;s.Initialize(this.wheelBody,this.pendulumBody,{x:0,y:0}),s.localAnchorA.Set(0,0),s.localAnchorB.Set(0,-.5*i.PENDULUM_LENGTH),s.collideConnected=!1,s.enableMotor=!0,s.maxMotorTorque=40,this.wheelJoint=this.m_world.CreateJoint(s),this.groundBody=this.m_world.CreateBody({type:0,position:n.ZERO});let l=new I;l.SetTwoSided({x:-100,y:0},{x:100,y:0}),r.shape=l,r.friction=10,this.groundBody.CreateFixture(r)}static{this.PENDULUM_LENGTH=10}getCenter(){return{x:0,y:5}}Step(e,r){let o=e.m_hertz>0?1/e.m_hertz:0;e.m_pause&&!e.m_singleStep&&(o=0),super.Step(e,r),this.targetPositionInterval+=o,this.targetPositionInterval>=8&&(this.targetPositionInterval=0,this.targetPosition=this.targetPosition===10?-10:10);let s=0;{this.posAvg=(1-.4)*this.posAvg+.4*this.pendulumBody.GetPosition().x,this.positionController.currentError=this.targetPosition-this.posAvg,this.positionController.step(o);let m=this.positionController.output;m=tt(m,-10,10),s=m/this.m_world.GetGravity().y,s=tt(s,pt(-15),pt(15))}let l=this.pendulumBody.GetAngle();l=dA(l),this.angleController.currentError=s-l,this.angleController.step(o);let a=this.angleController.output;Math.abs(a)>1e3&&(a=0);let c=a/(2*Math.PI*.6);this.wheelJoint.SetMotorSpeed(c)}};function dA(i){for(;i>pt(180);)i-=pt(360);for(;ithis.ApplyForce(-50)),Vr("s","Apply Backward Force",()=>this.ApplyForce(50)),Vr("a","Apply Torque Counter-Clockwise",()=>this.m_body.ApplyTorque(10)),Vr("d","Apply Torque Clockwise",()=>this.m_body.ApplyTorque(-10))]}ApplyForce(e){let r=this.m_body.GetWorldVector(new n(0,e),new n),o=this.m_body.GetWorldPoint(new n(0,3),new n);this.m_body.ApplyForce(r,o)}getCenter(){return{x:0,y:15}}};P("Forces","Apply Force",Th);var Ph=class extends R{constructor(){super();let t=this.m_world.CreateBody({position:{x:0,y:17}});{let e=t;{let r=new v;r.SetAsBox(4,1);let o=this.m_world.CreateBody({type:2,position:{x:-8,y:20}});o.CreateFixture({shape:r,density:2});let s=new q;s.Initialize(e,o,new n(-12,20)),this.m_world.CreateJoint(s),e=o}{let r=new v;r.SetAsBox(8,1);let o=this.m_world.CreateBody({type:2,position:{x:4,y:20}});o.CreateFixture({shape:r,density:2});let s=new q;s.Initialize(e,o,new n(-4,20)),this.m_world.CreateJoint(s),e=o}{let r=new v;r.SetAsBox(3,3);let o=this.m_world.CreateBody({type:2,fixedRotation:!0,position:{x:12,y:20}});o.CreateFixture({shape:r,density:2});let s=new q;s.Initialize(e,o,new n(12,20)),this.m_world.CreateJoint(s);let l=new Zt;l.Initialize(t,o,new n(12,17),new n(1,0)),this.m_world.CreateJoint(l)}}}getCenter(){return{x:0,y:15}}};P("Examples","Slider Crank 1",Ph);var Dh=class extends R{constructor(){super();this.m_speed=0;let e=this.m_world.CreateBody();{let r=new I;r.SetTwoSided(new n(-20,0),new n(20,0)),e.CreateFixture({shape:r})}{this.m_attachment=this.m_world.CreateBody({type:2,position:{x:0,y:3}});let r=new v;r.SetAsBox(.5,2),this.m_attachment.CreateFixture({shape:r,density:2})}{this.m_platform=this.m_world.CreateBody({type:2,position:{x:-4,y:5}});let r=new v;r.SetAsBox(.5,4,new n(4,0),.5*Math.PI),this.m_platform.CreateFixture({shape:r,friction:.6,density:2});let o=new q;o.Initialize(this.m_attachment,this.m_platform,new n(0,5)),o.maxMotorTorque=50,o.enableMotor=!0,this.m_world.CreateJoint(o);let s=new Zt;s.Initialize(e,this.m_platform,new n(0,5),new n(1,0)),s.maxMotorForce=1e3,s.enableMotor=!0,s.lowerTranslation=-10,s.upperTranslation=10,s.enableLimit=!0,this.m_world.CreateJoint(s),this.m_speed=3}{let r=this.m_world.CreateBody({type:2,position:{x:0,y:8}}),o=new v;o.SetAsBox(.75,.75),r.CreateFixture({shape:o,friction:.6,density:2})}}getHotkeys(){return[G("d","Set Dynamic Body",()=>this.m_platform.SetType(2)),G("s","Set Static Body",()=>this.m_platform.SetType(0)),G("k","Set Kinematic Body",()=>{this.m_platform.SetType(1),this.m_platform.SetLinearVelocity(new n(-this.m_speed,0)),this.m_platform.SetAngularVelocity(0)})]}Step(e,r){if(this.m_platform.GetType()===1){let{p:o}=this.m_platform.GetTransform(),s=this.m_platform.GetLinearVelocity();(o.x<-10&&s.x<0||o.x>10&&s.x>0)&&this.m_platform.SetLinearVelocity(new n(-s.x,s.y))}super.Step(e,r)}};P("Examples","Body Types",Dh);var Mh=class extends R{constructor(){super();this.m_velocity=new n;this.m_angularVelocity=0;this.m_shape1=new v;this.m_shape2=new v;this.m_broke=!1;this.m_break=!1;{let e=this.m_world.CreateBody(),r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r})}this.m_body1=this.m_world.CreateBody({type:2,position:{x:0,y:40},angle:.25*Math.PI}),this.m_shape1=new v,this.m_shape1.SetAsBox(.5,.5,new n(-.5,0),0),this.m_piece1=this.m_body1.CreateFixture({shape:this.m_shape1,density:1}),this.m_shape2=new v,this.m_shape2.SetAsBox(.5,.5,new n(.5,0),0),this.m_piece2=this.m_body1.CreateFixture({shape:this.m_shape2,density:1})}static{this.e_count=7}PostSolve(e,r){if(this.m_broke)return;let o=e.GetManifold().pointCount,s=0;for(let l=0;l40&&(this.m_break=!0)}Break(){let e=this.m_piece1.GetBody(),r=e.GetWorldCenter();e.DestroyFixture(this.m_piece2);let o=this.m_world.CreateBody({type:2,position:e.GetPosition(),angle:e.GetAngle()});this.m_piece2=o.CreateFixture({shape:this.m_shape2,density:1});let s=e.GetWorldCenter(),l=o.GetWorldCenter(),a=n.AddCrossScalarVec2(this.m_velocity,this.m_angularVelocity,n.Subtract(s,r,n.s_t0),new n),c=n.AddCrossScalarVec2(this.m_velocity,this.m_angularVelocity,n.Subtract(l,r,n.s_t0),new n);e.SetAngularVelocity(this.m_angularVelocity),e.SetLinearVelocity(a),o.SetAngularVelocity(this.m_angularVelocity),o.SetLinearVelocity(c)}Step(e,r){this.m_break&&(this.Break(),this.m_broke=!0,this.m_break=!1),this.m_broke||(this.m_velocity.Copy(this.m_body1.GetLinearVelocity()),this.m_angularVelocity=this.m_body1.GetAngularVelocity()),super.Step(e,r)}};P("Examples","Breakable",Mh);var Rh=class i extends R{static{this.e_count=30}constructor(){super();let t=null;{t=this.m_world.CreateBody();let e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e})}{let e=new v;e.SetAsBox(.5,.125);let r={shape:e,density:20,friction:.2},o=new q,s=t;for(let a=0;a>1&&(this.m_middle=c),s=c}let l=new n(-15+1*i.e_count,5);o.Initialize(s,t,l),this.m_world.CreateJoint(o)}for(let e=0;e<2;++e){let r=[];r[0]=new n(-.5,0),r[1]=new n(.5,0),r[2]=new n(0,1.5);let o=new v;o.Set(r),this.m_world.CreateBody({type:2,position:{x:-8+8*e,y:12}}).CreateFixture({shape:o,density:1})}for(let e=0;e<3;++e){let r=new V;r.m_radius=.5,this.m_world.CreateBody({type:2,position:{x:-6+6*e,y:10}}).CreateFixture({shape:r,density:1})}}};P("Joints","Bridge",Rh);function vx(i,t,e){return`${i.toFixed(0)} [${t.toFixed(1)}] (${e.toFixed(0)})`}var kh=class extends R{constructor(){super();this.m_x=0;{let e=this.m_world.CreateBody(),r=new I;r.SetTwoSided(new n(-10,0),new n(10,0)),e.CreateFixture({shape:r});let o=new v;o.SetAsBox(.2,1,new n(.5,1),0),e.CreateFixture({shape:o})}{let e=new v;e.SetAsBox(2,.1),this.m_body=this.m_world.CreateBody({type:2,position:{x:0,y:4}}),this.m_body.CreateFixture({shape:e,density:1}),e.SetAsBox(.25,.25),this.m_x=.20352793,this.m_bullet=this.m_world.CreateBody({type:2,bullet:!0,position:{x:this.m_x,y:10}}),this.m_bullet.CreateFixture({shape:e,density:100}),this.m_bullet.SetLinearVelocity(new n(0,-50))}}Launch(){this.m_body.SetTransformVec(new n(0,4),0),this.m_body.SetLinearVelocity(n.ZERO),this.m_body.SetAngularVelocity(0),this.m_x=W(-1,1),this.m_bullet.SetTransformVec(new n(this.m_x,10),0),this.m_bullet.SetLinearVelocity(new n(0,-50)),this.m_bullet.SetAngularVelocity(0),$t.reset(),rt.reset()}GetDefaultViewZoom(){return 50}Step(e,r){super.Step(e,r),this.addDebug("GJK Calls [ave Iters] (max Iters)",$t.calls>0&&vx($t.calls,$t.iters/$t.calls,$t.maxIters)),this.addDebug("Toi Calls [ave Iters] (max Iters)",rt.calls>0&&vx(rt.calls,rt.iters/rt.calls,rt.maxIters)),this.addDebug("Root Toi [ave Iters] (max Iters)",rt.calls>0&&`[${(rt.rootIters/rt.calls).toFixed(1)}] (${rt.maxRootIters})`),this.m_stepCount%60===0&&this.Launch()}};P("Continuous","Bullet Test",kh);var Fh=class i extends R{static{this.e_count=8}constructor(){super();let t=null;{t=this.m_world.CreateBody();let e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e})}{let e=new v;e.SetAsBox(.5,.125);let r={shape:e,density:20},o=new po,s=t;for(let l=0;l0){let c=new n(-5+1*l,5);o.Initialize(s,a,c),this.m_world.CreateJoint(o)}s=a}}{let e=new v;e.SetAsBox(.5,.125);let r={shape:e,density:20},o=new po,s=8,l=.7,a=t;for(let c=0;c0){let m=new n(5+1*c,10);o.Initialize(a,u,m),ld(o,s,l,o.bodyA,o.bodyB),this.m_world.CreateJoint(o)}a=u}}for(let e=0;e<2;++e){let r=[];r[0]=new n(-.5,0),r[1]=new n(.5,0),r[2]=new n(0,1.5);let o=new v;o.Set(r),this.m_world.CreateBody({type:2,position:{x:-8+8*e,y:12}}).CreateFixture({shape:o,density:1})}for(let e=0;e<2;++e){let r=new V;r.m_radius=.5,this.m_world.CreateBody({type:2,position:{x:-6+6*e,y:10}}).CreateFixture({shape:r,density:1})}}};P("Joints","Cantilever",Fh);var Ih=class extends R{constructor(){super();this.m_speed=0;this.m_speed=50;let e;{e=this.m_world.CreateBody();let r=new I,o={shape:r,density:0,friction:.6};r.SetTwoSided(new n(-20,0),new n(20,0)),e.CreateFixture(o);let s=[.25,1,4,0,0,-1,-2,-2,-1.25,0],l=20,a=0,c=5;for(let u=0;u<10;++u){let m=s[u];r.SetTwoSided(new n(l,a),new n(l+c,m)),e.CreateFixture(o),a=m,l+=c}for(let u=0;u<10;++u){let m=s[u];r.SetTwoSided(new n(l,a),new n(l+c,m)),e.CreateFixture(o),a=m,l+=c}r.SetTwoSided(new n(l,0),new n(l+40,0)),e.CreateFixture(o),l+=80,r.SetTwoSided(new n(l,0),new n(l+40,0)),e.CreateFixture(o),l+=40,r.SetTwoSided(new n(l,0),new n(l+10,5)),e.CreateFixture(o),l+=20,r.SetTwoSided(new n(l,0),new n(l+40,0)),e.CreateFixture(o),l+=40,r.SetTwoSided(new n(l,0),new n(l,20)),e.CreateFixture(o)}{let r=this.m_world.CreateBody({type:2,position:{x:140,y:1}}),o=new v;o.SetAsBox(10,.25),r.CreateFixture({shape:o,density:1});let s=new q;s.Initialize(e,r,r.GetPosition()),s.lowerAngle=-8*Math.PI/180,s.upperAngle=8*Math.PI/180,s.enableLimit=!0,this.m_world.CreateJoint(s),r.ApplyAngularImpulse(100)}{let o=new v;o.SetAsBox(1,.125);let s={shape:o,density:1,friction:.6},l=new q,a=e;for(let u=0;u<20;++u){let m=this.m_world.CreateBody({type:2,position:{x:161+2*u,y:-.125}});m.CreateFixture(s);let p=new n(160+2*u,-.125);l.Initialize(a,m,p),this.m_world.CreateJoint(l),a=m}let c=new n(160+2*20,-.125);l.Initialize(a,e,c),this.m_world.CreateJoint(l)}{let r=new v;r.SetAsBox(.5,.5);let o,s=new n,l={type:2,position:s};s.Set(230,.5),o=this.m_world.CreateBody(l),o.CreateFixture({shape:r,density:.5}),s.Set(230,1.5),o=this.m_world.CreateBody(l),o.CreateFixture({shape:r,density:.5}),s.Set(230,2.5),o=this.m_world.CreateBody(l),o.CreateFixture({shape:r,density:.5}),s.Set(230,3.5),o=this.m_world.CreateBody(l),o.CreateFixture({shape:r,density:.5}),s.Set(230,4.5),o=this.m_world.CreateBody(l),o.CreateFixture({shape:r,density:.5})}{let r=new v,o=H(8,n);o[0].Set(-1.5,-.5),o[1].Set(1.5,-.5),o[2].Set(1.5,0),o[3].Set(0,.9),o[4].Set(-1.15,.9),o[5].Set(-1.5,.2),r.Set(o,6);let s=new V;s.m_radius=.4;let l=new n,a={type:2,position:l};l.Set(0,1),this.m_car=this.m_world.CreateBody(a),this.m_car.CreateFixture({shape:r,density:1});let c={shape:s,density:1,friction:.9};l.Set(-1,.35),this.m_wheel1=this.m_world.CreateBody(a),this.m_wheel1.CreateFixture(c),l.Set(1,.4),this.m_wheel2=this.m_world.CreateBody(a),this.m_wheel2.CreateFixture(c);let u=new cs,m=new n(0,1),p=this.m_wheel1.GetMass(),d=this.m_wheel2.GetMass(),h=4,f=.7,b=2*Math.PI*h;u.Initialize(this.m_car,this.m_wheel1,this.m_wheel1.GetPosition(),m),u.motorSpeed=0,u.maxMotorTorque=20,u.enableMotor=!0,u.stiffness=p*b*b,u.damping=2*p*f*b,u.lowerTranslation=-.25,u.upperTranslation=.25,u.enableLimit=!0,this.m_spring1=this.m_world.CreateJoint(u),u.Initialize(this.m_car,this.m_wheel2,this.m_wheel2.GetPosition(),m),u.motorSpeed=0,u.maxMotorTorque=10,u.enableMotor=!1,u.stiffness=d*b*b,u.damping=2*d*f*b,u.lowerTranslation=-.25,u.upperTranslation=.25,u.enableLimit=!0,this.m_spring2=this.m_world.CreateJoint(u)}}getHotkeys(){return[Gr("a","Decelerate",e=>this.m_spring1.SetMotorSpeed(e?this.m_speed:0)),Gr("d","Accelerate",e=>this.m_spring1.SetMotorSpeed(e?-this.m_speed:0))]}Step(e,r){let o=ot.getCenter();ot.setPosition(this.m_car.GetPosition().x,o.y),super.Step(e,r)}};P("Examples","Car",Ih);var Lh=class extends R{constructor(){super();this.m_angularVelocity=0;{let e=this.m_world.CreateBody(),r=new I;r.SetTwoSided(new n(-10,0),new n(10,0)),e.CreateFixture({shape:r});let o=new v;o.SetAsBox(.2,1,new n(.5,1),0),e.CreateFixture({shape:o})}{let e=new v;e.SetAsBox(2,.1),this.m_body=this.m_world.CreateBody({type:2,position:{x:0,y:20}}),this.m_body.CreateFixture({shape:e,density:1}),this.m_angularVelocity=W(-50,50),this.m_body.SetLinearVelocity(new n(0,-100)),this.m_body.SetAngularVelocity(this.m_angularVelocity)}$t.reset(),rt.reset()}Launch(){$t.reset(),rt.reset(),this.m_body.SetTransformVec(new n(0,20),0),this.m_angularVelocity=W(-50,50),this.m_body.SetLinearVelocity(new n(0,-100)),this.m_body.SetAngularVelocity(this.m_angularVelocity)}Step(e,r){super.Step(e,r),this.addDebug("GJK Calls [ave Iters] (max Iters)",$t.calls>0&&`${$t.calls.toFixed(0)} [${($t.iters/$t.calls).toFixed(1)}] (${$t.maxIters.toFixed(0)})`),this.addDebug("Toi Calls [ave Iters] (max Iters)",rt.calls>0&&`${rt.calls} [${(rt.iters/rt.calls).toFixed(1)}] (${rt.maxIters})`),this.addDebug("Toi Root [ave Iters] (max Iters)",rt.calls>0&&`${rt.calls} [${(rt.rootIters/rt.calls).toFixed(1)}] (${rt.maxRootIters})`),this.addDebug("Toi Time in ms [ave] (max)",rt.calls>0&&`[${(1e3*rt.time/rt.calls).toFixed(1)}] (${(1e3*rt.maxTime).toFixed(1)})`),this.m_stepCount%60===0&&this.Launch()}GetDefaultViewZoom(){return 50}};P("Continuous","Continuous Test",Lh);var hA=!1,Gh=class extends R{constructor(){super();let t=null;{t=this.m_world.CreateBody();let e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e})}{let e=new v;e.SetAsBox(.6,.125);let r={shape:e,density:20,friction:.2},o=new q;o.collideConnected=!1;let s=25,l=t;for(let a=0;a<30;++a){let c=this.m_world.CreateBody({type:2,position:{x:.5+a,y:s}});hA&&(a===10?r.density=0:r.density=20),c.CreateFixture(r);let u=new n(a,s);o.Initialize(l,c,u),this.m_world.CreateJoint(o),l=c}}}};P("Joints","Chain",Gh);var Vh=class extends R{constructor(){super();{let t=this.m_world.CreateBody(),e=new I;e.SetTwoSided(new n(-20,0),new n(20,0)),t.CreateFixture({shape:e})}{let t=this.m_world.CreateBody(),e=new I;e.SetTwoSided(new n(-8,1),new n(-6,1)),t.CreateFixture({shape:e}),e.SetTwoSided(new n(-6,1),new n(-4,1)),t.CreateFixture({shape:e}),e.SetTwoSided(new n(-4,1),new n(-2,1)),t.CreateFixture({shape:e})}{let t=this.m_world.CreateBody({angle:.25*Math.PI}),e=H(4,n);e[0].Set(5,7),e[1].Set(8,7),e[2].Set(7,8),e[3].Set(6,8);let r=new dt;r.CreateLoop(e,4),t.CreateFixture({shape:r})}{let t=this.m_world.CreateBody(),e=new v;e.SetAsBox(1,1,new n(4,3),0),t.CreateFixture({shape:e}),e.SetAsBox(1,1,new n(6,3),0),t.CreateFixture({shape:e}),e.SetAsBox(1,1,new n(8,3),0),t.CreateFixture({shape:e})}{let t=this.m_world.CreateBody(),e=H(4,n);e[0].Set(-1,3),e[1].Set(1,3),e[2].Set(1,5),e[3].Set(-1,5);let r=new dt;r.CreateLoop(e,4),t.CreateFixture({shape:r})}{let t=this.m_world.CreateBody({position:{x:-10,y:4}}),e=H(10,n);e[0].Set(0,0),e[1].Set(6,0),e[2].Set(6,2),e[3].Set(4,1),e[4].Set(2,2),e[5].Set(0,2),e[6].Set(-2,2),e[7].Set(-4,3),e[8].Set(-6,2),e[9].Set(-6,0);let r=new dt;r.CreateLoop(e,10),t.CreateFixture({shape:r})}{let t=this.m_world.CreateBody({position:{x:-3,y:8},type:2,fixedRotation:!0,allowSleep:!1}),e=new v;e.SetAsBox(.5,.5),t.CreateFixture({shape:e,density:20})}{let t=this.m_world.CreateBody({position:{x:-5,y:5},type:2,fixedRotation:!0,allowSleep:!1}),e=new v;e.SetAsBox(.25,.25),t.CreateFixture({shape:e,density:20})}{let t=this.m_world.CreateBody({position:{x:-5,y:8},type:2,fixedRotation:!0,allowSleep:!1}),e=0,r=Math.PI/3,o=H(6,n);for(let l=0;l<6;++l)o[l].Set(.5*Math.cos(e),.5*Math.sin(e)),e+=r;let s=new v;s.Set(o,6),t.CreateFixture({shape:s,density:20})}{let t=this.m_world.CreateBody({position:{x:3,y:5},type:2,fixedRotation:!0,allowSleep:!1}),e=new V;e.m_radius=.5,t.CreateFixture({shape:e,density:20})}{this.m_character=this.m_world.CreateBody({position:{x:-7,y:6},type:2,allowSleep:!1});let t=new V;t.m_radius=.25,this.m_character.CreateFixture({shape:t,density:20,friction:1})}}GetDefaultViewZoom(){return 30}getCenter(){return{x:-2,y:0}}Step(t,e){let r=this.m_character.GetLinearVelocity();this.m_character.SetLinearVelocity({x:-5,y:r.y}),super.Step(t,e),this.addText("This tests various character collision shapes"),this.addText("Limitation: square and hexagon can snag on aligned boxes."),this.addText("Feature: edge chains have smooth collision inside and out.")}};P("Examples","Character Collision",Vh);var Eh=class i extends R{static{this.k_smallGroup=1}static{this.k_largeGroup=-1}static{this.k_triangleCategory=2}static{this.k_boxCategory=4}static{this.k_circleCategory=8}static{this.k_triangleMask=65535}static{this.k_boxMask=65535^i.k_triangleCategory}static{this.k_circleMask=65535}constructor(){super();{let x=new I;x.SetTwoSided(new n(-40,0),new n(40,0)),this.m_world.CreateBody().CreateFixture({shape:x,friction:.3})}let t=[];t[0]=new n(-1,0),t[1]=new n(1,0),t[2]=new n(0,2);let e=new v;e.Set(t,3);let r={groupIndex:i.k_smallGroup,categoryBits:i.k_triangleCategory,maskBits:i.k_triangleMask},o={shape:e,density:1,filter:r};this.m_world.CreateBody({type:2,position:{x:-5,y:2}}).CreateFixture(o),t[0].Scale(2),t[1].Scale(2),t[2].Scale(2),e.Set(t,3),r.groupIndex=i.k_largeGroup;let l=this.m_world.CreateBody({type:2,fixedRotation:!0,position:{x:-5,y:6}});l.CreateFixture(o);{let x=this.m_world.CreateBody({type:2,position:{x:-5,y:10}}),_=new v;_.SetAsBox(.5,1),x.CreateFixture({shape:_,density:1});let y=new Zt;y.bodyA=l,y.bodyB=x,y.enableLimit=!0,y.localAnchorA.Set(0,4),y.localAnchorB.SetZero(),y.localAxisA.Set(0,1),y.lowerTranslation=-1,y.upperTranslation=1,this.m_world.CreateJoint(y)}e.SetAsBox(1,.5);let a={groupIndex:i.k_smallGroup,categoryBits:i.k_boxCategory,maskBits:i.k_boxMask},c={shape:e,density:1,restitution:.1,filter:a};this.m_world.CreateBody({type:2,position:{x:0,y:2}}).CreateFixture(c),e.SetAsBox(2,1),a.groupIndex=i.k_largeGroup,this.m_world.CreateBody({type:2,position:{x:0,y:6}}).CreateFixture(c);let p=new V;p.m_radius=1;let d={groupIndex:i.k_smallGroup,categoryBits:i.k_circleCategory,maskBits:i.k_circleMask},h={shape:p,density:1,filter:d};this.m_world.CreateBody({type:2,position:{x:5,y:2}}).CreateFixture(h),p.m_radius*=2,d.groupIndex=i.k_largeGroup,this.m_world.CreateBody({type:2,position:{x:5,y:6}}).CreateFixture(h)}};P("Examples","Collision Filtering",Eh);var Uh=class extends R{constructor(){super();{let _=new I;_.SetTwoSided(new n(-50,0),new n(50,0)),this.m_world.CreateBody().CreateFixture({shape:_})}let t=-5,e=5,r=2,o=35,s=[new n(-1,0),new n(1,0),new n(0,2)],l=new v;l.Set(s,3);let a={shape:l,density:1};this.m_world.CreateBody({type:2,position:{x:W(t,e),y:W(r,o)}}).CreateFixture(a),s[0].Scale(2),s[1].Scale(2),s[2].Scale(2),l.Set(s,3),this.m_world.CreateBody({type:2,position:{x:W(t,e),y:W(r,o)}}).CreateFixture(a),l.SetAsBox(1,.5);let m={shape:l,density:1};this.m_world.CreateBody({type:2,position:{x:W(t,e),y:W(r,o)}}).CreateFixture(m),l.SetAsBox(2,1),this.m_world.CreateBody({type:2,position:{x:W(t,e),y:W(r,o)}}).CreateFixture(m);let h=new V;h.m_radius=1;let f={shape:h,density:1};this.m_world.CreateBody({type:2,position:{x:W(t,e),y:W(r,o)}}).CreateFixture(f),h.m_radius*=2,this.m_world.CreateBody({type:2,position:{x:W(t,e),y:W(r,o)}}).CreateFixture(f)}Step(t,e){super.Step(t,e);let r=6,o=new Array(r),s=0;for(let l=0;l0&&p>0&&(p>m?o[s++]=c:o[s++]=u,s===r))break}for(let l=0;l{this.Spawn()})]}getCenter(){return{x:0,y:5}}};P("Examples","Compound Shapes",Yh);var Jh=class i extends R{static{this.e_columnCount=0}static{this.e_rowCount=0}constructor(){super(n.ZERO);{let o=this.m_world.CreateBody(),s=new I;s.SetTwoSided(new n(-10,0),new n(10,0)),o.CreateFixture({shape:s}),s.SetTwoSided(new n(-10,0),new n(-10,20)),o.CreateFixture({shape:s}),s.SetTwoSided(new n(10,0),new n(10,20)),o.CreateFixture({shape:s}),s.SetTwoSided(new n(-10,20),new n(10,20)),o.CreateFixture({shape:s})}let t=.5,e=new V;e.m_p.SetZero(),e.m_radius=t;let r={shape:e,density:1,friction:.1};for(let o=0;othis.CreateCircle())]}Step(t,e){for(let r=this.m_world.GetBodyList();r;r=r.GetNext())r.GetType(),2;this.m_stepCount===180&&(this.m_stepCount+=0),super.Step(t,e);for(let r=this.m_world.GetBodyList();r;r=r.GetNext())r.GetType(),2}};P("Solver","Confined",Jh);var jh=class i extends R{constructor(){super();this.m_test_points=[];this.m_count=0;this.m_generation=0;this.m_bulk=!1;this.m_auto=!1;this.Generate()}static{this.e_count=Kt}GetDefaultViewZoom(){return 50}Generate(){let e=Math.PI*vn(),r=new D(e);for(let o=0;o{this.m_auto=!this.m_auto}),G("b","Toggle Bulk",()=>{this.m_bulk=!this.m_bulk}),G("g","Generate a new random convex hull",()=>this.Generate())]}Step(e,r){super.Step(e,r);let o,s=!1;if(this.m_bulk){for(let a=0;a<1e4;++a)if(this.Generate(),o=An(this.m_test_points,this.m_count),o.length!==0&&(s=Tn(o,o.length),s===!1)){this.m_bulk=!1;break}}else this.m_auto&&this.Generate(),o=An(this.m_test_points,this.m_count),o.length>0&&(s=Tn(o,o.length),s===!1&&(this.m_auto=!1));let l=e.m_debugDraw;s===!1?this.addText(`generation = ${this.m_generation}, FAILED`):this.addText(`generation = ${this.m_generation}, count = ${o.length}`),l.DrawPolygon(o,o.length,new F(.9,.9,.9));for(let a=0;athis.Adjust(-.1,0,0)),G("d","Move Right",()=>this.Adjust(.1,0,0)),G("s","Move Down",()=>this.Adjust(0,-.1,0)),G("w","Move Up",()=>this.Adjust(0,.1,0)),G("q","Turn Left",()=>this.Adjust(0,0,.1*Math.PI)),G("e","Turn Right",()=>this.Adjust(0,0,-.1*Math.PI))]}Adjust(e,r,o){this.m_positionB.x+=e,this.m_positionB.y+=r,this.m_angleB+=o,this.m_transformB.SetPositionAngle(this.m_positionB,this.m_angleB)}Step(e,r){super.Step(e,r);let o=new Mi;o.proxyA.SetShape(this.m_polygonA,0),o.proxyB.SetShape(this.m_polygonB,0),o.transformA.Copy(this.m_transformA),o.transformB.Copy(this.m_transformB),o.useRadii=!0;let s=new Di;s.count=0;let l=new Ri;Mr(l,s,o),this.addDebug("Distance",l.distance.toFixed(2)),this.addDebug("Iterations",l.iterations);let a=e.m_debugDraw;{let d=new F(.9,.9,.9),h=[];for(let f=0;fnew Nh);this.m_stepCount=0;this.m_automated=!1;this.m_worldExtent=15,this.m_proxyExtent=.5;for(let r=0;r>2);for(let u=0;u{this.m_automated=!this.m_automated}),G("c","Create Proxy",()=>this.CreateProxy()),G("d","Destroy Proxy",()=>this.DestroyProxy()),G("m","Move Proxy",()=>this.MoveProxy())]}GetRandomAABB(e){let r=new n;r.Set(2*this.m_proxyExtent,2*this.m_proxyExtent),e.lowerBound.x=W(-this.m_worldExtent,this.m_worldExtent),e.lowerBound.y=W(0,2*this.m_worldExtent),e.upperBound.Copy(e.lowerBound),e.upperBound.Add(r)}MoveAABB(e){let r=new n;r.x=W(-.5,.5),r.y=W(-.5,.5),e.lowerBound.Add(r),e.upperBound.Add(r);let o=n.Mid(e.lowerBound,e.upperBound,new n),s=new n(-this.m_worldExtent,0),l=new n(this.m_worldExtent,2*this.m_worldExtent),a=n.Clamp(o,s,l,new n);e.lowerBound.Add(n.Subtract(a,o,new n)),e.upperBound.Add(n.Subtract(a,o,new n))}CreateProxy(){for(let e=0;e{let r=at(e.userData);return r.overlap=this.m_queryAABB.TestOverlap(r.aabb),!0});for(let e=0;e{let a=at(l.userData),c=new _i;return a.aabb.RayCast(c,s)?(this.m_rayCastOutput=c,this.m_rayActor=a,this.m_rayActor.fraction=c.fraction,c.fraction):s.maxFraction});let r=null,o=new _i;for(let s=0;sthis.CreateBody(0)),G("2","Create Flat Triangle",()=>this.CreateBody(1)),G("3","Create Octagon",()=>this.CreateBody(2)),G("4","Create Box",()=>this.CreateBody(3)),G("5","Create Circle",()=>this.CreateBody(4)),G("d","Destroy Body",()=>this.DestroyBody())]}Step(e,r){let o=!e.m_pause||e.m_singleStep;super.Step(e,r);let s=25,l=new n(0,10),a=new n(s*Math.cos(this.m_angle),-s*Math.abs(Math.sin(this.m_angle))),c=n.Add(l,a,new n),u=null,m=new n,p=new n;this.m_world.RayCast(l,c,(h,f,b,x)=>(u=h,m.Copy(f),p.Copy(b),x));let d=e.m_debugDraw;if(u){d.DrawPoint(m,5,new F(.4,.9,.4)),d.DrawSegment(l,m,new F(.8,.8,.8));let h=n.Add(m,n.Scale(.5,p,n.s_t0),new n);d.DrawSegment(m,h,new F(.9,.9,.4))}else d.DrawSegment(l,c,new F(.8,.8,.8));o&&(this.m_angle+=.25*Math.PI/180)}};P("Geometry","Edge Shapes",Qh);var Ss=mt(Ut());function yu(i,t,e,r){return{type:"radio",name:i,options:t,initialValue:e,update:r}}var Bx=({control:i})=>Ss.default.createElement(Ss.default.Fragment,null,i.options.map(t=>Ss.default.createElement("label",{className:"radio",key:t},Ss.default.createElement("input",{name:i.name,type:"radio",className:"radio--input",value:t,onClick:()=>i.update(t),defaultChecked:i.initialValue===t}),Ss.default.createElement("span",null,t)))," ");var qh=class extends R{constructor(){super();this.m_offset1=new n;this.m_offset2=new n;this.m_body1=null;this.m_body2=null;let e=[new n(10,-4),new n(10,0),new n(6,0),new n(4,2),new n(2,0),new n(-2,0),new n(-6,0),new n(-8,-3),new n(-10,0),new n(-10,-4)];this.m_offset1.Set(0,8),this.m_offset2.Set(0,16);{let r=e[0].Clone().Add(this.m_offset1),o=e[1].Clone().Add(this.m_offset1),s=e[2].Clone().Add(this.m_offset1),l=e[3].Clone().Add(this.m_offset1),a=e[4].Clone().Add(this.m_offset1),c=e[5].Clone().Add(this.m_offset1),u=e[6].Clone().Add(this.m_offset1),m=e[7].Clone().Add(this.m_offset1),p=e[8].Clone().Add(this.m_offset1),d=e[9].Clone().Add(this.m_offset1),h=this.m_world.CreateBody(),f=new I;f.SetOneSided(d,r,o,s),h.CreateFixture({shape:f}),f.SetOneSided(r,o,s,l),h.CreateFixture({shape:f}),f.SetOneSided(o,s,l,a),h.CreateFixture({shape:f}),f.SetOneSided(s,l,a,c),h.CreateFixture({shape:f}),f.SetOneSided(l,a,c,u),h.CreateFixture({shape:f}),f.SetOneSided(a,c,u,m),h.CreateFixture({shape:f}),f.SetOneSided(c,u,m,p),h.CreateFixture({shape:f}),f.SetOneSided(u,m,p,d),h.CreateFixture({shape:f}),f.SetOneSided(m,p,d,r),h.CreateFixture({shape:f}),f.SetOneSided(p,d,r,o),h.CreateFixture({shape:f})}{let r=e[0].Clone().Add(this.m_offset2),o=e[1].Clone().Add(this.m_offset2),s=e[2].Clone().Add(this.m_offset2),l=e[3].Clone().Add(this.m_offset2),a=e[4].Clone().Add(this.m_offset2),c=e[5].Clone().Add(this.m_offset2),u=e[6].Clone().Add(this.m_offset2),m=e[7].Clone().Add(this.m_offset2),p=e[8].Clone().Add(this.m_offset2),d=e[9].Clone().Add(this.m_offset2),h=this.m_world.CreateBody(),f=new I;f.SetTwoSided(r,o),h.CreateFixture({shape:f}),f.SetTwoSided(o,s),h.CreateFixture({shape:f}),f.SetTwoSided(s,l),h.CreateFixture({shape:f}),f.SetTwoSided(l,a),h.CreateFixture({shape:f}),f.SetTwoSided(a,c),h.CreateFixture({shape:f}),f.SetTwoSided(c,u),h.CreateFixture({shape:f}),f.SetTwoSided(u,m),h.CreateFixture({shape:f}),f.SetTwoSided(m,p),h.CreateFixture({shape:f}),f.SetTwoSided(p,d),h.CreateFixture({shape:f}),f.SetTwoSided(d,r),h.CreateFixture({shape:f})}this.m_body1=null,this.m_body2=null,this.CreateBoxes()}setupControls(){this.addTestControlGroup("Custom",[yu("Type",["Boxes","Circles"],"Boxes",e=>{e==="Boxes"?this.CreateBoxes():this.CreateCircles()})])}CreateBoxes(){this.m_body1&&(this.m_world.DestroyBody(this.m_body1),this.m_body1=null),this.m_body2&&(this.m_world.DestroyBody(this.m_body2),this.m_body2=null);{this.m_body1=this.m_world.CreateBody({type:2,position:{x:8+this.m_offset1.x,y:2.6+this.m_offset1.y},allowSleep:!1});let e=new v;e.SetAsBox(.5,1),this.m_body1.CreateFixture({shape:e,density:1})}{this.m_body2=this.m_world.CreateBody({type:2,position:{x:8+this.m_offset2.x,y:2.6+this.m_offset2.y},allowSleep:!1});let e=new v;e.SetAsBox(.5,1),this.m_body2.CreateFixture({shape:e,density:1})}}CreateCircles(){this.m_body1&&(this.m_world.DestroyBody(this.m_body1),this.m_body1=null),this.m_body2&&(this.m_world.DestroyBody(this.m_body2),this.m_body2=null);{this.m_body1=this.m_world.CreateBody({type:2,position:{x:this.m_offset1.x-.5,y:this.m_offset1.y+.6},allowSleep:!1});let e=new V(.5);this.m_body1.CreateFixture({shape:e,density:1})}{this.m_body2=this.m_world.CreateBody({type:2,position:{x:this.m_offset2.x-.5,y:this.m_offset2.y+.6},allowSleep:!1});let e=new V(.5);this.m_body2.CreateFixture({shape:e,density:1})}}getHotkeys(){return[Vr("a","Apply Force Left",()=>{this.m_body1?.ApplyForceToCenter(new n(-10,0),!0),this.m_body2?.ApplyForceToCenter(new n(-10,0),!0)}),Vr("d","Apply Force Right",()=>{this.m_body1?.ApplyForceToCenter(new n(10,0),!0),this.m_body2?.ApplyForceToCenter(new n(10,0),!0)})]}};P("Geometry","Edge Test",qh);var Wh=class extends R{constructor(){super();let t=null;{t=this.m_world.CreateBody();let e=new I;e.SetTwoSided(new n(-50,0),new n(50,0)),t.CreateFixture({shape:e})}{let e=new V;e.m_radius=1;let r=new v;r.SetAsBox(.5,5);let o=new V;o.m_radius=2;let s={x:10,y:9},l=this.m_world.CreateBody({type:0,position:s});l.CreateFixture({shape:e,density:5});let a=this.m_world.CreateBody({type:2,position:{x:10,y:8}});a.CreateFixture({shape:r,density:5});let c={x:10,y:6},u=this.m_world.CreateBody({type:2,position:c});u.CreateFixture({shape:o,density:5});let m=new q;m.Initialize(a,l,s);let p=this.m_world.CreateJoint(m),d=new q;d.Initialize(a,u,c);let h=this.m_world.CreateJoint(d),f=new ns;f.bodyA=l,f.bodyB=u,f.joint1=p,f.joint2=h,f.ratio=o.m_radius/e.m_radius,this.m_world.CreateJoint(f)}{let e=new V;e.m_radius=1;let r=new V;r.m_radius=2;let o=new v;o.SetAsBox(.5,5);let s={x:-3,y:12},l=this.m_world.CreateBody({type:2,position:s});l.CreateFixture({shape:e,density:5});let a=new q;a.bodyA=t,a.bodyB=l,t.GetLocalPoint(s,a.localAnchorA),l.GetLocalPoint(s,a.localAnchorB),a.referenceAngle=l.GetAngle()-t.GetAngle(),this.m_joint1=this.m_world.CreateJoint(a);let c={x:0,y:12},u=this.m_world.CreateBody({type:2,position:c});u.CreateFixture({shape:r,density:5});let m=new q;m.Initialize(t,u,c),this.m_joint2=this.m_world.CreateJoint(m);let p={x:2.5,y:12},d=this.m_world.CreateBody({type:2,position:p});d.CreateFixture({shape:o,density:5});let h=new Zt;h.Initialize(t,d,p,new n(0,1)),h.lowerTranslation=-5,h.upperTranslation=5,h.enableLimit=!0,this.m_joint3=this.m_world.CreateJoint(h);let f=new ns;f.bodyA=l,f.bodyB=u,f.joint1=this.m_joint1,f.joint2=this.m_joint2,f.ratio=r.m_radius/e.m_radius,this.m_joint4=this.m_world.CreateJoint(f);let b=new ns;b.bodyA=u,b.bodyB=d,b.joint1=this.m_joint2,b.joint2=this.m_joint3,b.ratio=-1/r.m_radius,this.m_joint5=this.m_world.CreateJoint(b)}}};P("Joints","Gear",Wh);var Kh=class extends R{constructor(){super();{let r=this.m_world.CreateBody(),o=new I;o.SetTwoSided(new n(-40,0),new n(40,0)),r.CreateFixture({shape:o})}let t=this.m_world.CreateBody({type:2,position:{x:0,y:.5}}),e=new V;e.m_radius=.5,t.CreateFixture({shape:e,density:10}),t=this.m_world.CreateBody({type:2,position:{x:0,y:6}}),e.m_radius=5,t.CreateFixture({shape:e,density:10})}};P("Solver","Heavy 1",Kh);var $h=class extends R{constructor(){super();this.m_heavy=null;{let o=this.m_world.CreateBody(),s=new I;s.SetTwoSided(new n(-40,0),new n(40,0)),o.CreateFixture({shape:s})}let e=this.m_world.CreateBody({type:2,position:{x:0,y:2.5}}),r=new V;r.m_radius=.5,e.CreateFixture({shape:r,density:10}),e=this.m_world.CreateBody({type:2,position:{x:0,y:3.5}}),e.CreateFixture({shape:r,density:10})}ToggleHeavy(){if(this.m_heavy!==null)this.m_world.DestroyBody(this.m_heavy),this.m_heavy=null;else{this.m_heavy=this.m_world.CreateBody({type:2,position:{x:0,y:9}});let e=new V;e.m_radius=5,this.m_heavy.CreateFixture({shape:e,density:10})}}getHotkeys(){return[G("h","Toggle Heavy",()=>this.ToggleHeavy())]}};P("Solver","Heavy 2",$h);var tf=class i extends R{static{this.e_depth=4}constructor(){super();let t=this.m_world.CreateBody({position:{x:0,y:20}}),e=.5,r=new n(0,e),o=this.AddNode(t,n.ZERO,0,3,e),s=new q;s.bodyA=t,s.bodyB=o,s.localAnchorA.SetZero(),s.localAnchorB.Copy(r),this.m_world.CreateJoint(s)}GetDefaultViewZoom(){return 60}getCenter(){return{x:0,y:15}}AddNode(t,e,r,o,s){let a=new n(0,s),c=this.m_world.CreateBody({type:2,position:t.GetPosition().Clone().Add(e).Subtract(a)}),u=new v;if(u.SetAsBox(.25*s,s),c.CreateFixture({shape:u,density:20}),r===i.e_depth)return c;let m=new n(o,-s),p=new n(-o,-s),d=this.AddNode(c,m,r+1,.5*o,s),h=this.AddNode(c,p,r+1,.5*o,s),f=new q;return f.bodyA=c,f.localAnchorB.Copy(a),f.localAnchorA.Copy(m),f.bodyB=d,this.m_world.CreateJoint(f),f.localAnchorA.Copy(p),f.bodyB=h,this.m_world.CreateJoint(f),c}};P("Solver","Mobile Unbalanced",tf);var ef=class i extends R{static{this.e_depth=4}constructor(){super();let t=this.m_world.CreateBody({position:{x:0,y:20}}),e=.5,r=new n(0,e),o=this.AddNode(t,n.ZERO,0,3,e),s=new q;s.bodyA=t,s.bodyB=o,s.localAnchorA.SetZero(),s.localAnchorB.Copy(r),this.m_world.CreateJoint(s)}GetDefaultViewZoom(){return 60}getCenter(){return{x:0,y:15}}AddNode(t,e,r,o,s){let a=new n(0,s),c=t.GetPosition().Clone().Add(e).Subtract(a),u=this.m_world.CreateBody({type:2,position:c}),m=new v;if(m.SetAsBox(.25*s,s),u.CreateFixture({shape:m,density:20}),r===i.e_depth)return u;m.SetAsBox(o,.25*s,new n(0,-s),0),u.CreateFixture({shape:m,density:20});let p=new n(o,-s),d=new n(-o,-s),h=this.AddNode(u,p,r+1,.5*o,s),f=this.AddNode(u,d,r+1,.5*o,s),b=new q;return b.bodyA=u,b.localAnchorB.Copy(a),b.localAnchorA.Copy(p),b.bodyB=h,this.m_world.CreateJoint(b),b.localAnchorA.Copy(d),b.bodyB=f,this.m_world.CreateJoint(b),u}};P("Solver","Mobile Balanced",ef);var rf=class extends R{constructor(){super();this.m_time=0;this.m_go=!1;let e=null;{e=this.m_world.CreateBody();let r=new I;r.SetTwoSided(new n(-20,0),new n(20,0)),e.CreateFixture({shape:r})}{let r=this.m_world.CreateBody({type:2,position:{x:0,y:8}}),o=new v;o.SetAsBox(2,.5),r.CreateFixture({shape:o,friction:.6,density:2});let s=new ls;s.Initialize(e,r),s.maxForce=1e3,s.maxTorque=1e3,this.m_joint=this.m_world.CreateJoint(s)}this.m_go=!1,this.m_time=0}getHotkeys(){return[G("s","Start/Stop",()=>{this.m_go=!this.m_go})]}Step(e,r){this.m_go&&e.m_hertz>0&&(this.m_time+=1/e.m_hertz);let o=new n;o.x=6*Math.sin(2*this.m_time),o.y=8+4*Math.sin(1*this.m_time);let s=4*this.m_time;this.m_joint.SetLinearOffset(o),this.m_joint.SetAngularOffset(s),e.m_debugDraw.DrawPoint(o,4,new F(.9,.9,.9)),super.Step(e,r)}};P("Joints","Motor Joint",rf);var of=class extends R{constructor(){super();this.m_radius=0;this.m_top=0;this.m_bottom=0;this.m_state=0;{let e=this.m_world.CreateBody(),r=new I;r.SetTwoSided(new n(-20,0),new n(20,0)),e.CreateFixture({shape:r})}{let e=this.m_world.CreateBody({position:{x:0,y:10}}),r=new v;r.SetAsBox(3,.5),this.m_platform=e.CreateFixture({shape:r}),this.m_bottom=10-.5,this.m_top=10+.5}{let e=this.m_world.CreateBody({type:2,position:{x:0,y:12}});this.m_radius=.5;let r=new V;r.m_radius=this.m_radius,this.m_character=e.CreateFixture({shape:r,density:20}),e.SetLinearVelocity(new n(0,-50)),this.m_state=0}}GetDefaultViewZoom(){return 40}getCenter(){return{x:0,y:5}}PreSolve(e,r){super.PreSolve(e,r);let o=e.GetFixtureA(),s=e.GetFixtureB();if(o!==this.m_platform&&o!==this.m_character||s!==this.m_platform&&s!==this.m_character)return;this.m_character.GetBody().GetPosition().y{t?(this.m_leftJoint.SetMotorSpeed(20),this.m_rightJoint.SetMotorSpeed(-20)):(this.m_leftJoint.SetMotorSpeed(-10),this.m_rightJoint.SetMotorSpeed(10))})]}};P("Examples","Pinball",sf);var nf=class extends R{constructor(){super();this.m_polygonA=new v;this.m_polygonB=new v;this.m_transformA=new T;this.m_transformB=new T;this.m_positionB=new n;this.m_angleB=0;this.m_polygonA.SetAsBox(.2,.4),this.m_transformA.SetPositionAngle(new n,0),this.m_polygonB.SetAsBox(.5,.5),this.m_positionB.Set(1,1),this.m_angleB=1.9160721,this.m_transformB.SetPositionAngle(this.m_positionB,this.m_angleB)}GetDefaultViewZoom(){return 100}getHotkeys(){return[G("a","Move Left",()=>this.Adjust(-.1,0,0)),G("d","Move Right",()=>this.Adjust(.1,0,0)),G("s","Move Down",()=>this.Adjust(0,-.1,0)),G("w","Move Up",()=>this.Adjust(0,.1,0)),G("q","Turn Left",()=>this.Adjust(0,0,.1*Math.PI)),G("e","Turn Right",()=>this.Adjust(0,0,-.1*Math.PI))]}Adjust(e,r,o){this.m_positionB.x+=e,this.m_positionB.y+=r,this.m_angleB+=o,this.m_transformB.SetPositionAngle(this.m_positionB,this.m_angleB)}Step(e,r){super.Step(e,r);let o=new co;Xa(o,this.m_polygonA,this.m_transformA,this.m_polygonB,this.m_transformB);let s=new ki;s.Initialize(o,this.m_transformA,this.m_polygonA.m_radius,this.m_transformB,this.m_polygonB.m_radius),this.addDebug("Point Count",o.pointCount);let l=e.m_debugDraw;{let a=new F(.9,.9,.9),c=[];for(let u=0;unull);this.m_polygons=Array.from({length:4},()=>new v);this.m_circle=new V;{let e=this.m_world.CreateBody(),r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r})}{let e=new Array(3);e[0]=new n(-.5,0),e[1]=new n(.5,0),e[2]=new n(0,1.5),this.m_polygons[0].Set(e,3)}{let e=new Array(3);e[0]=new n(-.1,0),e[1]=new n(.1,0),e[2]=new n(0,1.5),this.m_polygons[1].Set(e,3)}{let r=1/(2+Math.sqrt(2)),o=Math.sqrt(2)*r,s=new Array(8);s[0]=new n(.5*o,0),s[1]=new n(.5*1,r),s[2]=new n(.5*1,r+o),s[3]=new n(.5*o,1),s[4]=new n(-.5*o,1),s[5]=new n(-.5*1,r+o),s[6]=new n(-.5*1,r),s[7]=new n(-.5*o,0),this.m_polygons[2].Set(s,8)}this.m_polygons[3].SetAsBox(.5,.5),this.m_circle.m_radius=.5;for(let e=0;ethis.CreateBody(0)),G("2","Create Flat Triangle",()=>this.CreateBody(1)),G("3","Create Octagon",()=>this.CreateBody(2)),G("4","Create Box",()=>this.CreateBody(3)),G("5","Create Circle",()=>this.CreateBody(4)),G("a","Toggle Enabled of Even Bodies",()=>{for(let e=0;ethis.DestroyBody())]}Step(e,r){super.Step(e,r);let o=e.m_debugDraw,s=0,{circle:l,transform:a}=_A.Step;l.m_radius=2,l.m_p.Set(0,1.1),a.SetIdentity();let c=new nt;l.ComputeAABB(c,a,0),this.m_world.QueryAABB(c,m=>{if(s===bA)return!1;let p=m.GetBody(),d=m.GetShape();if(ts(d,0,l,0,p.GetTransform(),a)){let f=new F(.95,.95,.6),b=p.GetWorldCenter();o.DrawPoint(b,5,f),++s}return!0});let u=new F(.4,.7,.8);o.DrawCircle(l.m_p,l.m_radius,u)}};P("Geometry","Polygon Shapes",lf);var yo=mt(Ut());function _t(i,t,e,r,o,s){return{type:"slider",name:i,min:t,max:e,step:r,initialValue:o,update:s}}function Cx({control:i}){let[t,e]=(0,yo.useState)(i.initialValue);return yo.default.createElement("label",{className:"slider"},yo.default.createElement("div",{className:"slider--input"},yo.default.createElement("input",{type:"range",min:i.min,max:i.max,step:i.step,defaultValue:t,onChange:r=>{let o=parseFloat(r.currentTarget.value);e(o),i.update(o)}}),yo.default.createElement("span",null,t)),i.name.split("#")[0])}var af=class extends R{constructor(){super();this.m_motorSpeed=10;this.m_enableMotor=!1;this.m_enableLimit=!0;let e=null;{e=this.m_world.CreateBody();let r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r})}{let r=new v;r.SetAsBox(1,1);let o={x:0,y:10},s=this.m_world.CreateBody({type:2,position:o,angle:.5*Math.PI,allowSleep:!1});s.CreateFixture({shape:r,density:5});let l=new Zt;l.Initialize(e,s,o,new n(1,0)),l.motorSpeed=this.m_motorSpeed,l.maxMotorForce=1e4,l.enableMotor=this.m_enableMotor,l.lowerTranslation=-10,l.upperTranslation=10,l.enableLimit=this.m_enableLimit,this.m_joint=this.m_world.CreateJoint(l)}}setupControls(){this.addTestControlGroup("Joint",[jt("Limit",this.m_enableLimit,e=>{this.m_enableLimit=this.m_joint.EnableLimit(e)}),jt("Motor",this.m_enableMotor,e=>{this.m_enableMotor=this.m_joint.EnableMotor(e)}),_t("Speed",-100,100,1,this.m_motorSpeed,e=>{this.m_motorSpeed=this.m_joint.SetMotorSpeed(e)})])}getHotkeys(){return[G("l","Toggle Limit",()=>this.m_joint.EnableLimit(!this.m_joint.IsLimitEnabled())),G("m","Start/Stop",()=>this.m_joint.EnableMotor(!this.m_joint.IsMotorEnabled())),G("s","Reverse Direction",()=>this.m_joint.SetMotorSpeed(-this.m_joint.GetMotorSpeed()))]}Step(e,r){super.Step(e,r);let o=this.m_joint.GetMotorForce(e.m_hertz);this.addDebug("Motor Force",o.toFixed())}};P("Joints","Prismatic",af);var cf=class extends R{constructor(){super();let t=16,e=12,r=1,o=2,s=null;{s=this.m_world.CreateBody();let l=new V;l.m_radius=2,l.m_p.Set(-10,t+o+e),s.CreateFixture({shape:l}),l.m_p.Set(10,t+o+e),s.CreateFixture({shape:l})}{let l=new v;l.SetAsBox(r,o);let a=this.m_world.CreateBody({type:2,position:{x:-10,y:t}});a.CreateFixture({shape:l,density:5});let c=this.m_world.CreateBody({type:2,position:{x:10,y:t}});c.CreateFixture({shape:l,density:5});let u=new sc,m=new n(-10,t+o),p=new n(10,t+o),d=new n(-10,t+o+e),h=new n(10,t+o+e);u.Initialize(a,c,d,h,m,p,1.5),this.m_joint1=this.m_world.CreateJoint(u)}}getCenter(){return{x:0,y:15}}Step(t,e){super.Step(t,e),this.addDebug("Ratio",this.m_joint1.GetRatio().toFixed(2)),this.addDebug("Length A",this.m_joint1.GetCurrentLengthA().toFixed(2)),this.addDebug("Length B",this.m_joint1.GetCurrentLengthB().toFixed(2))}};P("Joints","Pulley",cf);var uf=class i extends R{static{this.e_count=20}constructor(){super();{let t=this.m_world.CreateBody(),e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e})}{let e=new v;e.SetAsBox(.5,.5);let r=new n(-7,.75),o=new n,s=new n(.5625,1.25),l=new n(1.125,0);for(let a=0;a{this.m_mode=e}),_t("Angle",0,360,1,this.m_degrees,e=>{this.m_degrees=e})])}CreateBody(e){let r=this.m_bodies[this.m_bodyIndex];r!==null&&(this.m_world.DestroyBody(r),this.m_bodies[this.m_bodyIndex]=null);let o=this.m_bodies[this.m_bodyIndex]=this.m_world.CreateBody({position:{x:W(-10,10),y:W(0,20)},angle:W(-Math.PI,Math.PI),angularDamping:e===4?.02:0,userData:{rayCastBodyIndex:e}});e<4?o.CreateFixture({shape:this.m_polygons[e],friction:.3}):e<5?o.CreateFixture({shape:this.m_circle,friction:.3}):o.CreateFixture({shape:this.m_edge,friction:.3}),this.m_bodyIndex=(this.m_bodyIndex+1)%i.e_maxBodies}DestroyBody(){for(let e=0;ethis.CreateBody(0)),G("2","Create Flat Triangle",()=>this.CreateBody(1)),G("3","Create Octagon",()=>this.CreateBody(2)),G("4","Create Box",()=>this.CreateBody(3)),G("5","Create Circle",()=>this.CreateBody(4)),G("6","Create Edge",()=>this.CreateBody(5)),G("d","Destroy Body",()=>this.DestroyBody())]}Step(e,r){super.Step(e,r);let o=pt(this.m_degrees),s=11,l=new n(0,10),a=new n(s*Math.cos(o),s*Math.sin(o)),c=n.Add(l,a,new n);switch(this.addText("Shape 1 is intentionally ignored by the ray"),this.m_mode){case"Closest":this.addDebug("Ray-Cast Mode","Find closest fixture along the ray"),this.rayCastClosest(l,c,e);break;case"Any":this.addDebug("Ray-Cast Mode","Check for obstruction"),this.rayCastAny(l,c,e);break;case"Multiple":this.addDebug("Ray-Cast Mode","Gather multiple fixtures"),this.rayCastMultiple(l,c,e);break}}rayCastClosest(e,r,o){let s=!1,l=new n,a=new n;this.m_world.RayCast(e,r,(u,m,p,d)=>{let{rayCastBodyIndex:h}=u.GetBody().GetUserData();return h===0?-1:(s=!0,l.Copy(m),a.Copy(p),d)});let c=o.m_debugDraw;if(s){c.DrawPoint(l,5,new F(.4,.9,.4)),c.DrawSegment(e,l,new F(.8,.8,.8));let u=n.Add(l,n.Scale(.5,a,n.s_t0),new n);c.DrawSegment(l,u,new F(.9,.9,.4))}else c.DrawSegment(e,r,new F(.8,.8,.8))}rayCastAny(e,r,o){let s=!1,l=new n,a=new n;this.m_world.RayCast(e,r,(u,m,p,d)=>{let{rayCastBodyIndex:h}=u.GetBody().GetUserData();return h===0?-1:(s=!0,l.Copy(m),a.Copy(p),0)});let c=o.m_debugDraw;if(s){c.DrawPoint(l,5,new F(.4,.9,.4)),c.DrawSegment(e,l,new F(.8,.8,.8));let u=n.Add(l,n.Scale(.5,a,n.s_t0),new n);c.DrawSegment(l,u,new F(.9,.9,.4))}else c.DrawSegment(e,r,new F(.8,.8,.8))}rayCastMultiple(e,r,o){let l=H(3,n),a=H(3,n),c=0;this.m_world.RayCast(e,r,(m,p,d)=>{let{rayCastBodyIndex:h}=m.GetBody().GetUserData();return h===0?-1:(l[c].Copy(p),a[c].Copy(d),++c,c===3?0:1)});let u=o.m_debugDraw;u.DrawSegment(e,r,new F(.8,.8,.8));for(let m=0;m{this.m_enableLimit=this.m_joint1.EnableLimit(r)}),jt("Motor",this.m_enableMotor,r=>{this.m_enableMotor=this.m_joint1.EnableMotor(r)}),_t("Speed",-20,20,1,this.m_motorSpeed,r=>{this.m_motorSpeed=this.m_joint1.SetMotorSpeed(r)})])}getCenter(){return{x:0,y:5}}Step(e,r){super.Step(e,r);let o=this.m_joint1.GetMotorTorque(e.m_hertz);this.addDebug("Motor Torque 1",o.toFixed(0));let s=this.m_joint2.GetMotorTorque(e.m_hertz);this.addDebug("Motor Torque 2",s.toFixed(0))}};P("Joints","Revolute",pf);var df=class i extends R{constructor(){super();this.m_bodies=new Array(i.e_count);this.m_force=100;this.m_touching=X2(i.e_count);let e=this.m_world.CreateBody();{let r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r})}{let r=new V;r.m_radius=5,r.m_p.Set(0,10),this.m_sensor=e.CreateFixture({shape:r,isSensor:!0})}{let r=new V;r.m_radius=1;for(let o=0;o{this.m_force=e})])}getCenter(){return{x:0,y:5}}BeginContact(e){let r=e.GetFixtureA(),o=e.GetFixtureB();if(r===this.m_sensor){let{sensor:s}=o.GetBody().GetUserData();s!==void 0&&(this.m_touching[s]=!0)}if(o===this.m_sensor){let{sensor:s}=r.GetBody().GetUserData();s!==void 0&&(this.m_touching[s]=!0)}}EndContact(e){let r=e.GetFixtureA(),o=e.GetFixtureB();if(r===this.m_sensor){let{sensor:s}=o.GetBody().GetUserData();s!==void 0&&(this.m_touching[s]=!1)}if(o===this.m_sensor){let{sensor:s}=r.GetBody().GetUserData();s!==void 0&&(this.m_touching[s]=!1)}}Step(e,r){super.Step(e,r);for(let o=0;o{if(this.m_fixture2===null){let e=new V;e.m_radius=3,e.m_p.Set(.5,-4),this.m_fixture2=this.m_body.CreateFixture({shape:e,density:10}),this.m_body.SetAwake(!0)}}),G("d","Destroy a Shape",()=>{this.m_fixture2!==null&&(this.m_body.DestroyFixture(this.m_fixture2),this.m_fixture2=null,this.m_body.SetAwake(!0))}),G("s","Toggle Sensor",()=>{this.m_fixture2!==null&&(this.m_sensor=!this.m_sensor,this.m_fixture2.SetSensor(this.m_sensor))})]}Step(e,r){super.Step(e,r),this.addDebug("Sensor",this.m_sensor)}};P("Examples","Shape Editing",ff);var bf=class extends R{constructor(){super();let t=this.m_world.CreateBody(),e=8,r=-30,o=10,s=2,l=.2,a=-r*Math.PI/180,c=a-o*Math.PI/180;this.m_platform_width=e;let u=new n(-e,0),m=new n,p=new n(s*Math.cos(a),-s*Math.sin(a)),d=new n(p.x+s*Math.cos(c),p.y-s*Math.sin(c)),f=[new n(d.x,d.y-1),d,p,m,u],b=new dt;b.CreateLoop(f),t.CreateFixture({shape:b,density:0,friction:l});{let B=this.m_world.CreateBody({type:2,position:{x:-this.m_platform_width/2,y:1.55}}),C=new v,A=H(4,n);A[0].Set(-3/2-.3,-2.5/2),A[1].Set(-3/2,-2.5/2-.3),A[2].Set(3/2,-2.5/2-.3),A[3].Set(3/2+.3,-2.5/2),C.Set(A),B.CreateFixture({density:1,friction:0,restitution:.15,shape:C}),B.SetLinearVelocity(new n(.5,0)),this.m_skier=B}ot.setPositionAndZoom(this.m_platform_width/2,0,.4),this.m_fixed_camera=!0}GetDefaultViewZoom(){return 50}getHotkeys(){return[G("c","Switch Camera Fixed/Tracking",()=>{this.m_fixed_camera=!this.m_fixed_camera,this.m_fixed_camera&&ot.setPosition(this.m_platform_width/2,0)})]}Step(t,e){if(!this.m_fixed_camera){let r=this.m_skier.GetPosition();ot.setPosition(r.x,r.y)}super.Step(t,e)}};P("Bugs","Skier",bf);var _f=class extends R{constructor(){super();let t=null;{t=this.m_world.CreateBody();let e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e})}{let e=t;{let r=new v;r.SetAsBox(.5,2);let o=this.m_world.CreateBody({type:2,position:{x:0,y:7}});o.CreateFixture({shape:r,density:2});let s=new q;s.Initialize(e,o,new n(0,5)),s.motorSpeed=1*Math.PI,s.maxMotorTorque=1e4,s.enableMotor=!0,this.m_joint1=this.m_world.CreateJoint(s),e=o}{let r=new v;r.SetAsBox(.5,4);let o=this.m_world.CreateBody({type:2,position:{x:0,y:13}});o.CreateFixture({shape:r,density:2});let s=new q;s.Initialize(e,o,new n(0,9)),s.enableMotor=!1,this.m_world.CreateJoint(s),e=o}{let r=new v;r.SetAsBox(1.5,1.5);let o=this.m_world.CreateBody({type:2,fixedRotation:!0,position:{x:0,y:17}});o.CreateFixture({shape:r,density:2});let s=new q;s.Initialize(e,o,new n(0,17)),this.m_world.CreateJoint(s);let l=new Zt;l.Initialize(t,o,new n(0,17),new n(0,1)),l.maxMotorForce=1e3,l.enableMotor=!0,this.m_joint2=this.m_world.CreateJoint(l)}{let r=new v;r.SetAsBox(1.5,1.5),this.m_world.CreateBody({type:2,position:{x:0,y:23}}).CreateFixture({shape:r,density:2})}}}getHotkeys(){return[G("f","Toggle Friction",()=>{this.m_joint2.EnableMotor(!this.m_joint2.IsMotorEnabled()),this.m_joint2.GetBodyB().SetAwake(!0)}),G("m","Toggle Motor",()=>{this.m_joint1.EnableMotor(!this.m_joint1.IsMotorEnabled()),this.m_joint1.GetBodyB().SetAwake(!0)})]}Step(t,e){super.Step(t,e);let r=this.m_joint1.GetMotorTorque(t.m_hertz);this.addDebug("Motor Torque",r.toFixed(0))}};P("Examples","Slider Crank 2",_f);var yf=class i extends R{constructor(){super();this.m_bodies=[];{let e=this.m_world.CreateBody(),r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r})}{let e=new V;e.m_radius=1;for(let r=0;r0){let f=[];f[0]=o,f[1]=s,f[2]=l,m.Set(f),f[0]=n.ZERO,f[1]=n.Subtract(c,a,new n),f[2]=n.Subtract(u,a,new n),p.Set(f)}else{let f=[];f[0]=o,f[1]=l,f[2]=s,m.Set(f),f[0]=n.ZERO,f[1]=n.Subtract(u,a,new n),f[2]=n.Subtract(c,a,new n),p.Set(f)}let d=this.m_world.CreateBody({type:2,position:this.m_offset,angularDamping:10}),h=this.m_world.CreateBody({type:2,position:n.Add(a,this.m_offset,new n),angularDamping:10});d.CreateFixture({filter:{groupIndex:-1},shape:m,density:1}),h.CreateFixture({filter:{groupIndex:-1},density:1,shape:p});{let f=new ke,b=.5,x=10;f.Initialize(d,h,n.Add(s,this.m_offset,new n),n.Add(c,this.m_offset,new n)),Ct(f,x,b,f.bodyA,f.bodyB),this.m_world.CreateJoint(f),f.Initialize(d,h,n.Add(l,this.m_offset,new n),n.Add(a,this.m_offset,new n)),Ct(f,x,b,f.bodyA,f.bodyB),this.m_world.CreateJoint(f),f.Initialize(d,this.m_wheel,n.Add(l,this.m_offset,new n),n.Add(r,this.m_offset,new n)),Ct(f,x,b,f.bodyA,f.bodyB),this.m_world.CreateJoint(f),f.Initialize(h,this.m_wheel,n.Add(u,this.m_offset,new n),n.Add(r,this.m_offset,new n)),Ct(f,x,b,f.bodyA,f.bodyB),this.m_world.CreateJoint(f)}{let f=new q;f.Initialize(h,this.m_chassis,n.Add(a,this.m_offset,new n)),this.m_world.CreateJoint(f)}}getHotkeys(){return[G("a","Left",()=>this.m_motorJoint.SetMotorSpeed(-this.m_motorSpeed)),G("s","Brake",()=>this.m_motorJoint.SetMotorSpeed(0)),G("d","Right",()=>this.m_motorJoint.SetMotorSpeed(this.m_motorSpeed)),G("m","Toggle Enabled",()=>this.m_motorJoint.EnableMotor(!this.m_motorJoint.IsMotorEnabled()))]}};P("Examples","Theo Jansen's Walker",xf);var gf=class i extends R{constructor(){super();this.m_fixtureCount=0;this.m_createTime=0;this.m_fixtureCount=0;let e=new ii;{let o=this.m_world.CreateBody({position:{x:0,y:-.5}});{let a=new n;a.y=0;for(let c=0;c<10;++c){a.x=-200*.5;for(let u=0;u<200;++u){let m=new v;m.SetAsBox(.5,.5,a,0),o.CreateFixture({shape:m}),++this.m_fixtureCount,a.x+=2*.5}a.y-=2*.5}}}{let o=new v;o.SetAsBox(.5,.5);let s=new n(-7,.75),l=new n,a=new n(.5625,1.25),c=new n(1.125,0);for(let u=0;uthis.LaunchBullet()),G("b","Toggle Block solving",()=>Pc(!Dc()))]}Destroy(){Pc(!0)}LaunchBullet(){this.m_bullet&&(this.m_world.DestroyBody(this.m_bullet),this.m_bullet=null);{let e=new V;e.m_radius=.25;let r={shape:e,density:20,restitution:.05};this.m_bullet=this.m_world.CreateBody({type:2,bullet:!0,position:{x:-31,y:5}}),this.m_bullet.CreateFixture(r),this.m_bullet.SetLinearVelocity(new n(400,0))}}Step(e,r){super.Step(e,r),this.addDebug("Blocksolve",Dc())}};P("Stacking","Box Stack",Cf);var Af=class extends R{constructor(){super();this.m_bodies=new Array(4);this.m_joints=new Array(8);let e=null;{e=this.m_world.CreateBody();let r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r})}{let r=new v;r.SetAsBox(.5,.5);let o=this.m_bodies[0]=this.m_world.CreateBody({type:2,position:{x:-5,y:5}});o.CreateFixture({shape:r,density:5});let s=this.m_bodies[1]=this.m_world.CreateBody({type:2,position:{x:5,y:5}});s.CreateFixture({shape:r,density:5});let l=this.m_bodies[2]=this.m_world.CreateBody({type:2,position:{x:5,y:15}});l.CreateFixture({shape:r,density:5});let a=this.m_bodies[3]=this.m_world.CreateBody({type:2,position:{x:-5,y:15}});a.CreateFixture({shape:r,density:5});let c=new ke,u,m,p,d=2,h=0;c.bodyA=e,c.bodyB=o,c.localAnchorA.Set(-10,0),c.localAnchorB.Set(-.5,-.5),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[0]=this.m_world.CreateJoint(c),c.bodyA=e,c.bodyB=s,c.localAnchorA.Set(10,0),c.localAnchorB.Set(.5,-.5),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[1]=this.m_world.CreateJoint(c),c.bodyA=e,c.bodyB=l,c.localAnchorA.Set(10,20),c.localAnchorB.Set(.5,.5),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[2]=this.m_world.CreateJoint(c),c.bodyA=e,c.bodyB=a,c.localAnchorA.Set(-10,20),c.localAnchorB.Set(-.5,.5),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[3]=this.m_world.CreateJoint(c),c.bodyA=o,c.bodyB=s,c.localAnchorA.Set(.5,0),c.localAnchorB.Set(-.5,0),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[4]=this.m_world.CreateJoint(c),c.bodyA=s,c.bodyB=l,c.localAnchorA.Set(0,.5),c.localAnchorB.Set(0,-.5),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[5]=this.m_world.CreateJoint(c),c.bodyA=l,c.bodyB=a,c.localAnchorA.Set(-.5,0),c.localAnchorB.Set(.5,0),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[6]=this.m_world.CreateJoint(c),c.bodyA=a,c.bodyB=o,c.localAnchorA.Set(0,-.5),c.localAnchorB.Set(0,.5),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[7]=this.m_world.CreateJoint(c)}}JointDestroyed(e){for(let r=0;r<8;++r)if(this.m_joints[r]===e){this.m_joints[r]=null;break}}getHotkeys(){return[G("b","Delete a Body",()=>{for(let e=0;e<4;++e){let r=this.m_bodies[e];if(r){this.m_world.DestroyBody(r),this.m_bodies[e]=null;break}}}),G("j","Delete a Joint",()=>{for(let e=0;e<8;++e){let r=this.m_joints[e];if(r){this.m_world.DestroyJoint(r),this.m_joints[e]=null;break}}})]}Step(e,r){super.Step(e,r),this.addText("This demonstrates a soft distance joint.")}};P("Examples","Web",Af);var Tf=["Spring","PBD Ang","XPBD Ang","PBD Dist","PBD Height","PBD Triangle"],Pf=["PBD","XPBD"],Df=class extends R{constructor(){super();this.m_tuning1=new us;this.m_tuning2=new us;this.m_iterations=[8,8];this.m_position1=new n;this.m_position2=new n;this.m_speed=10;this.m_moveLeft=!1;this.m_moveRight=!1;let e=20,r=.5,o=H(e,n),s=je(e);for(let a=0;a{this.m_speed=e})])}ropeControls(e,r){return[rr("Bend Model#",Tf,Tf[r.bendingModel],o=>{r.bendingModel=Tf.indexOf(o)}),_t("Damping#b",0,4,.1,r.bendDamping,o=>{r.bendDamping=o}),_t("Hertz#b",0,60,1,r.bendHertz,o=>{r.bendHertz=o}),_t("Stiffness#b",0,1,.1,r.bendStiffness,o=>{r.bendStiffness=o}),jt("Isometric",r.isometric,o=>{r.isometric=o}),jt("Fixed Mass",r.fixedEffectiveMass,o=>{r.fixedEffectiveMass=o}),jt("Warm Start",r.warmStart,o=>{r.warmStart=o}),rr("Stretch Model",Pf,Pf[r.stretchingModel],o=>{r.stretchingModel=Pf.indexOf(o)}),_t("Damping#s",0,4,.1,r.stretchDamping,o=>{r.stretchDamping=o}),_t("Hertz#s",0,60,1,r.stretchHertz,o=>{r.stretchHertz=o}),_t("Stiffness#s",0,1,.1,r.stretchStiffness,o=>{r.stretchStiffness=o}),_t("Iterations",0,100,1,this.m_iterations[e],o=>{this.m_iterations[e]=o})]}GetDefaultViewZoom(){return 45}getCenter(){return{x:0,y:20}}getHotkeys(){return[Er("a","Move Left",this,"m_moveLeft"),Er("d","Move Right",this,"m_moveRight"),G("s","Reset Ropes",()=>{this.m_position1.Set(-5,15),this.m_position2.Set(5,15),this.m_rope1.Reset(this.m_position1),this.m_rope2.Reset(this.m_position2)})]}Step(e,r){let o=e.m_hertz>0?1/e.m_hertz:0;e.m_pause===!0&&e.m_singleStep===!1&&(o=0);let s=0;this.m_moveLeft&&(s-=1),this.m_moveRight&&(s+=1),s&&(this.m_position1.x+=s*this.m_speed*o,this.m_position2.x+=s*this.m_speed*o),this.m_rope1.SetTuning(this.m_tuning1),this.m_rope2.SetTuning(this.m_tuning2),this.m_rope1.Step(o,this.m_iterations[0],this.m_position1),this.m_rope2.Step(o,this.m_iterations[1],this.m_position2),super.Step(e,r);let l=e.m_debugDraw;this.m_rope1.Draw(l),this.m_rope2.Draw(l)}};P("Rope","Bending",Df);var Mf=class extends R{constructor(){super();let t;{t=this.m_world.CreateBody();let o=new I;o.SetTwoSided(new n(-20,0),new n(20,0)),t.CreateFixture({shape:o})}let e;{e=this.m_world.CreateBody({type:2,position:{x:0,y:4}});let o=new V;o.m_radius=1,e.CreateFixture({shape:o,friction:.6,density:2})}let r;{r=this.m_world.CreateBody({type:2,position:{x:4,y:8}});let o=new V;o.m_radius=1,r.CreateFixture({shape:o,friction:.6,density:2})}{let o=new ls;o.Initialize(e,r),o.maxForce=1e3,o.maxTorque=1e3,this.m_joint=this.m_world.CreateJoint(o)}}};P("Bugs","Motor Joint (Bug #487)",Mf);var Rf=class extends R{constructor(){super();let t=this.m_world.CreateBody();{let e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e}),e.SetTwoSided(new n(-40,0),new n(-40,25)),t.CreateFixture({shape:e}),e.SetTwoSided(new n(40,0),new n(40,25)),t.CreateFixture({shape:e})}{let e=new qa,r=0,o=10,s=5,l=5,a=20,c=.5;for(let p=0;p{let d=c.CreateBody({type:2,position:u}),h=new v;m?h.SetAsBox(.5*e,.5*t):h.SetAsBox(.5*t,.5*e),d.CreateFixture({shape:h,density:1/(t*e),friction:.6,restitution:0})},o=this.m_world,s=o.CreateBody(),l=new I;l.SetTwoSided(new n(-600,-240),new n(600,-240)),s.CreateFixture({shape:l,friction:1,restitution:1});let a=12;for(let c=0;c3&&(s*=.8);let m=r*.5+(r+2*t)*.99*u;for(let p=0;p{t.frictionModifier>this.m_currentTraction&&(this.m_currentTraction=t.frictionModifier)}))}getLateralVelocity(){let t=this.m_body.GetWorldVector(new n(1,0),new n);return t.Scale(n.Dot(t,this.m_body.GetLinearVelocity()))}getForwardVelocity(){let t=this.m_body.GetWorldVector(new n(0,1),new n);return t.Scale(n.Dot(t,this.m_body.GetLinearVelocity()))}updateFriction(){let t=this.getLateralVelocity().Scale(-1*this.m_body.GetMass());t.Length()>this.m_maxLateralImpulse&&t.Scale(this.m_maxLateralImpulse/t.Length()),this.m_body.ApplyLinearImpulse(t.Scale(this.m_currentTraction),this.m_body.GetWorldCenter()),this.m_body.ApplyAngularImpulse(this.m_currentTraction*.1*this.m_body.GetInertia()*-this.m_body.GetAngularVelocity());let e=this.getForwardVelocity(),o=-2*e.Normalize();this.m_body.ApplyForce(e.Scale(this.m_currentTraction*o),this.m_body.GetWorldCenter())}updateDrive(t){if(t.up===t.down)return;let e=0;t.up?e=this.m_maxForwardSpeed:t.down&&(e=this.m_maxBackwardSpeed);let r=this.m_body.GetWorldVector(new n(0,1),new n),o=n.Dot(this.getForwardVelocity(),r),s=0;if(e>o)s=this.m_maxDriveForce;else if(e{u.updateFriction()}),this.m_tires.forEach(u=>{u.updateDrive(t)});let e=35*xu,o=160*xu/60,s=0;t.left&&(s+=e),t.right&&(s-=e);let l=this.flJoint.GetJointAngle(),a=s-l;a=tt(a,-o,o);let c=l+a;this.flJoint.SetLimits(c,c),this.frJoint.SetLimits(c,c)}},Ef=class i extends R{constructor(){super(n.ZERO);this.m_controlState={left:!1,right:!1,up:!1,down:!1};{this.m_groundBody=this.m_world.CreateBody();let e=new v,r={shape:e,isSensor:!0,userData:{groundArea:new gu(.5,!1)}};e.SetAsBox(9,7,new n(-10,15),20*xu),this.m_groundBody.CreateFixture(r),e.SetAsBox(9,5,new n(5,20),-40*xu),r.userData={groundArea:new gu(.2,!1)},this.m_groundBody.CreateFixture(r)}this.m_car=new Vf(this.m_world)}getHotkeys(){return[Er("a","Turn Left",this.m_controlState,"left"),Er("d","Turn Right",this.m_controlState,"right"),Er("w","Move Forward",this.m_controlState,"up"),Er("s","Move Backward",this.m_controlState,"down")]}static handleContact(e,r){let o=e.GetFixtureA(),s=e.GetFixtureB(),l=o.GetUserData(),a=s.GetUserData();l.tire||a.groundArea?i.tire_vs_groundArea(o,s,r):(l.groundArea||a.tire)&&i.tire_vs_groundArea(s,o,r)}BeginContact(e){i.handleContact(e,!0)}EndContact(e){i.handleContact(e,!1)}static tire_vs_groundArea(e,r,o){let{tire:s}=e.GetBody().GetUserData(),{groundArea:l}=r.GetUserData();s&&l&&(o?s.addGroundArea(l):s.removeGroundArea(l))}Step(e,r){this.m_car.update(this.m_controlState),super.Step(e,r)}};P("Examples","TopDown Car",Ef);var Uf=class extends R{constructor(){super();let t=this.m_world.CreateBody(),e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e});let r={x:0,y:5},o=this.m_world.CreateBody({type:2,angularDamping:.1,position:r}),s=new v;s.SetAsBox(.5,.5),o.CreateFixture({shape:s,density:5}),this.m_hertz=1,this.m_dampingRatio=.7;let l=new ke;l.Initialize(t,o,new n(0,15),r),l.collideConnected=!0,this.m_length=l.length,this.m_minLength=this.m_length,this.m_maxLength=this.m_length,Ct(l,this.m_hertz,this.m_dampingRatio,l.bodyA,l.bodyB),this.m_joint=this.m_world.CreateJoint(l)}setupControls(){this.addTestControlGroup("Joint",[_t("Length",0,20,1,this.m_length,t=>{this.m_length=this.m_joint.SetLength(t)}),_t("Min Length",0,20,1,this.m_minLength,t=>{this.m_minLength=this.m_joint.SetMinLength(t)}),_t("Max Length",0,20,1,this.m_maxLength,t=>{this.m_maxLength=this.m_joint.SetMaxLength(t)}),_t("Hertz",0,10,.1,this.m_hertz,t=>{this.m_hertz=t,this.UpdateStiffness()}),_t("Damping Ratio",0,2,.1,this.m_dampingRatio,t=>{this.m_dampingRatio=t,this.UpdateStiffness()})])}UpdateStiffness(){let t={stiffness:0,damping:0};Ct(t,this.m_hertz,this.m_dampingRatio,this.m_joint.GetBodyA(),this.m_joint.GetBodyB()),this.m_joint.SetStiffness(t.stiffness),this.m_joint.SetDamping(t.damping)}};P("Joints","Distance Joint",Uf);var Yf=class extends R{constructor(){super();this.m_motorSpeed=10;this.m_enableMotor=!1;this.m_enableLimit=!0;let e=this.m_world.CreateBody();{let r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r,density:0})}{let r=new V(2),o=new n(0,10),s=this.m_world.CreateBody({type:2,position:o,allowSleep:!1});s.CreateFixture({shape:r,density:5});let l=new cs;l.Initialize(e,s,o,new n(0,1)),l.motorSpeed=this.m_motorSpeed,l.maxMotorTorque=1e4,l.enableMotor=this.m_enableMotor,l.lowerTranslation=-3,l.upperTranslation=3,l.enableLimit=this.m_enableLimit,Ct(l,1,.7,e,s),this.m_joint=this.m_world.CreateJoint(l)}this.addTestControlGroup("Joint",[jt("Limit",this.m_enableLimit,r=>{this.m_enableLimit=this.m_joint.EnableLimit(r)}),jt("Motor",this.m_enableMotor,r=>{this.m_enableMotor=this.m_joint.EnableMotor(r)}),_t("Speed",-100,100,1,this.m_motorSpeed,r=>{this.m_motorSpeed=this.m_joint.SetMotorSpeed(r)})])}Step(e,r){super.Step(e,r);let o=this.m_joint.GetMotorTorque(e.m_hertz);this.addDebug("Motor Torque",o);let s=this.m_joint.GetReactionForce(e.m_hertz,new n);this.addDebug("Reaction Force",`(${s.x.toFixed(1)}, ${s.y.toFixed(1)})`)}};P("Joints","Wheel",Yf);var Jf=class extends R{constructor(){super();this.m_distanceJointDef=new ke;this.m_stabilize=!1;let e=this.m_world.CreateBody();{let r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r,density:0})}{let r=new v;r.SetAsBox(.5,.125);let o={shape:r,density:20,friction:.2,filter:{categoryBits:1,maskBits:65533}},s=new q;s.collideConnected=!1;let l=10,a=15;this.m_distanceJointDef.localAnchorA.Set(0,a);let c=e;for(let m=0;m{this.m_stabilize=e,e&&this.m_distanceJoint===null?this.m_distanceJoint=this.m_world.CreateJoint(this.m_distanceJointDef):!e&&this.m_distanceJoint!==null&&(this.m_world.DestroyJoint(this.m_distanceJoint),this.m_distanceJoint=null)})])}Step(e,r){super.Step(e,r),this.addDebug("Distance Joint",this.m_distanceJoint?"ON":"OFF")}};P("Examples","Wrecking Ball",Jf);var jf=class extends R{constructor(){super();let t=new n(0,-10);this.m_world.SetGravity(t);let e=[];e[0]=this.m_world.CreateBody({type:0});{let r=new n(0,1),o=new n(0,0),s=new n(4,0),l=new I;l.SetTwoSided(r,o),e[0].CreateFixture({shape:l}),l.SetTwoSided(o,s),e[0].CreateFixture({shape:l})}e[1]=this.m_world.CreateBody({type:2,position:new n(1,3)});{let r=new v,o=H(8,n);o[0].Set(.5,-3),o[1].Set(.5,3),o[2].Set(-.5,3),o[3].Set(-.5,-3),r.Set(o,4),e[1].CreateFixture({shape:r,friction:.2,density:10})}}};P("Bugs","Chain Problem",jf);var zf=class extends R{constructor(){super();let t=25,e=this.m_world.CreateBody();{let m=new v;m.SetAsBox(.5,2),e.CreateFixture({shape:m}),e=this.m_world.CreateBody({angle:.5*Math.PI,position:{x:t,y:2*t}}),m.SetAsBox(2*t,1.2),e.CreateFixture({shape:m}),e=this.m_world.CreateBody({angle:.5*Math.PI,position:{x:-t,y:2*t}}),e.CreateFixture({shape:m})}let r=26,s=.5*2,l=s*r/2,a=s/2,c=new v;c.SetAsBox(.5,.5);let u=5*r;for(let m=0;m{};this.setLeftTable=()=>{};this.setRightTable=()=>{};this.setTestControlGroups=()=>{};this.particleParameter=new su(this);for(let{tests:t}of this.groupedTests)this.flatTests.push(...t);this.ownHotKeys=[G("0","Reset Camera",()=>this.HomeCamera()),G("+","Zoom In",()=>this.ZoomCamera(1.1)),G("-","Zoom Out",()=>this.ZoomCamera(.9)),G("r","Reload Test",()=>this.LoadTest()),G("o","Single Step",()=>this.SingleStep()),G("p","Pause/Continue",()=>this.SetPause(!this.m_settings.m_pause)),G("PageUp","Previous Test",()=>this.DecrementTest()),G("PageDown","Next Test",()=>this.IncrementTest())]}init(t,e,r,o,s,l,a){this.setLeftTable=s,this.setRightTable=l,this.activateTest=o,this.setTestControlGroups=a,e.addEventListener("mousedown",u=>this.HandleMouseDown(u)),e.addEventListener("mouseup",u=>this.HandleMouseUp(u)),e.addEventListener("mousemove",u=>this.HandleMouseMove(u)),e.addEventListener("wheel",u=>this.HandleMouseWheel(u)),e.addEventListener("mouseenter",()=>{this.m_hoveringCanvas=!0}),e.addEventListener("mouseleave",()=>{this.m_hoveringCanvas=!1});let c=()=>{let{clientWidth:u,clientHeight:m}=r;(e.width!==u||e.height!==m)&&(e.width=t.width=u,e.height=t.height=m,ot.resize(u,m),this.m_test?.Resize(u,m),this.gl&&vd(t,this.gl,u,r.clientHeight))};if(window.addEventListener("resize",c),window.addEventListener("orientationchange",c),c(),this.m_ctx=e.getContext("2d"),!this.m_ctx)throw new Error("Could not create 2d context for debug-draw");this.m_settings.m_debugDraw=new Fn(this.m_ctx),window.addEventListener("contextmenu",u=>{u.target instanceof HTMLElement&&u.target.closest("main")&&u.preventDefault()},!0),window.addEventListener("keydown",u=>this.HandleKey(u,!0)),window.addEventListener("keyup",u=>this.HandleKey(u,!1)),this.LoadTest(),this.prepareGl(t)}async prepareGl(t){this.gl=W0(t),this.textures=await O0(this.gl),this.defaultShader=q0(this.gl),this.LoadTest()}setTest(t,e){this.testTitle=t,this.testConstructor=e,this.LoadTest()}HomeCamera(){let t=this.m_test?this.m_test.GetDefaultViewZoom():25,e=this.m_test?this.m_test.getCenter():n.ZERO;ot.setPositionAndZoom(e.x,e.y,t)}ZoomCamera(t){ot.setZoom(tt(ot.getZoom()*t,.5,500))}HandleMouseMove(t){let e=new n(t.offsetX,t.offsetY),r=ot.unproject(e,new n);if(this.m_mouse.Copy(e),this.m_test?.MouseMove(r,this.m_lMouseDown),this.m_rMouseDown){let{x:o,y:s}=ot.getCenter(),l=1/ot.getZoom();ot.setPosition(o-t.movementX*l,s+t.movementY*l)}}HandleMouseDown(t){let e=new n(t.offsetX,t.offsetY),r=ot.unproject(e,new n);switch(t.button){case 0:this.m_lMouseDown=!0,t.shiftKey?this.m_test?.ShiftMouseDown(r):this.m_test?.MouseDown(r);break;case 2:this.m_rMouseDown=!0;break}}HandleMouseUp(t){let e=new n(t.offsetX,t.offsetY),r=ot.unproject(e,new n);switch(t.button){case 0:this.m_lMouseDown=!1,this.m_test?.MouseUp(r);break;case 2:this.m_rMouseDown=!1;break}}HandleMouseWheel(t){this.m_hoveringCanvas&&(t.deltaY<0?this.ZoomCamera(1.1):t.deltaY>0&&this.ZoomCamera(1/1.1),t.preventDefault())}HandleKey(t,e){if(this.m_hoveringCanvas||!e){let{key:r}=t,o=this.allHotKeys.find(s=>s.key===r);o&&(!!this.m_keyMap[r]!==e&&(o.step||o.callback(e),this.m_keyMap[r]=e),this.m_hoveringCanvas&&t.preventDefault())}}DecrementTest(){let t=this.flatTests.findIndex(e=>e.name===this.testTitle)-1;t<0?this.activateTest(this.flatTests[this.flatTests.length-1]):t>=0&&this.activateTest(this.flatTests[t])}IncrementTest(){let t=this.flatTests.findIndex(e=>e.name===this.testTitle)+1;t>=this.flatTests.length?this.activateTest(this.flatTests[0]):t>0&&this.activateTest(this.flatTests[t])}LoadTest(t=!1){let e=this.testConstructor;if(!(!e||!this.m_ctx||!this.gl||!this.defaultShader||!this.textures||!this.m_settings.m_debugDraw)){t||this.particleParameter.Reset(),this.m_test?.Destroy(),this.m_test=new e({gl:this.gl,shader:this.defaultShader,textures:this.textures,particleParameter:this.particleParameter}),this.m_test.setupControls(),this.testBaseHotKeys=this.m_test.getBaseHotkeys(),this.testHotKeys=this.m_test.getHotkeys(),this.allHotKeys=[...this.ownHotKeys,...this.testBaseHotKeys,...this.testHotKeys],this.stepHotKeys=this.allHotKeys.filter(r=>r.step);for(let r of this.allHotKeys){let o=this.allHotKeys.find(s=>r.key===s.key);o&&r!==o&&console.error(`Conflicting keys "${r.description}" and "${o.description}"`)}t||this.HomeCamera(),this.setTestControlGroups(this.m_test.m_testControlGroups.slice())}}SetPause(t){this.m_settings.m_pause=t,this.onPauseChanged.emit(t)}SingleStep(){this.m_settings.m_pause||(this.m_settings.m_pause=!0,this.onPauseChanged.emit(!0)),this.m_settings.m_singleStep=!0}scheduleRestart(){this.shouldRestart=!0}SimulationLoop(){let t=this.m_settings.m_debugDraw;if(this.m_fpsCalculator.addFrame()<=0||!this.gl||!this.defaultShader||!this.m_ctx||!t)return;ds(this.gl,0,0,0,0),this.gl.enable(this.gl.BLEND),this.defaultShader.use(),this.defaultShader.uMVMatrix.set(!1,ot.modelView),this.defaultShader.uPMatrix.set(!1,ot.projection);let e=ot.getCenter(),r=ot.getZoom();if(t.Prepare(e.x,e.y,r,!0),this.m_test?.RunStep(this.m_settings),this.m_hoveringCanvas)for(let o of this.stepHotKeys)this.m_keyMap[o.key]&&o.callback(!0);t.Finish(),this.m_settings.m_drawFpsMeter&&this.DrawFpsMeter(this.m_ctx),this.UpdateText(),this.shouldRestart&&(this.shouldRestart=!1,this.LoadTest(!0))}DrawFpsMeter(t){t.save(),t.translate(0,ot.getHeight()),t.scale(1,-1),t.fillStyle=Fn.MakeStyleString(F.GREEN);let e=5;for(let r of this.m_fpsCalculator.getFrames())t.fillRect(e,5,1,r),e++;t.restore()}UpdateText(){let t=[],e=this.m_fpsCalculator.getFps(),r=[["Performance:","!"],["Avg. FPS",e.avgFps.toFixed(1)],["Max. Time in ms",e.maxTime.toFixed(1)],["Min. Time in ms",e.minTime.toFixed(1)],["",""]];this.m_test&&(this.m_test.m_textLines.length&&t.push(["Description:","!"],...this.m_test.m_textLines.map(o=>[o,"-"]),["",""]),this.m_settings.m_drawInputHelp&&(t.push(["Mouse:","!"],["Right Drag","Move Camera"],["Left Drag","Grab Objects"],["Wheel","Zoom"],["",""]),t.push(["Keyboard:","!"],...this.allHotKeys.map(o=>[yA(o),o.description]),["",""])),this.m_test.m_debugLines.length&&r.push(["Debug Info:","!"],...this.m_test.m_debugLines,["",""]),this.m_test.m_statisticLines.length&&r.push(["Statistics:","!"],...this.m_test.m_statisticLines,["",""])),this.setLeftTable(t),this.setRightTable(r)}},xA=(0,Su.createContext)(new Xf),tl=()=>(0,Su.useContext)(xA);var Rx=/[^a-z0-9-]+/gi,sr=({group:i,name:t})=>`/${i.replace(Rx,"_")}#${t.replace(Rx,"_")}`;function kx(i,t){return JSON.stringify(i)!==JSON.stringify(t)?t:i}var gA=({label:i,value:t})=>t==="!"?ft.default.createElement("tr",null,ft.default.createElement("th",{colSpan:2},i)):t==="-"?ft.default.createElement("tr",null,ft.default.createElement("td",{colSpan:2},i)):ft.default.createElement("tr",null,ft.default.createElement("td",null,t),ft.default.createElement("td",null,i)),Fx=({id:i,table:t})=>ft.default.createElement("div",{id:i},ft.default.createElement("table",null,ft.default.createElement("tbody",null,t.map(([e,r],o)=>ft.default.createElement(gA,{key:o,label:e,value:r}))))),SA=({entry:{name:i,TestClass:t},setTestControlGroups:e})=>{let[r,o]=(0,ft.useReducer)(kx,[]),[s,l]=(0,ft.useReducer)(kx,[]),a=(0,ft.useRef)(null),c=(0,ft.useRef)(null),u=(0,ft.useRef)(null),m=tl(),p=Zi(),d=Ix();return(0,ft.useEffect)(()=>{let h=a.current,f=c.current,b=u.current;if(h&&f&&b){let x=()=>{try{m.SimulationLoop(),window.requestAnimationFrame(x)}catch(y){console.error("Error during simulation loop",y)}},_=()=>{let y=g=>{p.history.push(sr(g))};m.init(h,f,b,y,o,l,e),window.requestAnimationFrame(x)};window.requestAnimationFrame(_)}},[c.current,a.current,u.current,m]),(0,ft.useEffect)(()=>{m.setTest(i,t)},[m,t]),ft.default.createElement("main",{ref:u},ft.default.createElement("canvas",{ref:a}),ft.default.createElement("canvas",{ref:c}),ft.default.createElement(Fx,{id:"left_overlay",table:r}),ft.default.createElement("div",{id:"title_overlay"},d?.name??""),ft.default.createElement(Fx,{id:"right_overlay",table:s}))};function Ix(){let i=Zi(),t=decodeURIComponent(i.path);return tl().flatTests.find(r=>sr(r)===t)}var Lx=({setTestControlGroups:i})=>{let t=Ix();return t?ft.default.createElement(SA,{entry:t,setTestControlGroups:i}):ft.default.createElement("main",null,"Select a test from the right sidebar")};var At=mt(Ut());var Or=mt(Ut());var Gx=mt(Ut());var Vx=()=>Gx.default.createElement("div",{className:"separator"});var vs=mt(Ut());var wu=({className:i,legend:t,legendClassName:e,children:r,defaultOpen:o=!1})=>{let[s,l]=(0,vs.useState)(o),a=[];return e&&a.push(e),s&&a.push("open-legend"),vs.default.createElement("fieldset",{className:`section ${i??""}`},vs.default.createElement("legend",{onClick:()=>l(!s),tabIndex:0,className:a.join(" ")},t),vs.default.createElement("div",{className:s?"section-content":"section-content section-content-hidden"},r))};var wA=({control:i})=>{switch(i.type){case"slider":return Or.default.createElement(Cx,{control:i});case"checkbox":return Or.default.createElement(px,{control:i});case"radio":return Or.default.createElement(Bx,{control:i});case"select":return Or.default.createElement(mx,{control:i});case"separator":return Or.default.createElement(Vx,null)}return null},Bs=({legend:i,controls:t,defaultOpen:e})=>Or.default.createElement(wu,{legend:i,defaultOpen:e,className:"settings-section"},t.map(r=>Or.default.createElement(wA,{key:r.name,control:r})));function re(i,t,e){let r=i[t];return jt(e,r,o=>{i[t]=o})}function el(i,t,e,r,o,s){let l=i[t];return _t(e,r,o,s,l,a=>{i[t]=a})}var Ex=mt(Ut());var vu=({label:i,onClick:t})=>Ex.default.createElement("button",{className:"button",onClick:t},i);var Hf=mt(Ut());var Ux=({name:i,link:t,tests:e})=>{let r=e.some(o=>t===sr(o));return Hf.default.createElement(wu,{legend:i,legendClassName:r?"active-legend":""},e.map(o=>Hf.default.createElement(I2,{href:sr(o),key:o.name,className:t===sr(o)?"active-link":""},o.name)))};var Yx=({testControlGroups:i})=>{let[t,e]=(0,At.useState)("controls"),[r,o]=(0,At.useState)(!1),s=tl(),l=Zi(),a=decodeURIComponent(l.path),c=(0,At.useMemo)(()=>s.groupedTests.some(f=>f.tests.some(b=>a===sr(b))),[s,a]);(0,At.useEffect)(()=>{let f=s.onPauseChanged.connect(o);return()=>{f.disconnect()}}),(0,At.useEffect)(()=>{!c&&t!=="tests"&&e("tests")},[c,t]);let u=s.m_settings,m=[el(u,"m_velocityIterations","Velocity Iters",0,50,1),el(u,"m_positionIterations","Position Iters",0,50,1),el(u,"m_particleIterations","Particle Iters",0,50,1),el(u,"m_hertz","Hertz",5,120,1)],p=[re(u,"m_enableSleep","Sleep"),re(u,"m_enableWarmStarting","Warm Starting"),re(u,"m_enableContinuous","Time of Impact"),re(u,"m_enableSubStepping","Sub-Stepping")],d=[re(u,"m_drawShapes","Shapes"),re(u,"m_drawParticles","Particles"),re(u,"m_drawJoints","Joints"),re(u,"m_drawAABBs","AABBs"),re(u,"m_drawContactPoints","Contact Points"),re(u,"m_drawContactNormals","Contact Normals"),re(u,"m_drawContactImpulse","Contact Impulses"),re(u,"m_drawFrictionImpulse","Friction Impulses"),re(u,"m_drawCOMs","Center of Masses")],h=[re(u,"m_drawStats","Statistics"),re(u,"m_drawInputHelp","Input Help"),re(u,"m_drawProfile","Profile"),re(u,"m_drawFpsMeter","FPS Meter")];return At.default.createElement("div",{className:"sidebar"},At.default.createElement("div",{className:"sidebar--tabs"},At.default.createElement("div",{onClick:()=>e("controls"),className:t==="controls"?"active-tab":""},"Controls"),At.default.createElement("div",{onClick:()=>e("tests"),className:t==="tests"?"active-tab":""},"Tests")),At.default.createElement("div",{className:t==="controls"?"tab-content":"tab-content tab-content-hidden"},At.default.createElement(Bs,{legend:"Iterations",controls:m}),At.default.createElement(Bs,{legend:"General",controls:p}),At.default.createElement(Bs,{legend:"Draw",controls:d}),At.default.createElement(Bs,{legend:"Overlay",controls:h}),i.groups.map((f,b)=>At.default.createElement(Bs,{defaultOpen:!0,legend:`[Test] ${f.legend}`,key:`${i.key}-${b}`,controls:f.controls}))),At.default.createElement("div",{className:t==="tests"?"tab-content":"tab-content tab-content-hidden"},s.groupedTests.map(({name:f,tests:b})=>At.default.createElement(Ux,{key:f,name:f,tests:b,link:a}))),t==="controls"&&At.default.createElement("div",{className:"sidebar--buttons"},At.default.createElement(vu,{label:r?"Continue (P)":"Pause (P)",onClick:()=>s.SetPause(!r)}),At.default.createElement(vu,{label:"Single Step (O)",onClick:()=>s.SingleStep()}),At.default.createElement(vu,{label:"Restart (R)",onClick:()=>s.LoadTest(!0)})))};var Jx={name:"@box2d/testbed",version:"0.10.0",private:!0,description:"A Testbed for all @box2d packages",homepage:"https://lusito.github.io/box2d.ts/",bugs:{url:"https://github.com/lusito/box2d.ts/issues"},repository:{type:"git",url:"https://github.com/lusito/box2d.ts.git"},license:"MIT",author:"Santo Pfingsten",files:["dist"],scripts:{build:"rimraf dist && node src/ui/esbuild.js",start:"node src/ui/esbuild.js serve"},browserslist:["> 5%"],dependencies:{"@box2d/controllers":"^0.10.0","@box2d/core":"^0.10.0","@box2d/debug-draw":"^0.10.0","@box2d/lights":"^0.10.0","@box2d/particles":"^0.10.0","@react-nano/router":"^0.15.0","@types/randomcolor":"^0.5.9","@types/react":"^18.3.3","@types/react-dom":"^18.3.0",esbuild:"^0.21.5","esbuild-sass-plugin":"^3.3.1","esbuild-ts-paths":"^1.1.3","gl-matrix":"^3.4.3",react:"^18.3.1","react-dom":"^18.3.1","sort-package-json":"^2.10.0","typed-glsl":"0.11.2","typed-signals":"^3.0.0","typeface-open-sans":"1.1.13"}};var BA={key:0,groups:[]};function CA(i,t){return{key:i.key+1,groups:t}}function AA(){let[i,t]=(0,Nr.useReducer)(CA,BA);return Nr.default.createElement("div",{className:"container"},Nr.default.createElement(Lx,{setTestControlGroups:t}),Nr.default.createElement(Yx,{testControlGroups:i}))}document.title=`@Box2D Testbed v${Jx.version}`;var TA=(0,jx.createRoot)(document.getElementById("root"));TA.render(Nr.default.createElement(U2,{mode:"hash"},Nr.default.createElement(AA,null)));})(); +`;function _x(i){return(0,Zn.createShaderProgram)(i,nA,lA,{a_position:"vertexAttribPointer",a_texCoord:"vertexAttribPointer",u_texture:"uniform1i",ambient:"uniform4f"})}var pu=class{constructor(t,e,r){this.lightMapDrawingDisabled=!1;this.rayHandler=t,this.gl=t.gl,this.vertBuffer=new si(this.gl,this.gl.STATIC_DRAW,this.createVertices()),this.uvBuffer=new si(this.gl,this.gl.STATIC_DRAW,this.createUvCoords()),e<=0&&(e=1),r<=0&&(r=1),this.frameBuffer=new Yn(this.gl,e,r),this.pingPongBuffer=new Yn(this.gl,e,r),this.shadowShader=bx(this.gl),this.diffuseShader=dx(this.gl),this.withoutShadowShader=_x(this.gl),this.blurShader=hx(this.gl,e,r,fe.isDiffuse)}render(){let t=this.rayHandler.lightRenderedLastFrame>0;if(!this.lightMapDrawingDisabled){if(this.frameBuffer.bindTexture(),this.rayHandler.shadows){let e=this.rayHandler.ambientLight,r=this.shadowShader;fe.isDiffuse?(r=this.diffuseShader,r.use(),this.rayHandler.diffuseBlendFunc.apply(),r.ambient.set(e.r,e.g,e.b,e.a)):(r.use(),this.rayHandler.shadowBlendFunc.apply(),r.ambient.set(e.r*e.a,e.g*e.a,e.b*e.a,1-e.a)),this.renderTriangleFan(r)}else t&&(this.rayHandler.simpleBlendFunc.apply(),this.withoutShadowShader.use(),this.renderTriangleFan(this.withoutShadowShader));this.gl.disable(this.gl.BLEND)}}renderTriangleFan(t){this.vertBuffer.bind(),t.a_position.enable(),t.a_position.set(2,this.gl.FLOAT,!1,0,0),this.vertBuffer.unbind(),this.uvBuffer.bind(),t.a_texCoord.enable(),t.a_texCoord.set(2,this.gl.FLOAT,!1,0,0),this.uvBuffer.unbind(),this.gl.drawArrays(this.gl.TRIANGLE_FAN,0,4),t.a_position.disable(),t.a_texCoord.disable()}gaussianBlur(){this.gl.disable(this.gl.BLEND);for(let t=0;tt-r&&this.y1e-r}updateAndRender(){this.update(),this.render()}update(){for(let t of this.lightList)t.update()}prepareRender(){this.lightRenderedLastFrame=0,this.gl.depthMask(!1),this.gl.enable(this.gl.BLEND),this.simpleBlendFunc.apply();let t=this.shadows||this.blur;t&&(this.lightMap.frameBuffer.begin(),this.gl.clearColor(0,0,0,0),this.gl.clear(this.gl.COLOR_BUFFER_BIT));let e=this.customLightShader??this.lightShader;e.use(),e.u_projTrans.set(!1,this.combined),this.customLightShader&&this.updateLightShader();for(let r of this.lightList)this.customLightShader&&this.updateLightShaderPerLight(r),r.render();t&&(this.customViewport?this.lightMap.frameBuffer.end(this.viewportX,this.viewportY,this.viewportWidth,this.viewportHeight):this.lightMap.frameBuffer.endSimple(),this.lightRenderedLastFrame>0&&this.blur&&this.lightMap.gaussianBlur())}render(){this.prepareRender(),this.lightMap.render()}renderOnly(){this.lightMap.render()}updateLightShader(){}updateLightShaderPerLight(t){}pointAtLight(t,e){return this.lightList.some(r=>r.contains(t,e))}pointAtShadow(t,e){return!this.lightList.some(r=>r.contains(t,e))}dispose(){this.removeAll(),this.lightMap&&this.lightMap.dispose(),this.lightShader&&this.lightShader.dispose()}removeAll(){this.lightList.forEach(t=>t.dispose()),this.lightList.length=0,this.disabledLights.forEach(t=>t.dispose()),this.disabledLights.length=0}removeLight(t){let e=t.isActive()?this.lightList:this.disabledLights;zn(e,t)}setLightShader(t){this.customLightShader=t}setCulling(t){this.culling=t}setBlur(t){this.blur=t}setBlurNum(t){this.blurNum=t}setShadows(t){this.shadows=t}setAmbientLightBrightness(t){this.ambientLight.a=Math.max(0,Math.min(1,t))}setAmbientLight(t,e,r,o){this.ambientLight.set(t,e,r,o)}setAmbientLightC(t){this.ambientLight.copy(t)}useCustomViewport(t,e,r,o){this.customViewport=!0,this.viewportX=t,this.viewportY=e,this.viewportWidth=r,this.viewportHeight=o}useDefaultViewport(){this.customViewport=!1}setLightMapRendering(t){this.lightMap.lightMapDrawingDisabled=!t}getLightMapBuffer(){return this.lightMap.frameBuffer}};function _o(i){i.setColor(Math.random(),Math.random(),Math.random(),1)}var yx=[{x1:0,x2:1.625,y1:-14.0615415625,y2:-11.9990415625},{x1:2.5,x2:4.75,y1:-11.1240415625,y2:-8.6865415625},{x1:5.75,x2:7.875,y1:-7.9365415625,y2:-6.0615415625},{x1:8.6875,x2:10.5625,y1:-5.3115415625,y2:-3.5615415624999986},{x1:11.1875,x2:12.625,y1:-2.4365415624999986,y2:-.24904156249999865},{x1:13.1875,x2:14,y1:1.2509584375000014,y2:3.6259584375000014},{x1:14.1875,x2:13.875,y1:4.813458437500001,y2:7.3759584375000005},{x1:13.25,x2:11.75,y1:8.6884584375,y2:10.4384584375},{x1:10.625,x2:8.8125,y1:11.3759584375,y2:11.8759584375},{x1:7.508101249999999,x2:5.239583124999999,y1:11.9990415625,y2:11.9527453125},{x1:4.54513875,x2:2.6932868749999983,y1:11.8138565625,y2:10.8879309375},{x1:1.9988424999999985,x2:.9340274999999991,y1:10.5638565625,y2:9.4064490625},{x1:.33217562499999964,x2:.008101249999999283,y1:8.6194121875,y2:6.9990415625},{x1:0,x2:-1.625,y1:-14.0615415625,y2:-11.9990415625},{x1:-2.5,x2:-4.75,y1:-11.1240415625,y2:-8.6865415625},{x1:-5.75,x2:-7.875,y1:-7.9365415625,y2:-6.0615415625},{x1:-8.6875,x2:-10.5625,y1:-5.3115415625,y2:-3.5615415624999986},{x1:-11.1875,x2:-12.625,y1:-2.4365415624999986,y2:-.24904156249999865},{x1:-13.1875,x2:-14,y1:1.2509584375000014,y2:3.6259584375000014},{x1:-14.1875,x2:-13.875,y1:4.813458437500001,y2:7.3759584375000005},{x1:-13.25,x2:-11.75,y1:8.6884584375,y2:10.4384584375},{x1:-10.625,x2:-8.8125,y1:11.3759584375,y2:11.8759584375},{x1:-7.508101249999999,x2:-5.239583124999999,y1:11.9990415625,y2:11.9527453125},{x1:-4.54513875,x2:-2.6932868749999983,y1:11.8138565625,y2:10.8879309375},{x1:-1.9988424999999985,x2:-.9340274999999991,y1:10.5638565625,y2:9.4064490625},{x1:-.33217562499999964,x2:-.008101249999999283,y1:8.6194121875,y2:6.9990415625}];var hu=class extends du{constructor(t,e,r,o,s,l){super(e,r,o,s,l),this.world=t}createRayCastCallback(t){return(e,r)=>this.world.RayCast(e,r,(o,s,l,a)=>t.reportFixture(o.GetFilterData(),o.GetBody(),s,a))}getBodyPosition(t){return t.GetPosition()}getBodyAngle(t){return t.GetAngle()}};var gs=class extends R{constructor(e){super({x:0,y:0});this.gl=e;this.drawDebugLight=!1;this.soft=!0;this.mode="Default";this.blendFunc=new zr(e,e.SRC_ALPHA,e.ONE_MINUS_SRC_ALPHA),fe.gammaCorrection=.625,fe.isDiffuse=!0;let r=this.getViewportSize();this.rayHandler=new hu(this.m_world,e,ot.getWidth()/4,ot.getHeight()/4,r.x,r.y),this.rayHandler.setAmbientLight(0,0,0,.5),this.rayHandler.setBlurNum(3)}setupControls(){this.addTestControlGroup("Light",this.getLightControls())}getLightControls(){return[rr("Blend Mode",["Default","Over-Burn","Some Other"],this.mode,e=>{this.setBlending(e)}),jt("Debug Light Shapes",this.drawDebugLight,e=>{this.drawDebugLight=e}),jt("Soft Shadows",this.soft,e=>{this.soft=e,this.setSoft(e)})]}Resize(e,r){this.rayHandler.resizeFBO(e/4,r/4)}Destroy(){super.Destroy(),this.rayHandler.dispose(),Gt.setGlobalContactFilter(null)}setBlending(e){this.mode=e,e==="Over-Burn"?this.rayHandler.diffuseBlendFunc.set(this.gl.DST_COLOR,this.gl.SRC_COLOR):e==="Some Other"?this.rayHandler.diffuseBlendFunc.set(this.gl.SRC_COLOR,this.gl.DST_COLOR):this.rayHandler.diffuseBlendFunc.reset()}Step(e,r){return super.Step(e,r),this.clearGlCanvas(),this.blendFunc.apply(),r}clearGlCanvas(){ds(this.gl,0,0,0,1)}renderLights(e,r){let o=this.getViewportSize();if(this.rayHandler.setCombinedMatrix(ot.combined,o.x/2,o.y/2,o.x,o.y),r>0&&this.rayHandler.update(),this.rayHandler.render(),this.drawDebugLight){let s=e.m_debugDraw,l=s.DrawPolygon.bind(s);for(let a of this.rayHandler.lightList)a.debugRender(l)}}};var mA=512,pA=32,xh=class extends gs{constructor({gl:e,shader:r,textures:o}){super(e);this.lights=[];this.currentEdgeFixture=null;this.currentEdgeBody=null;this.dragStart=new n;this.gl=e,this.shader=r,this.textures=o,this.mouseLight=this.createLight(),this.mouseLight.setColor(1,0,0,1);let s=this.m_world.CreateBody();for(let l of yx){let a=new I;a.SetTwoSided({x:l.x1,y:l.y1},{x:l.x2,y:l.y2}),s.CreateFixture({shape:a,density:0,restitution:0})}}getViewportSize(){return{x:ot.getWidth(),y:ot.getHeight()}}setSoft(e){this.mouseLight.setSoft(e);for(let r of this.lights)r.setSoft(e)}createLight(){let e=new xs(this.rayHandler,mA,Gt.DefaultColor,pA,0,0);return _o(e),e.setPositionV(this.m_mouseWorld),e.setSoft(this.soft),e}GetDefaultViewZoom(){return 30}getHotkeys(){return[G("a","Place current light",()=>{this.lights.push(this.mouseLight),this.mouseLight=this.createLight()})]}MouseDown(e){super.MouseDown(e),this.dragStart.Copy(e)}MouseUp(e){super.MouseUp(e),this.currentEdgeFixture=null,this.currentEdgeBody=null}MouseMove(e,r){if(super.MouseMove(e,r),r){if(!this.currentEdgeBody){let s=1/ot.getZoom();this.currentEdgeBody=this.m_world.CreateBody({position:{x:-s,y:s}})}this.currentEdgeFixture&&this.currentEdgeBody.DestroyFixture(this.currentEdgeFixture);let o=new I;o.SetTwoSided(this.dragStart,e),this.currentEdgeFixture=this.currentEdgeBody.CreateFixture({shape:o,density:0,restitution:0})}}Step(e,r){super.Step(e,r),this.addText("Left drag to draw lines"),this.mouseLight.setPositionV(this.m_mouseWorld);let o=ot.getCenter();return this.rayHandler.setCombinedMatrix(ot.combined,o.x,o.y,ot.getWidth(),ot.getHeight()),this.renderLights(e,r),r}clearGlCanvas(){ds(this.gl,1,1,1,1)}};P("Lights","Draw World",xh);function xx(i,t,e,r,o){let s=t+r,l=e+o;return i.set([t,l,t,e,s,l,s,l,t,e,s,e]),i}function gx(i,t,e,r,o){return i.set([o,r,o,t,e,r,e,r,o,t,e,t]),i}function Sx(i,t,e,r,o,s,l,a,c=1){let u=t+l,m=e+a,p=-l*c,d=-a*c,h=(r-l)*c,f=(o-a)*c,b=s*fs,x=Math.cos(b),_=Math.sin(b),y=x*p-_*f+u,g=_*p+x*f+m,S=x*p-_*d+u,w=_*p+x*d+m,B=x*h-_*f+u,C=_*h+x*f+m,A=S+(B-y),k=C-(g-w);return i.set([y,g,S,w,B,C,B,C,S,w,A,k]),i}var fu=new Float32Array(12),Qn=class{constructor(t,e,r){this.uvOffsetX=0;this.uvOffsetY=0;this.gl=t,this.shader=e,this.texture=r,this.vertBuffer=new si(t,t.DYNAMIC_DRAW,fu),this.uvBuffer=new si(t,t.STATIC_DRAW,gx(fu,0,1,1,0))}setUvOffset(t,e){this.uvOffsetX=t,this.uvOffsetY=e}destroy(){this.vertBuffer.dispose(),this.uvBuffer.dispose()}isDone(){return!0}setRect(t,e,r,o){this.vertBuffer.setData(xx(fu,t,e,r,o))}setRotatedRect(t,e,r,o,s,l,a,c=1){this.vertBuffer.setData(Sx(fu,t,e,r,o,s,l,a,c))}render(){this.vertBuffer.bind(),this.shader.position.enable(),this.shader.position.set(2,this.gl.FLOAT,!1,0,0),this.vertBuffer.unbind(),this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,this.texture),this.uvBuffer.bind(),this.shader.uv.enable(),this.shader.uv.set(2,this.gl.FLOAT,!1,0,0),this.uvBuffer.unbind(),this.shader.opacity.set(1),this.shader.uvOffset.set(this.uvOffsetX,this.uvOffsetY),this.shader.textureID.set(0),this.gl.drawArrays(this.gl.TRIANGLES,0,6)}};var gh=i=>1<{let c=this.m_world.CreateBody({type:2,position:{x:4+Math.random()*40,y:4+Math.random()*25}});return c.CreateFixture(l).SetFilterData({categoryBits:Hr.MARBLE,maskBits:Sh.MARBLE}),new wh(new Qn(e,r,o.marble.texture),c)};this.marbles=Array.from({length:5},a),this.bg.setRect(0,0,Wn,Kn),Gt.setGlobalContactFilterBits(Hr.LIGHT,0,Sh.LIGHT),this.setLightsType(this.lightsType)}getLightControls(){return[rr("Lights Type",["Point","Cone","Chain","Directional"],this.lightsType,e=>{this.setLightsType(e)}),...super.getLightControls()]}getViewportSize(){return{x:Wn,y:Kn}}setSoft(e){this.directionalLight?.setSoft(e);for(let r of this.marbles)r.light?.setSoft(e)}GetDefaultViewZoom(){return 20}getCenter(){return{x:Wn/2,y:Kn/2}}Destroy(){this.bg.destroy();for(let e of this.marbles)e.light.dispose(),e.sprite.destroy();super.Destroy()}setLightsType(e){switch(e){case"Point":this.initPointLights();break;case"Cone":this.initConeLights();break;case"Chain":this.initChainLights();break;case"Directional":this.initDirectionalLight();break}this.lightsType=e}getHotkeys(){return[G("c","Random Light Colors",()=>{for(let e of this.marbles)_o(e.light)}),G("d","Random Light Distance",()=>{for(let e of this.marbles)e.light.setDistance($n(qn*.5,qn*2))})]}Step(e,r){super.Step(e,r),this.bg.render();for(let o of this.marbles)o.render();return this.directionalLight&&(this.sunDirection+=r*.008,this.directionalLight.setDirection(this.sunDirection)),this.renderLights(e,r),r}createBoundary(){let e=new dt;e.CreateLoop([new n,new n(0,Kn),new n(Wn,Kn),new n(Wn,0)]),this.groundBody=this.m_world.CreateBody({type:0}),this.groundBody.CreateFixture({shape:e,density:0}).SetFilterData({categoryBits:Hr.WORLD,maskBits:Sh.WORLD})}clearLights(){this.directionalLight&&(this.directionalLight.remove(),this.directionalLight=null);for(let e of this.marbles)e.light?.remove()}initPointLights(){this.clearLights();for(let e of this.marbles){let r=new xs(this.rayHandler,bu,Gt.DefaultColor,qn,0,0);r.attachToBody(e.body,Gi/2,Gi/2),_o(r),e.light=r,r.setSoft(this.soft)}}initConeLights(){this.clearLights();for(let e of this.marbles){let r=new uu(this.rayHandler,bu,Gt.DefaultColor,qn,0,0,0,$n(15,40));r.attachToBody(e.body,Gi/2,Gi/2,$n(0,360)),_o(r),e.light=r,r.setSoft(this.soft)}}initChainLights(){this.clearLights();for(let e of this.marbles){let r=new cu(this.rayHandler,bu,Gt.DefaultColor,qn,1,[-5,0,0,3,5,0]);r.attachToBody(e.body,$n(0,360)),_o(r),e.light=r,r.setSoft(this.soft)}}initDirectionalLight(){this.clearLights(),this.sunDirection=$n(0,360),this.directionalLight=new mu(this.rayHandler,4*bu,Gt.DefaultColor,this.sunDirection),this.directionalLight.setSoftnessLength(5),this.directionalLight.setSoft(this.soft)}};P("Lights","Official Demo",vh);var Bh=class extends R{constructor(){super(),this.m_bodies=[];let t=new hs;this.m_controller=t,t.normal.Set(0,1),t.offset=20,t.density=2,t.linearDrag=5,t.angularDrag=2;let e=this.m_world.CreateBody();{let r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r}),r.SetTwoSided(new n(-40,0),new n(-40,25)),e.CreateFixture({shape:r}),r.SetTwoSided(new n(40,0),new n(40,25)),e.CreateFixture({shape:r})}for(let r=0;r<5;r++){let o=this.m_world.CreateBody({type:2,position:{x:Math.random()*40-20,y:Math.random()*15+5},angle:Math.random()*Math.PI}),s=new v;s.SetAsBox(Math.random()*.5+1,Math.random()*.5+1),o.CreateFixture({density:1,friction:.3,restitution:.1,shape:s}),this.m_bodies.push(o)}for(let r=0;r<5;r++){let o=this.m_world.CreateBody({type:2,position:{x:Math.random()*40-20,y:Math.random()*15+5},angle:Math.random()*Math.PI});o.CreateFixture({density:1,friction:.3,restitution:.1,shape:new V(Math.random()*.5+1)}),this.m_bodies.push(o)}for(let r=0;r<15;r++){let o=this.m_world.CreateBody({type:2,position:{x:Math.random()*40-20,y:Math.random()*15+5},angle:Math.random()*Math.PI}),s=new v;if(Math.random()>.66)s.Set([new n(-1-Math.random()*1,1+Math.random()*1),new n(-.5-Math.random()*1,-1-Math.random()*1),new n(.5+Math.random()*1,-1-Math.random()*1),new n(1+Math.random()*1,1+Math.random()*1)]);else if(Math.random()>.5){let l=[];l[0]=new n(0,1+Math.random()*1),l[2]=new n(-.5-Math.random()*1,-1-Math.random()*1),l[3]=new n(.5+Math.random()*1,-1-Math.random()*1),l[1]=new n(l[0].x+l[2].x,l[0].y+l[2].y),l[1].Scale(Math.random()/2+.8),l[4]=new n(l[3].x+l[0].x,l[3].y+l[0].y),l[4].Scale(Math.random()/2+.8),s.Set(l)}else s.Set([new n(0,1+Math.random()*1),new n(-.5-Math.random()*1,-1-Math.random()*1),new n(.5+Math.random()*1,-1-Math.random()*1)]);o.CreateFixture({density:1,friction:.3,restitution:.1,shape:s}),this.m_bodies.push(o)}{let r=this.m_world.CreateBody({type:2,position:{x:0,y:40},angle:0}),o=new v;o.SetAsBox(4,1),r.CreateFixture({density:3,shape:o}),this.m_bodies.push(r)}{let r=this.m_world.CreateBody({type:2,position:{x:0,y:30}}),o=new V(.7),s={density:2,shape:o};o.m_p.Set(3,0),r.CreateFixture(s),o.m_p.Set(-3,0),r.CreateFixture(s),o.m_p.Set(0,3),r.CreateFixture(s),o.m_p.Set(0,-3),r.CreateFixture(s),s.density=2;let l=new v;s.shape=l,l.SetAsBox(3,.2),r.CreateFixture(s),l.SetAsBox(.2,3),r.CreateFixture(s),this.m_bodies.push(r)}for(let r of this.m_bodies)this.m_controller.AddBody(r);this.m_world.AddController(this.m_controller)}getCenter(){return{x:0,y:10}}};P("Controllers","Boyancy Test",Bh);var _u=class{constructor(){this.gainP=1;this.gainI=1;this.gainD=1;this.currentError=0;this.previousError=0;this.integral=0;this.output=0}step(t){this.integral=t*(this.integral+this.currentError);let e=1/t*(this.currentError-this.previousError);this.output=this.gainP*this.currentError+this.gainI*this.integral+this.gainD*e,this.previousError=this.currentError}},Ch=class i extends R{constructor(){super({x:0,y:-30});this.targetPosition=10;this.targetPositionInterval=0;this.posAvg=0;this.angleController=new _u;this.positionController=new _u;this.angleController.gainP=1e3,this.angleController.gainI=0,this.angleController.gainD=250,this.positionController.gainP=.5,this.positionController.gainI=0,this.positionController.gainD=1.5,this.pendulumBody=this.m_world.CreateBody({type:2,position:{x:0,y:2+.5*i.PENDULUM_LENGTH}});let e=new v;e.SetAsBox(.5,.5*i.PENDULUM_LENGTH);let r={shape:e,density:1/(1*i.PENDULUM_LENGTH)};this.pendulumBody.CreateFixture(r),this.wheelBody=this.m_world.CreateBody({type:2,position:n.UNITY});let o=new V;o.m_radius=.6,r.shape=o,r.density=1/(Math.PI*.6*.6),r.friction=10,this.wheelBody.CreateFixture(r);let s=new q;s.Initialize(this.wheelBody,this.pendulumBody,{x:0,y:0}),s.localAnchorA.Set(0,0),s.localAnchorB.Set(0,-.5*i.PENDULUM_LENGTH),s.collideConnected=!1,s.enableMotor=!0,s.maxMotorTorque=40,this.wheelJoint=this.m_world.CreateJoint(s),this.groundBody=this.m_world.CreateBody({type:0,position:n.ZERO});let l=new I;l.SetTwoSided({x:-100,y:0},{x:100,y:0}),r.shape=l,r.friction=10,this.groundBody.CreateFixture(r)}static{this.PENDULUM_LENGTH=10}getCenter(){return{x:0,y:5}}Step(e,r){let o=e.m_hertz>0?1/e.m_hertz:0;e.m_pause&&!e.m_singleStep&&(o=0),super.Step(e,r),this.targetPositionInterval+=o,this.targetPositionInterval>=8&&(this.targetPositionInterval=0,this.targetPosition=this.targetPosition===10?-10:10);let s=0;{this.posAvg=(1-.4)*this.posAvg+.4*this.pendulumBody.GetPosition().x,this.positionController.currentError=this.targetPosition-this.posAvg,this.positionController.step(o);let m=this.positionController.output;m=tt(m,-10,10),s=m/this.m_world.GetGravity().y,s=tt(s,pt(-15),pt(15))}let l=this.pendulumBody.GetAngle();l=dA(l),this.angleController.currentError=s-l,this.angleController.step(o);let a=this.angleController.output;Math.abs(a)>1e3&&(a=0);let c=a/(2*Math.PI*.6);this.wheelJoint.SetMotorSpeed(c)}};function dA(i){for(;i>pt(180);)i-=pt(360);for(;ithis.ApplyForce(-50)),Vr("s","Apply Backward Force",()=>this.ApplyForce(50)),Vr("a","Apply Torque Counter-Clockwise",()=>this.m_body.ApplyTorque(10)),Vr("d","Apply Torque Clockwise",()=>this.m_body.ApplyTorque(-10))]}ApplyForce(e){let r=this.m_body.GetWorldVector(new n(0,e),new n),o=this.m_body.GetWorldPoint(new n(0,3),new n);this.m_body.ApplyForce(r,o)}getCenter(){return{x:0,y:15}}};P("Forces","Apply Force",Th);var Ph=class extends R{constructor(){super();let t=this.m_world.CreateBody({position:{x:0,y:17}});{let e=t;{let r=new v;r.SetAsBox(4,1);let o=this.m_world.CreateBody({type:2,position:{x:-8,y:20}});o.CreateFixture({shape:r,density:2});let s=new q;s.Initialize(e,o,new n(-12,20)),this.m_world.CreateJoint(s),e=o}{let r=new v;r.SetAsBox(8,1);let o=this.m_world.CreateBody({type:2,position:{x:4,y:20}});o.CreateFixture({shape:r,density:2});let s=new q;s.Initialize(e,o,new n(-4,20)),this.m_world.CreateJoint(s),e=o}{let r=new v;r.SetAsBox(3,3);let o=this.m_world.CreateBody({type:2,fixedRotation:!0,position:{x:12,y:20}});o.CreateFixture({shape:r,density:2});let s=new q;s.Initialize(e,o,new n(12,20)),this.m_world.CreateJoint(s);let l=new Zt;l.Initialize(t,o,new n(12,17),new n(1,0)),this.m_world.CreateJoint(l)}}}getCenter(){return{x:0,y:15}}};P("Examples","Slider Crank 1",Ph);var Dh=class extends R{constructor(){super();this.m_speed=0;let e=this.m_world.CreateBody();{let r=new I;r.SetTwoSided(new n(-20,0),new n(20,0)),e.CreateFixture({shape:r})}{this.m_attachment=this.m_world.CreateBody({type:2,position:{x:0,y:3}});let r=new v;r.SetAsBox(.5,2),this.m_attachment.CreateFixture({shape:r,density:2})}{this.m_platform=this.m_world.CreateBody({type:2,position:{x:-4,y:5}});let r=new v;r.SetAsBox(.5,4,new n(4,0),.5*Math.PI),this.m_platform.CreateFixture({shape:r,friction:.6,density:2});let o=new q;o.Initialize(this.m_attachment,this.m_platform,new n(0,5)),o.maxMotorTorque=50,o.enableMotor=!0,this.m_world.CreateJoint(o);let s=new Zt;s.Initialize(e,this.m_platform,new n(0,5),new n(1,0)),s.maxMotorForce=1e3,s.enableMotor=!0,s.lowerTranslation=-10,s.upperTranslation=10,s.enableLimit=!0,this.m_world.CreateJoint(s),this.m_speed=3}{let r=this.m_world.CreateBody({type:2,position:{x:0,y:8}}),o=new v;o.SetAsBox(.75,.75),r.CreateFixture({shape:o,friction:.6,density:2})}}getHotkeys(){return[G("d","Set Dynamic Body",()=>this.m_platform.SetType(2)),G("s","Set Static Body",()=>this.m_platform.SetType(0)),G("k","Set Kinematic Body",()=>{this.m_platform.SetType(1),this.m_platform.SetLinearVelocity(new n(-this.m_speed,0)),this.m_platform.SetAngularVelocity(0)})]}Step(e,r){if(this.m_platform.GetType()===1){let{p:o}=this.m_platform.GetTransform(),s=this.m_platform.GetLinearVelocity();(o.x<-10&&s.x<0||o.x>10&&s.x>0)&&this.m_platform.SetLinearVelocity(new n(-s.x,s.y))}super.Step(e,r)}};P("Examples","Body Types",Dh);var Mh=class extends R{constructor(){super();this.m_velocity=new n;this.m_angularVelocity=0;this.m_shape1=new v;this.m_shape2=new v;this.m_broke=!1;this.m_break=!1;{let e=this.m_world.CreateBody(),r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r})}this.m_body1=this.m_world.CreateBody({type:2,position:{x:0,y:40},angle:.25*Math.PI}),this.m_shape1=new v,this.m_shape1.SetAsBox(.5,.5,new n(-.5,0),0),this.m_piece1=this.m_body1.CreateFixture({shape:this.m_shape1,density:1}),this.m_shape2=new v,this.m_shape2.SetAsBox(.5,.5,new n(.5,0),0),this.m_piece2=this.m_body1.CreateFixture({shape:this.m_shape2,density:1})}static{this.e_count=7}PostSolve(e,r){if(this.m_broke)return;let o=e.GetManifold().pointCount,s=0;for(let l=0;l40&&(this.m_break=!0)}Break(){let e=this.m_piece1.GetBody(),r=e.GetWorldCenter();e.DestroyFixture(this.m_piece2);let o=this.m_world.CreateBody({type:2,position:e.GetPosition(),angle:e.GetAngle()});this.m_piece2=o.CreateFixture({shape:this.m_shape2,density:1});let s=e.GetWorldCenter(),l=o.GetWorldCenter(),a=n.AddCrossScalarVec2(this.m_velocity,this.m_angularVelocity,n.Subtract(s,r,n.s_t0),new n),c=n.AddCrossScalarVec2(this.m_velocity,this.m_angularVelocity,n.Subtract(l,r,n.s_t0),new n);e.SetAngularVelocity(this.m_angularVelocity),e.SetLinearVelocity(a),o.SetAngularVelocity(this.m_angularVelocity),o.SetLinearVelocity(c)}Step(e,r){this.m_break&&(this.Break(),this.m_broke=!0,this.m_break=!1),this.m_broke||(this.m_velocity.Copy(this.m_body1.GetLinearVelocity()),this.m_angularVelocity=this.m_body1.GetAngularVelocity()),super.Step(e,r)}};P("Examples","Breakable",Mh);var Rh=class i extends R{static{this.e_count=30}constructor(){super();let t=null;{t=this.m_world.CreateBody();let e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e})}{let e=new v;e.SetAsBox(.5,.125);let r={shape:e,density:20,friction:.2},o=new q,s=t;for(let a=0;a>1&&(this.m_middle=c),s=c}let l=new n(-15+1*i.e_count,5);o.Initialize(s,t,l),this.m_world.CreateJoint(o)}for(let e=0;e<2;++e){let r=[];r[0]=new n(-.5,0),r[1]=new n(.5,0),r[2]=new n(0,1.5);let o=new v;o.Set(r),this.m_world.CreateBody({type:2,position:{x:-8+8*e,y:12}}).CreateFixture({shape:o,density:1})}for(let e=0;e<3;++e){let r=new V;r.m_radius=.5,this.m_world.CreateBody({type:2,position:{x:-6+6*e,y:10}}).CreateFixture({shape:r,density:1})}}};P("Joints","Bridge",Rh);function vx(i,t,e){return`${i.toFixed(0)} [${t.toFixed(1)}] (${e.toFixed(0)})`}var kh=class extends R{constructor(){super();this.m_x=0;{let e=this.m_world.CreateBody(),r=new I;r.SetTwoSided(new n(-10,0),new n(10,0)),e.CreateFixture({shape:r});let o=new v;o.SetAsBox(.2,1,new n(.5,1),0),e.CreateFixture({shape:o})}{let e=new v;e.SetAsBox(2,.1),this.m_body=this.m_world.CreateBody({type:2,position:{x:0,y:4}}),this.m_body.CreateFixture({shape:e,density:1}),e.SetAsBox(.25,.25),this.m_x=.20352793,this.m_bullet=this.m_world.CreateBody({type:2,bullet:!0,position:{x:this.m_x,y:10}}),this.m_bullet.CreateFixture({shape:e,density:100}),this.m_bullet.SetLinearVelocity(new n(0,-50))}}Launch(){this.m_body.SetTransformVec(new n(0,4),0),this.m_body.SetLinearVelocity(n.ZERO),this.m_body.SetAngularVelocity(0),this.m_x=W(-1,1),this.m_bullet.SetTransformVec(new n(this.m_x,10),0),this.m_bullet.SetLinearVelocity(new n(0,-50)),this.m_bullet.SetAngularVelocity(0),$t.reset(),rt.reset()}GetDefaultViewZoom(){return 50}Step(e,r){super.Step(e,r),this.addDebug("GJK Calls [ave Iters] (max Iters)",$t.calls>0&&vx($t.calls,$t.iters/$t.calls,$t.maxIters)),this.addDebug("Toi Calls [ave Iters] (max Iters)",rt.calls>0&&vx(rt.calls,rt.iters/rt.calls,rt.maxIters)),this.addDebug("Root Toi [ave Iters] (max Iters)",rt.calls>0&&`[${(rt.rootIters/rt.calls).toFixed(1)}] (${rt.maxRootIters})`),this.m_stepCount%60===0&&this.Launch()}};P("Continuous","Bullet Test",kh);var Fh=class i extends R{static{this.e_count=8}constructor(){super();let t=null;{t=this.m_world.CreateBody();let e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e})}{let e=new v;e.SetAsBox(.5,.125);let r={shape:e,density:20},o=new po,s=t;for(let l=0;l0){let c=new n(-5+1*l,5);o.Initialize(s,a,c),this.m_world.CreateJoint(o)}s=a}}{let e=new v;e.SetAsBox(.5,.125);let r={shape:e,density:20},o=new po,s=8,l=.7,a=t;for(let c=0;c0){let m=new n(5+1*c,10);o.Initialize(a,u,m),ld(o,s,l,o.bodyA,o.bodyB),this.m_world.CreateJoint(o)}a=u}}for(let e=0;e<2;++e){let r=[];r[0]=new n(-.5,0),r[1]=new n(.5,0),r[2]=new n(0,1.5);let o=new v;o.Set(r),this.m_world.CreateBody({type:2,position:{x:-8+8*e,y:12}}).CreateFixture({shape:o,density:1})}for(let e=0;e<2;++e){let r=new V;r.m_radius=.5,this.m_world.CreateBody({type:2,position:{x:-6+6*e,y:10}}).CreateFixture({shape:r,density:1})}}};P("Joints","Cantilever",Fh);var Ih=class extends R{constructor(){super();this.m_speed=0;this.m_speed=50;let e;{e=this.m_world.CreateBody();let r=new I,o={shape:r,density:0,friction:.6};r.SetTwoSided(new n(-20,0),new n(20,0)),e.CreateFixture(o);let s=[.25,1,4,0,0,-1,-2,-2,-1.25,0],l=20,a=0,c=5;for(let u=0;u<10;++u){let m=s[u];r.SetTwoSided(new n(l,a),new n(l+c,m)),e.CreateFixture(o),a=m,l+=c}for(let u=0;u<10;++u){let m=s[u];r.SetTwoSided(new n(l,a),new n(l+c,m)),e.CreateFixture(o),a=m,l+=c}r.SetTwoSided(new n(l,0),new n(l+40,0)),e.CreateFixture(o),l+=80,r.SetTwoSided(new n(l,0),new n(l+40,0)),e.CreateFixture(o),l+=40,r.SetTwoSided(new n(l,0),new n(l+10,5)),e.CreateFixture(o),l+=20,r.SetTwoSided(new n(l,0),new n(l+40,0)),e.CreateFixture(o),l+=40,r.SetTwoSided(new n(l,0),new n(l,20)),e.CreateFixture(o)}{let r=this.m_world.CreateBody({type:2,position:{x:140,y:1}}),o=new v;o.SetAsBox(10,.25),r.CreateFixture({shape:o,density:1});let s=new q;s.Initialize(e,r,r.GetPosition()),s.lowerAngle=-8*Math.PI/180,s.upperAngle=8*Math.PI/180,s.enableLimit=!0,this.m_world.CreateJoint(s),r.ApplyAngularImpulse(100)}{let o=new v;o.SetAsBox(1,.125);let s={shape:o,density:1,friction:.6},l=new q,a=e;for(let u=0;u<20;++u){let m=this.m_world.CreateBody({type:2,position:{x:161+2*u,y:-.125}});m.CreateFixture(s);let p=new n(160+2*u,-.125);l.Initialize(a,m,p),this.m_world.CreateJoint(l),a=m}let c=new n(160+2*20,-.125);l.Initialize(a,e,c),this.m_world.CreateJoint(l)}{let r=new v;r.SetAsBox(.5,.5);let o,s=new n,l={type:2,position:s};s.Set(230,.5),o=this.m_world.CreateBody(l),o.CreateFixture({shape:r,density:.5}),s.Set(230,1.5),o=this.m_world.CreateBody(l),o.CreateFixture({shape:r,density:.5}),s.Set(230,2.5),o=this.m_world.CreateBody(l),o.CreateFixture({shape:r,density:.5}),s.Set(230,3.5),o=this.m_world.CreateBody(l),o.CreateFixture({shape:r,density:.5}),s.Set(230,4.5),o=this.m_world.CreateBody(l),o.CreateFixture({shape:r,density:.5})}{let r=new v,o=H(8,n);o[0].Set(-1.5,-.5),o[1].Set(1.5,-.5),o[2].Set(1.5,0),o[3].Set(0,.9),o[4].Set(-1.15,.9),o[5].Set(-1.5,.2),r.Set(o,6);let s=new V;s.m_radius=.4;let l=new n,a={type:2,position:l};l.Set(0,1),this.m_car=this.m_world.CreateBody(a),this.m_car.CreateFixture({shape:r,density:1});let c={shape:s,density:1,friction:.9};l.Set(-1,.35),this.m_wheel1=this.m_world.CreateBody(a),this.m_wheel1.CreateFixture(c),l.Set(1,.4),this.m_wheel2=this.m_world.CreateBody(a),this.m_wheel2.CreateFixture(c);let u=new cs,m=new n(0,1),p=this.m_wheel1.GetMass(),d=this.m_wheel2.GetMass(),h=4,f=.7,b=2*Math.PI*h;u.Initialize(this.m_car,this.m_wheel1,this.m_wheel1.GetPosition(),m),u.motorSpeed=0,u.maxMotorTorque=20,u.enableMotor=!0,u.stiffness=p*b*b,u.damping=2*p*f*b,u.lowerTranslation=-.25,u.upperTranslation=.25,u.enableLimit=!0,this.m_spring1=this.m_world.CreateJoint(u),u.Initialize(this.m_car,this.m_wheel2,this.m_wheel2.GetPosition(),m),u.motorSpeed=0,u.maxMotorTorque=10,u.enableMotor=!1,u.stiffness=d*b*b,u.damping=2*d*f*b,u.lowerTranslation=-.25,u.upperTranslation=.25,u.enableLimit=!0,this.m_spring2=this.m_world.CreateJoint(u)}}getHotkeys(){return[Gr("a","Decelerate",e=>this.m_spring1.SetMotorSpeed(e?this.m_speed:0)),Gr("d","Accelerate",e=>this.m_spring1.SetMotorSpeed(e?-this.m_speed:0))]}Step(e,r){let o=ot.getCenter();ot.setPosition(this.m_car.GetPosition().x,o.y),super.Step(e,r)}};P("Examples","Car",Ih);var Lh=class extends R{constructor(){super();this.m_angularVelocity=0;{let e=this.m_world.CreateBody(),r=new I;r.SetTwoSided(new n(-10,0),new n(10,0)),e.CreateFixture({shape:r});let o=new v;o.SetAsBox(.2,1,new n(.5,1),0),e.CreateFixture({shape:o})}{let e=new v;e.SetAsBox(2,.1),this.m_body=this.m_world.CreateBody({type:2,position:{x:0,y:20}}),this.m_body.CreateFixture({shape:e,density:1}),this.m_angularVelocity=W(-50,50),this.m_body.SetLinearVelocity(new n(0,-100)),this.m_body.SetAngularVelocity(this.m_angularVelocity)}$t.reset(),rt.reset()}Launch(){$t.reset(),rt.reset(),this.m_body.SetTransformVec(new n(0,20),0),this.m_angularVelocity=W(-50,50),this.m_body.SetLinearVelocity(new n(0,-100)),this.m_body.SetAngularVelocity(this.m_angularVelocity)}Step(e,r){super.Step(e,r),this.addDebug("GJK Calls [ave Iters] (max Iters)",$t.calls>0&&`${$t.calls.toFixed(0)} [${($t.iters/$t.calls).toFixed(1)}] (${$t.maxIters.toFixed(0)})`),this.addDebug("Toi Calls [ave Iters] (max Iters)",rt.calls>0&&`${rt.calls} [${(rt.iters/rt.calls).toFixed(1)}] (${rt.maxIters})`),this.addDebug("Toi Root [ave Iters] (max Iters)",rt.calls>0&&`${rt.calls} [${(rt.rootIters/rt.calls).toFixed(1)}] (${rt.maxRootIters})`),this.addDebug("Toi Time in ms [ave] (max)",rt.calls>0&&`[${(1e3*rt.time/rt.calls).toFixed(1)}] (${(1e3*rt.maxTime).toFixed(1)})`),this.m_stepCount%60===0&&this.Launch()}GetDefaultViewZoom(){return 50}};P("Continuous","Continuous Test",Lh);var hA=!1,Gh=class extends R{constructor(){super();let t=null;{t=this.m_world.CreateBody();let e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e})}{let e=new v;e.SetAsBox(.6,.125);let r={shape:e,density:20,friction:.2},o=new q;o.collideConnected=!1;let s=25,l=t;for(let a=0;a<30;++a){let c=this.m_world.CreateBody({type:2,position:{x:.5+a,y:s}});hA&&(a===10?r.density=0:r.density=20),c.CreateFixture(r);let u=new n(a,s);o.Initialize(l,c,u),this.m_world.CreateJoint(o),l=c}}}};P("Joints","Chain",Gh);var Vh=class extends R{constructor(){super();{let t=this.m_world.CreateBody(),e=new I;e.SetTwoSided(new n(-20,0),new n(20,0)),t.CreateFixture({shape:e})}{let t=this.m_world.CreateBody(),e=new I;e.SetTwoSided(new n(-8,1),new n(-6,1)),t.CreateFixture({shape:e}),e.SetTwoSided(new n(-6,1),new n(-4,1)),t.CreateFixture({shape:e}),e.SetTwoSided(new n(-4,1),new n(-2,1)),t.CreateFixture({shape:e})}{let t=this.m_world.CreateBody({angle:.25*Math.PI}),e=H(4,n);e[0].Set(5,7),e[1].Set(8,7),e[2].Set(7,8),e[3].Set(6,8);let r=new dt;r.CreateLoop(e,4),t.CreateFixture({shape:r})}{let t=this.m_world.CreateBody(),e=new v;e.SetAsBox(1,1,new n(4,3),0),t.CreateFixture({shape:e}),e.SetAsBox(1,1,new n(6,3),0),t.CreateFixture({shape:e}),e.SetAsBox(1,1,new n(8,3),0),t.CreateFixture({shape:e})}{let t=this.m_world.CreateBody(),e=H(4,n);e[0].Set(-1,3),e[1].Set(1,3),e[2].Set(1,5),e[3].Set(-1,5);let r=new dt;r.CreateLoop(e,4),t.CreateFixture({shape:r})}{let t=this.m_world.CreateBody({position:{x:-10,y:4}}),e=H(10,n);e[0].Set(0,0),e[1].Set(6,0),e[2].Set(6,2),e[3].Set(4,1),e[4].Set(2,2),e[5].Set(0,2),e[6].Set(-2,2),e[7].Set(-4,3),e[8].Set(-6,2),e[9].Set(-6,0);let r=new dt;r.CreateLoop(e,10),t.CreateFixture({shape:r})}{let t=this.m_world.CreateBody({position:{x:-3,y:8},type:2,fixedRotation:!0,allowSleep:!1}),e=new v;e.SetAsBox(.5,.5),t.CreateFixture({shape:e,density:20})}{let t=this.m_world.CreateBody({position:{x:-5,y:5},type:2,fixedRotation:!0,allowSleep:!1}),e=new v;e.SetAsBox(.25,.25),t.CreateFixture({shape:e,density:20})}{let t=this.m_world.CreateBody({position:{x:-5,y:8},type:2,fixedRotation:!0,allowSleep:!1}),e=0,r=Math.PI/3,o=H(6,n);for(let l=0;l<6;++l)o[l].Set(.5*Math.cos(e),.5*Math.sin(e)),e+=r;let s=new v;s.Set(o,6),t.CreateFixture({shape:s,density:20})}{let t=this.m_world.CreateBody({position:{x:3,y:5},type:2,fixedRotation:!0,allowSleep:!1}),e=new V;e.m_radius=.5,t.CreateFixture({shape:e,density:20})}{this.m_character=this.m_world.CreateBody({position:{x:-7,y:6},type:2,allowSleep:!1});let t=new V;t.m_radius=.25,this.m_character.CreateFixture({shape:t,density:20,friction:1})}}GetDefaultViewZoom(){return 30}getCenter(){return{x:-2,y:0}}Step(t,e){let r=this.m_character.GetLinearVelocity();this.m_character.SetLinearVelocity({x:-5,y:r.y}),super.Step(t,e),this.addText("This tests various character collision shapes"),this.addText("Limitation: square and hexagon can snag on aligned boxes."),this.addText("Feature: edge chains have smooth collision inside and out.")}};P("Examples","Character Collision",Vh);var Eh=class i extends R{static{this.k_smallGroup=1}static{this.k_largeGroup=-1}static{this.k_triangleCategory=2}static{this.k_boxCategory=4}static{this.k_circleCategory=8}static{this.k_triangleMask=65535}static{this.k_boxMask=65535^i.k_triangleCategory}static{this.k_circleMask=65535}constructor(){super();{let x=new I;x.SetTwoSided(new n(-40,0),new n(40,0)),this.m_world.CreateBody().CreateFixture({shape:x,friction:.3})}let t=[];t[0]=new n(-1,0),t[1]=new n(1,0),t[2]=new n(0,2);let e=new v;e.Set(t,3);let r={groupIndex:i.k_smallGroup,categoryBits:i.k_triangleCategory,maskBits:i.k_triangleMask},o={shape:e,density:1,filter:r};this.m_world.CreateBody({type:2,position:{x:-5,y:2}}).CreateFixture(o),t[0].Scale(2),t[1].Scale(2),t[2].Scale(2),e.Set(t,3),r.groupIndex=i.k_largeGroup;let l=this.m_world.CreateBody({type:2,fixedRotation:!0,position:{x:-5,y:6}});l.CreateFixture(o);{let x=this.m_world.CreateBody({type:2,position:{x:-5,y:10}}),_=new v;_.SetAsBox(.5,1),x.CreateFixture({shape:_,density:1});let y=new Zt;y.bodyA=l,y.bodyB=x,y.enableLimit=!0,y.localAnchorA.Set(0,4),y.localAnchorB.SetZero(),y.localAxisA.Set(0,1),y.lowerTranslation=-1,y.upperTranslation=1,this.m_world.CreateJoint(y)}e.SetAsBox(1,.5);let a={groupIndex:i.k_smallGroup,categoryBits:i.k_boxCategory,maskBits:i.k_boxMask},c={shape:e,density:1,restitution:.1,filter:a};this.m_world.CreateBody({type:2,position:{x:0,y:2}}).CreateFixture(c),e.SetAsBox(2,1),a.groupIndex=i.k_largeGroup,this.m_world.CreateBody({type:2,position:{x:0,y:6}}).CreateFixture(c);let p=new V;p.m_radius=1;let d={groupIndex:i.k_smallGroup,categoryBits:i.k_circleCategory,maskBits:i.k_circleMask},h={shape:p,density:1,filter:d};this.m_world.CreateBody({type:2,position:{x:5,y:2}}).CreateFixture(h),p.m_radius*=2,d.groupIndex=i.k_largeGroup,this.m_world.CreateBody({type:2,position:{x:5,y:6}}).CreateFixture(h)}};P("Examples","Collision Filtering",Eh);var Uh=class extends R{constructor(){super();{let _=new I;_.SetTwoSided(new n(-50,0),new n(50,0)),this.m_world.CreateBody().CreateFixture({shape:_})}let t=-5,e=5,r=2,o=35,s=[new n(-1,0),new n(1,0),new n(0,2)],l=new v;l.Set(s,3);let a={shape:l,density:1};this.m_world.CreateBody({type:2,position:{x:W(t,e),y:W(r,o)}}).CreateFixture(a),s[0].Scale(2),s[1].Scale(2),s[2].Scale(2),l.Set(s,3),this.m_world.CreateBody({type:2,position:{x:W(t,e),y:W(r,o)}}).CreateFixture(a),l.SetAsBox(1,.5);let m={shape:l,density:1};this.m_world.CreateBody({type:2,position:{x:W(t,e),y:W(r,o)}}).CreateFixture(m),l.SetAsBox(2,1),this.m_world.CreateBody({type:2,position:{x:W(t,e),y:W(r,o)}}).CreateFixture(m);let h=new V;h.m_radius=1;let f={shape:h,density:1};this.m_world.CreateBody({type:2,position:{x:W(t,e),y:W(r,o)}}).CreateFixture(f),h.m_radius*=2,this.m_world.CreateBody({type:2,position:{x:W(t,e),y:W(r,o)}}).CreateFixture(f)}Step(t,e){super.Step(t,e);let r=6,o=new Array(r),s=0;for(let l=0;l0&&p>0&&(p>m?o[s++]=c:o[s++]=u,s===r))break}for(let l=0;l{this.Spawn()})]}getCenter(){return{x:0,y:5}}};P("Examples","Compound Shapes",Yh);var Jh=class i extends R{static{this.e_columnCount=0}static{this.e_rowCount=0}constructor(){super(n.ZERO);{let o=this.m_world.CreateBody(),s=new I;s.SetTwoSided(new n(-10,0),new n(10,0)),o.CreateFixture({shape:s}),s.SetTwoSided(new n(-10,0),new n(-10,20)),o.CreateFixture({shape:s}),s.SetTwoSided(new n(10,0),new n(10,20)),o.CreateFixture({shape:s}),s.SetTwoSided(new n(-10,20),new n(10,20)),o.CreateFixture({shape:s})}let t=.5,e=new V;e.m_p.SetZero(),e.m_radius=t;let r={shape:e,density:1,friction:.1};for(let o=0;othis.CreateCircle())]}Step(t,e){for(let r=this.m_world.GetBodyList();r;r=r.GetNext())r.GetType(),2;this.m_stepCount===180&&(this.m_stepCount+=0),super.Step(t,e);for(let r=this.m_world.GetBodyList();r;r=r.GetNext())r.GetType(),2}};P("Solver","Confined",Jh);var jh=class i extends R{constructor(){super();this.m_test_points=[];this.m_count=0;this.m_generation=0;this.m_bulk=!1;this.m_auto=!1;this.Generate()}static{this.e_count=Kt}GetDefaultViewZoom(){return 50}Generate(){let e=Math.PI*vn(),r=new D(e);for(let o=0;o{this.m_auto=!this.m_auto}),G("b","Toggle Bulk",()=>{this.m_bulk=!this.m_bulk}),G("g","Generate a new random convex hull",()=>this.Generate())]}Step(e,r){super.Step(e,r);let o,s=!1;if(this.m_bulk){for(let a=0;a<1e4;++a)if(this.Generate(),o=An(this.m_test_points,this.m_count),o.length!==0&&(s=Tn(o,o.length),s===!1)){this.m_bulk=!1;break}}else this.m_auto&&this.Generate(),o=An(this.m_test_points,this.m_count),o.length>0&&(s=Tn(o,o.length),s===!1&&(this.m_auto=!1));let l=e.m_debugDraw;s===!1?this.addText(`generation = ${this.m_generation}, FAILED`):this.addText(`generation = ${this.m_generation}, count = ${o.length}`),l.DrawPolygon(o,o.length,new F(.9,.9,.9));for(let a=0;athis.Adjust(-.1,0,0)),G("d","Move Right",()=>this.Adjust(.1,0,0)),G("s","Move Down",()=>this.Adjust(0,-.1,0)),G("w","Move Up",()=>this.Adjust(0,.1,0)),G("q","Turn Left",()=>this.Adjust(0,0,.1*Math.PI)),G("e","Turn Right",()=>this.Adjust(0,0,-.1*Math.PI))]}Adjust(e,r,o){this.m_positionB.x+=e,this.m_positionB.y+=r,this.m_angleB+=o,this.m_transformB.SetPositionAngle(this.m_positionB,this.m_angleB)}Step(e,r){super.Step(e,r);let o=new Mi;o.proxyA.SetShape(this.m_polygonA,0),o.proxyB.SetShape(this.m_polygonB,0),o.transformA.Copy(this.m_transformA),o.transformB.Copy(this.m_transformB),o.useRadii=!0;let s=new Di;s.count=0;let l=new Ri;Mr(l,s,o),this.addDebug("Distance",l.distance.toFixed(2)),this.addDebug("Iterations",l.iterations);let a=e.m_debugDraw;{let d=new F(.9,.9,.9),h=[];for(let f=0;fnew Nh);this.m_stepCount=0;this.m_automated=!1;this.m_worldExtent=15,this.m_proxyExtent=.5;for(let r=0;r>2);for(let u=0;u{this.m_automated=!this.m_automated}),G("c","Create Proxy",()=>this.CreateProxy()),G("d","Destroy Proxy",()=>this.DestroyProxy()),G("m","Move Proxy",()=>this.MoveProxy())]}GetRandomAABB(e){let r=new n;r.Set(2*this.m_proxyExtent,2*this.m_proxyExtent),e.lowerBound.x=W(-this.m_worldExtent,this.m_worldExtent),e.lowerBound.y=W(0,2*this.m_worldExtent),e.upperBound.Copy(e.lowerBound),e.upperBound.Add(r)}MoveAABB(e){let r=new n;r.x=W(-.5,.5),r.y=W(-.5,.5),e.lowerBound.Add(r),e.upperBound.Add(r);let o=n.Mid(e.lowerBound,e.upperBound,new n),s=new n(-this.m_worldExtent,0),l=new n(this.m_worldExtent,2*this.m_worldExtent),a=n.Clamp(o,s,l,new n);e.lowerBound.Add(n.Subtract(a,o,new n)),e.upperBound.Add(n.Subtract(a,o,new n))}CreateProxy(){for(let e=0;e{let r=at(e.userData);return r.overlap=this.m_queryAABB.TestOverlap(r.aabb),!0});for(let e=0;e{let a=at(l.userData),c=new _i;return a.aabb.RayCast(c,s)?(this.m_rayCastOutput=c,this.m_rayActor=a,this.m_rayActor.fraction=c.fraction,c.fraction):s.maxFraction});let r=null,o=new _i;for(let s=0;sthis.CreateBody(0)),G("2","Create Flat Triangle",()=>this.CreateBody(1)),G("3","Create Octagon",()=>this.CreateBody(2)),G("4","Create Box",()=>this.CreateBody(3)),G("5","Create Circle",()=>this.CreateBody(4)),G("d","Destroy Body",()=>this.DestroyBody())]}Step(e,r){let o=!e.m_pause||e.m_singleStep;super.Step(e,r);let s=25,l=new n(0,10),a=new n(s*Math.cos(this.m_angle),-s*Math.abs(Math.sin(this.m_angle))),c=n.Add(l,a,new n),u=null,m=new n,p=new n;this.m_world.RayCast(l,c,(h,f,b,x)=>(u=h,m.Copy(f),p.Copy(b),x));let d=e.m_debugDraw;if(u){d.DrawPoint(m,5,new F(.4,.9,.4)),d.DrawSegment(l,m,new F(.8,.8,.8));let h=n.Add(m,n.Scale(.5,p,n.s_t0),new n);d.DrawSegment(m,h,new F(.9,.9,.4))}else d.DrawSegment(l,c,new F(.8,.8,.8));o&&(this.m_angle+=.25*Math.PI/180)}};P("Geometry","Edge Shapes",Qh);var Ss=mt(Ut());function yu(i,t,e,r){return{type:"radio",name:i,options:t,initialValue:e,update:r}}var Bx=({control:i})=>Ss.default.createElement(Ss.default.Fragment,null,i.options.map(t=>Ss.default.createElement("label",{className:"radio",key:t},Ss.default.createElement("input",{name:i.name,type:"radio",className:"radio--input",value:t,onClick:()=>i.update(t),defaultChecked:i.initialValue===t}),Ss.default.createElement("span",null,t)))," ");var qh=class extends R{constructor(){super();this.m_offset1=new n;this.m_offset2=new n;this.m_body1=null;this.m_body2=null;let e=[new n(10,-4),new n(10,0),new n(6,0),new n(4,2),new n(2,0),new n(-2,0),new n(-6,0),new n(-8,-3),new n(-10,0),new n(-10,-4)];this.m_offset1.Set(0,8),this.m_offset2.Set(0,16);{let r=e[0].Clone().Add(this.m_offset1),o=e[1].Clone().Add(this.m_offset1),s=e[2].Clone().Add(this.m_offset1),l=e[3].Clone().Add(this.m_offset1),a=e[4].Clone().Add(this.m_offset1),c=e[5].Clone().Add(this.m_offset1),u=e[6].Clone().Add(this.m_offset1),m=e[7].Clone().Add(this.m_offset1),p=e[8].Clone().Add(this.m_offset1),d=e[9].Clone().Add(this.m_offset1),h=this.m_world.CreateBody(),f=new I;f.SetOneSided(d,r,o,s),h.CreateFixture({shape:f}),f.SetOneSided(r,o,s,l),h.CreateFixture({shape:f}),f.SetOneSided(o,s,l,a),h.CreateFixture({shape:f}),f.SetOneSided(s,l,a,c),h.CreateFixture({shape:f}),f.SetOneSided(l,a,c,u),h.CreateFixture({shape:f}),f.SetOneSided(a,c,u,m),h.CreateFixture({shape:f}),f.SetOneSided(c,u,m,p),h.CreateFixture({shape:f}),f.SetOneSided(u,m,p,d),h.CreateFixture({shape:f}),f.SetOneSided(m,p,d,r),h.CreateFixture({shape:f}),f.SetOneSided(p,d,r,o),h.CreateFixture({shape:f})}{let r=e[0].Clone().Add(this.m_offset2),o=e[1].Clone().Add(this.m_offset2),s=e[2].Clone().Add(this.m_offset2),l=e[3].Clone().Add(this.m_offset2),a=e[4].Clone().Add(this.m_offset2),c=e[5].Clone().Add(this.m_offset2),u=e[6].Clone().Add(this.m_offset2),m=e[7].Clone().Add(this.m_offset2),p=e[8].Clone().Add(this.m_offset2),d=e[9].Clone().Add(this.m_offset2),h=this.m_world.CreateBody(),f=new I;f.SetTwoSided(r,o),h.CreateFixture({shape:f}),f.SetTwoSided(o,s),h.CreateFixture({shape:f}),f.SetTwoSided(s,l),h.CreateFixture({shape:f}),f.SetTwoSided(l,a),h.CreateFixture({shape:f}),f.SetTwoSided(a,c),h.CreateFixture({shape:f}),f.SetTwoSided(c,u),h.CreateFixture({shape:f}),f.SetTwoSided(u,m),h.CreateFixture({shape:f}),f.SetTwoSided(m,p),h.CreateFixture({shape:f}),f.SetTwoSided(p,d),h.CreateFixture({shape:f}),f.SetTwoSided(d,r),h.CreateFixture({shape:f})}this.m_body1=null,this.m_body2=null,this.CreateBoxes()}setupControls(){this.addTestControlGroup("Custom",[yu("Type",["Boxes","Circles"],"Boxes",e=>{e==="Boxes"?this.CreateBoxes():this.CreateCircles()})])}CreateBoxes(){this.m_body1&&(this.m_world.DestroyBody(this.m_body1),this.m_body1=null),this.m_body2&&(this.m_world.DestroyBody(this.m_body2),this.m_body2=null);{this.m_body1=this.m_world.CreateBody({type:2,position:{x:8+this.m_offset1.x,y:2.6+this.m_offset1.y},allowSleep:!1});let e=new v;e.SetAsBox(.5,1),this.m_body1.CreateFixture({shape:e,density:1})}{this.m_body2=this.m_world.CreateBody({type:2,position:{x:8+this.m_offset2.x,y:2.6+this.m_offset2.y},allowSleep:!1});let e=new v;e.SetAsBox(.5,1),this.m_body2.CreateFixture({shape:e,density:1})}}CreateCircles(){this.m_body1&&(this.m_world.DestroyBody(this.m_body1),this.m_body1=null),this.m_body2&&(this.m_world.DestroyBody(this.m_body2),this.m_body2=null);{this.m_body1=this.m_world.CreateBody({type:2,position:{x:this.m_offset1.x-.5,y:this.m_offset1.y+.6},allowSleep:!1});let e=new V(.5);this.m_body1.CreateFixture({shape:e,density:1})}{this.m_body2=this.m_world.CreateBody({type:2,position:{x:this.m_offset2.x-.5,y:this.m_offset2.y+.6},allowSleep:!1});let e=new V(.5);this.m_body2.CreateFixture({shape:e,density:1})}}getHotkeys(){return[Vr("a","Apply Force Left",()=>{this.m_body1?.ApplyForceToCenter(new n(-10,0),!0),this.m_body2?.ApplyForceToCenter(new n(-10,0),!0)}),Vr("d","Apply Force Right",()=>{this.m_body1?.ApplyForceToCenter(new n(10,0),!0),this.m_body2?.ApplyForceToCenter(new n(10,0),!0)})]}};P("Geometry","Edge Test",qh);var Wh=class extends R{constructor(){super();let t=null;{t=this.m_world.CreateBody();let e=new I;e.SetTwoSided(new n(-50,0),new n(50,0)),t.CreateFixture({shape:e})}{let e=new V;e.m_radius=1;let r=new v;r.SetAsBox(.5,5);let o=new V;o.m_radius=2;let s={x:10,y:9},l=this.m_world.CreateBody({type:0,position:s});l.CreateFixture({shape:e,density:5});let a=this.m_world.CreateBody({type:2,position:{x:10,y:8}});a.CreateFixture({shape:r,density:5});let c={x:10,y:6},u=this.m_world.CreateBody({type:2,position:c});u.CreateFixture({shape:o,density:5});let m=new q;m.Initialize(a,l,s);let p=this.m_world.CreateJoint(m),d=new q;d.Initialize(a,u,c);let h=this.m_world.CreateJoint(d),f=new ns;f.bodyA=l,f.bodyB=u,f.joint1=p,f.joint2=h,f.ratio=o.m_radius/e.m_radius,this.m_world.CreateJoint(f)}{let e=new V;e.m_radius=1;let r=new V;r.m_radius=2;let o=new v;o.SetAsBox(.5,5);let s={x:-3,y:12},l=this.m_world.CreateBody({type:2,position:s});l.CreateFixture({shape:e,density:5});let a=new q;a.bodyA=t,a.bodyB=l,t.GetLocalPoint(s,a.localAnchorA),l.GetLocalPoint(s,a.localAnchorB),a.referenceAngle=l.GetAngle()-t.GetAngle(),this.m_joint1=this.m_world.CreateJoint(a);let c={x:0,y:12},u=this.m_world.CreateBody({type:2,position:c});u.CreateFixture({shape:r,density:5});let m=new q;m.Initialize(t,u,c),this.m_joint2=this.m_world.CreateJoint(m);let p={x:2.5,y:12},d=this.m_world.CreateBody({type:2,position:p});d.CreateFixture({shape:o,density:5});let h=new Zt;h.Initialize(t,d,p,new n(0,1)),h.lowerTranslation=-5,h.upperTranslation=5,h.enableLimit=!0,this.m_joint3=this.m_world.CreateJoint(h);let f=new ns;f.bodyA=l,f.bodyB=u,f.joint1=this.m_joint1,f.joint2=this.m_joint2,f.ratio=r.m_radius/e.m_radius,this.m_joint4=this.m_world.CreateJoint(f);let b=new ns;b.bodyA=u,b.bodyB=d,b.joint1=this.m_joint2,b.joint2=this.m_joint3,b.ratio=-1/r.m_radius,this.m_joint5=this.m_world.CreateJoint(b)}}};P("Joints","Gear",Wh);var Kh=class extends R{constructor(){super();{let r=this.m_world.CreateBody(),o=new I;o.SetTwoSided(new n(-40,0),new n(40,0)),r.CreateFixture({shape:o})}let t=this.m_world.CreateBody({type:2,position:{x:0,y:.5}}),e=new V;e.m_radius=.5,t.CreateFixture({shape:e,density:10}),t=this.m_world.CreateBody({type:2,position:{x:0,y:6}}),e.m_radius=5,t.CreateFixture({shape:e,density:10})}};P("Solver","Heavy 1",Kh);var $h=class extends R{constructor(){super();this.m_heavy=null;{let o=this.m_world.CreateBody(),s=new I;s.SetTwoSided(new n(-40,0),new n(40,0)),o.CreateFixture({shape:s})}let e=this.m_world.CreateBody({type:2,position:{x:0,y:2.5}}),r=new V;r.m_radius=.5,e.CreateFixture({shape:r,density:10}),e=this.m_world.CreateBody({type:2,position:{x:0,y:3.5}}),e.CreateFixture({shape:r,density:10})}ToggleHeavy(){if(this.m_heavy!==null)this.m_world.DestroyBody(this.m_heavy),this.m_heavy=null;else{this.m_heavy=this.m_world.CreateBody({type:2,position:{x:0,y:9}});let e=new V;e.m_radius=5,this.m_heavy.CreateFixture({shape:e,density:10})}}getHotkeys(){return[G("h","Toggle Heavy",()=>this.ToggleHeavy())]}};P("Solver","Heavy 2",$h);var tf=class i extends R{static{this.e_depth=4}constructor(){super();let t=this.m_world.CreateBody({position:{x:0,y:20}}),e=.5,r=new n(0,e),o=this.AddNode(t,n.ZERO,0,3,e),s=new q;s.bodyA=t,s.bodyB=o,s.localAnchorA.SetZero(),s.localAnchorB.Copy(r),this.m_world.CreateJoint(s)}GetDefaultViewZoom(){return 60}getCenter(){return{x:0,y:15}}AddNode(t,e,r,o,s){let a=new n(0,s),c=this.m_world.CreateBody({type:2,position:t.GetPosition().Clone().Add(e).Subtract(a)}),u=new v;if(u.SetAsBox(.25*s,s),c.CreateFixture({shape:u,density:20}),r===i.e_depth)return c;let m=new n(o,-s),p=new n(-o,-s),d=this.AddNode(c,m,r+1,.5*o,s),h=this.AddNode(c,p,r+1,.5*o,s),f=new q;return f.bodyA=c,f.localAnchorB.Copy(a),f.localAnchorA.Copy(m),f.bodyB=d,this.m_world.CreateJoint(f),f.localAnchorA.Copy(p),f.bodyB=h,this.m_world.CreateJoint(f),c}};P("Solver","Mobile Unbalanced",tf);var ef=class i extends R{static{this.e_depth=4}constructor(){super();let t=this.m_world.CreateBody({position:{x:0,y:20}}),e=.5,r=new n(0,e),o=this.AddNode(t,n.ZERO,0,3,e),s=new q;s.bodyA=t,s.bodyB=o,s.localAnchorA.SetZero(),s.localAnchorB.Copy(r),this.m_world.CreateJoint(s)}GetDefaultViewZoom(){return 60}getCenter(){return{x:0,y:15}}AddNode(t,e,r,o,s){let a=new n(0,s),c=t.GetPosition().Clone().Add(e).Subtract(a),u=this.m_world.CreateBody({type:2,position:c}),m=new v;if(m.SetAsBox(.25*s,s),u.CreateFixture({shape:m,density:20}),r===i.e_depth)return u;m.SetAsBox(o,.25*s,new n(0,-s),0),u.CreateFixture({shape:m,density:20});let p=new n(o,-s),d=new n(-o,-s),h=this.AddNode(u,p,r+1,.5*o,s),f=this.AddNode(u,d,r+1,.5*o,s),b=new q;return b.bodyA=u,b.localAnchorB.Copy(a),b.localAnchorA.Copy(p),b.bodyB=h,this.m_world.CreateJoint(b),b.localAnchorA.Copy(d),b.bodyB=f,this.m_world.CreateJoint(b),u}};P("Solver","Mobile Balanced",ef);var rf=class extends R{constructor(){super();this.m_time=0;this.m_go=!1;let e=null;{e=this.m_world.CreateBody();let r=new I;r.SetTwoSided(new n(-20,0),new n(20,0)),e.CreateFixture({shape:r})}{let r=this.m_world.CreateBody({type:2,position:{x:0,y:8}}),o=new v;o.SetAsBox(2,.5),r.CreateFixture({shape:o,friction:.6,density:2});let s=new ls;s.Initialize(e,r),s.maxForce=1e3,s.maxTorque=1e3,this.m_joint=this.m_world.CreateJoint(s)}this.m_go=!1,this.m_time=0}getHotkeys(){return[G("s","Start/Stop",()=>{this.m_go=!this.m_go})]}Step(e,r){this.m_go&&e.m_hertz>0&&(this.m_time+=1/e.m_hertz);let o=new n;o.x=6*Math.sin(2*this.m_time),o.y=8+4*Math.sin(1*this.m_time);let s=4*this.m_time;this.m_joint.SetLinearOffset(o),this.m_joint.SetAngularOffset(s),e.m_debugDraw.DrawPoint(o,4,new F(.9,.9,.9)),super.Step(e,r)}};P("Joints","Motor Joint",rf);var of=class extends R{constructor(){super();this.m_radius=0;this.m_top=0;this.m_bottom=0;this.m_state=0;{let e=this.m_world.CreateBody(),r=new I;r.SetTwoSided(new n(-20,0),new n(20,0)),e.CreateFixture({shape:r})}{let e=this.m_world.CreateBody({position:{x:0,y:10}}),r=new v;r.SetAsBox(3,.5),this.m_platform=e.CreateFixture({shape:r}),this.m_bottom=10-.5,this.m_top=10+.5}{let e=this.m_world.CreateBody({type:2,position:{x:0,y:12}});this.m_radius=.5;let r=new V;r.m_radius=this.m_radius,this.m_character=e.CreateFixture({shape:r,density:20}),e.SetLinearVelocity(new n(0,-50)),this.m_state=0}}GetDefaultViewZoom(){return 40}getCenter(){return{x:0,y:5}}PreSolve(e,r){super.PreSolve(e,r);let o=e.GetFixtureA(),s=e.GetFixtureB();if(o!==this.m_platform&&o!==this.m_character||s!==this.m_platform&&s!==this.m_character)return;this.m_character.GetBody().GetPosition().y{t?(this.m_leftJoint.SetMotorSpeed(20),this.m_rightJoint.SetMotorSpeed(-20)):(this.m_leftJoint.SetMotorSpeed(-10),this.m_rightJoint.SetMotorSpeed(10))})]}};P("Examples","Pinball",sf);var nf=class extends R{constructor(){super();this.m_polygonA=new v;this.m_polygonB=new v;this.m_transformA=new T;this.m_transformB=new T;this.m_positionB=new n;this.m_angleB=0;this.m_polygonA.SetAsBox(.2,.4),this.m_transformA.SetPositionAngle(new n,0),this.m_polygonB.SetAsBox(.5,.5),this.m_positionB.Set(1,1),this.m_angleB=1.9160721,this.m_transformB.SetPositionAngle(this.m_positionB,this.m_angleB)}GetDefaultViewZoom(){return 100}getHotkeys(){return[G("a","Move Left",()=>this.Adjust(-.1,0,0)),G("d","Move Right",()=>this.Adjust(.1,0,0)),G("s","Move Down",()=>this.Adjust(0,-.1,0)),G("w","Move Up",()=>this.Adjust(0,.1,0)),G("q","Turn Left",()=>this.Adjust(0,0,.1*Math.PI)),G("e","Turn Right",()=>this.Adjust(0,0,-.1*Math.PI))]}Adjust(e,r,o){this.m_positionB.x+=e,this.m_positionB.y+=r,this.m_angleB+=o,this.m_transformB.SetPositionAngle(this.m_positionB,this.m_angleB)}Step(e,r){super.Step(e,r);let o=new co;Xa(o,this.m_polygonA,this.m_transformA,this.m_polygonB,this.m_transformB);let s=new ki;s.Initialize(o,this.m_transformA,this.m_polygonA.m_radius,this.m_transformB,this.m_polygonB.m_radius),this.addDebug("Point Count",o.pointCount);let l=e.m_debugDraw;{let a=new F(.9,.9,.9),c=[];for(let u=0;unull);this.m_polygons=Array.from({length:4},()=>new v);this.m_circle=new V;{let e=this.m_world.CreateBody(),r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r})}{let e=new Array(3);e[0]=new n(-.5,0),e[1]=new n(.5,0),e[2]=new n(0,1.5),this.m_polygons[0].Set(e,3)}{let e=new Array(3);e[0]=new n(-.1,0),e[1]=new n(.1,0),e[2]=new n(0,1.5),this.m_polygons[1].Set(e,3)}{let r=1/(2+Math.sqrt(2)),o=Math.sqrt(2)*r,s=new Array(8);s[0]=new n(.5*o,0),s[1]=new n(.5*1,r),s[2]=new n(.5*1,r+o),s[3]=new n(.5*o,1),s[4]=new n(-.5*o,1),s[5]=new n(-.5*1,r+o),s[6]=new n(-.5*1,r),s[7]=new n(-.5*o,0),this.m_polygons[2].Set(s,8)}this.m_polygons[3].SetAsBox(.5,.5),this.m_circle.m_radius=.5;for(let e=0;ethis.CreateBody(0)),G("2","Create Flat Triangle",()=>this.CreateBody(1)),G("3","Create Octagon",()=>this.CreateBody(2)),G("4","Create Box",()=>this.CreateBody(3)),G("5","Create Circle",()=>this.CreateBody(4)),G("a","Toggle Enabled of Even Bodies",()=>{for(let e=0;ethis.DestroyBody())]}Step(e,r){super.Step(e,r);let o=e.m_debugDraw,s=0,{circle:l,transform:a}=_A.Step;l.m_radius=2,l.m_p.Set(0,1.1),a.SetIdentity();let c=new nt;l.ComputeAABB(c,a,0),this.m_world.QueryAABB(c,m=>{if(s===bA)return!1;let p=m.GetBody(),d=m.GetShape();if(ts(d,0,l,0,p.GetTransform(),a)){let f=new F(.95,.95,.6),b=p.GetWorldCenter();o.DrawPoint(b,5,f),++s}return!0});let u=new F(.4,.7,.8);o.DrawCircle(l.m_p,l.m_radius,u)}};P("Geometry","Polygon Shapes",lf);var yo=mt(Ut());function _t(i,t,e,r,o,s){return{type:"slider",name:i,min:t,max:e,step:r,initialValue:o,update:s}}function Cx({control:i}){let[t,e]=(0,yo.useState)(i.initialValue);return yo.default.createElement("label",{className:"slider"},yo.default.createElement("div",{className:"slider--input"},yo.default.createElement("input",{type:"range",min:i.min,max:i.max,step:i.step,defaultValue:t,onChange:r=>{let o=parseFloat(r.currentTarget.value);e(o),i.update(o)}}),yo.default.createElement("span",null,t)),i.name.split("#")[0])}var af=class extends R{constructor(){super();this.m_motorSpeed=10;this.m_enableMotor=!1;this.m_enableLimit=!0;let e=null;{e=this.m_world.CreateBody();let r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r})}{let r=new v;r.SetAsBox(1,1);let o={x:0,y:10},s=this.m_world.CreateBody({type:2,position:o,angle:.5*Math.PI,allowSleep:!1});s.CreateFixture({shape:r,density:5});let l=new Zt;l.Initialize(e,s,o,new n(1,0)),l.motorSpeed=this.m_motorSpeed,l.maxMotorForce=1e4,l.enableMotor=this.m_enableMotor,l.lowerTranslation=-10,l.upperTranslation=10,l.enableLimit=this.m_enableLimit,this.m_joint=this.m_world.CreateJoint(l)}}setupControls(){this.addTestControlGroup("Joint",[jt("Limit",this.m_enableLimit,e=>{this.m_enableLimit=this.m_joint.EnableLimit(e)}),jt("Motor",this.m_enableMotor,e=>{this.m_enableMotor=this.m_joint.EnableMotor(e)}),_t("Speed",-100,100,1,this.m_motorSpeed,e=>{this.m_motorSpeed=this.m_joint.SetMotorSpeed(e)})])}getHotkeys(){return[G("l","Toggle Limit",()=>this.m_joint.EnableLimit(!this.m_joint.IsLimitEnabled())),G("m","Start/Stop",()=>this.m_joint.EnableMotor(!this.m_joint.IsMotorEnabled())),G("s","Reverse Direction",()=>this.m_joint.SetMotorSpeed(-this.m_joint.GetMotorSpeed()))]}Step(e,r){super.Step(e,r);let o=this.m_joint.GetMotorForce(e.m_hertz);this.addDebug("Motor Force",o.toFixed())}};P("Joints","Prismatic",af);var cf=class extends R{constructor(){super();let t=16,e=12,r=1,o=2,s=null;{s=this.m_world.CreateBody();let l=new V;l.m_radius=2,l.m_p.Set(-10,t+o+e),s.CreateFixture({shape:l}),l.m_p.Set(10,t+o+e),s.CreateFixture({shape:l})}{let l=new v;l.SetAsBox(r,o);let a=this.m_world.CreateBody({type:2,position:{x:-10,y:t}});a.CreateFixture({shape:l,density:5});let c=this.m_world.CreateBody({type:2,position:{x:10,y:t}});c.CreateFixture({shape:l,density:5});let u=new sc,m=new n(-10,t+o),p=new n(10,t+o),d=new n(-10,t+o+e),h=new n(10,t+o+e);u.Initialize(a,c,d,h,m,p,1.5),this.m_joint1=this.m_world.CreateJoint(u)}}getCenter(){return{x:0,y:15}}Step(t,e){super.Step(t,e),this.addDebug("Ratio",this.m_joint1.GetRatio().toFixed(2)),this.addDebug("Length A",this.m_joint1.GetCurrentLengthA().toFixed(2)),this.addDebug("Length B",this.m_joint1.GetCurrentLengthB().toFixed(2))}};P("Joints","Pulley",cf);var uf=class i extends R{static{this.e_count=20}constructor(){super();{let t=this.m_world.CreateBody(),e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e})}{let e=new v;e.SetAsBox(.5,.5);let r=new n(-7,.75),o=new n,s=new n(.5625,1.25),l=new n(1.125,0);for(let a=0;a{this.m_mode=e}),_t("Angle",0,360,1,this.m_degrees,e=>{this.m_degrees=e})])}CreateBody(e){let r=this.m_bodies[this.m_bodyIndex];r!==null&&(this.m_world.DestroyBody(r),this.m_bodies[this.m_bodyIndex]=null);let o=this.m_bodies[this.m_bodyIndex]=this.m_world.CreateBody({position:{x:W(-10,10),y:W(0,20)},angle:W(-Math.PI,Math.PI),angularDamping:e===4?.02:0,userData:{rayCastBodyIndex:e}});e<4?o.CreateFixture({shape:this.m_polygons[e],friction:.3}):e<5?o.CreateFixture({shape:this.m_circle,friction:.3}):o.CreateFixture({shape:this.m_edge,friction:.3}),this.m_bodyIndex=(this.m_bodyIndex+1)%i.e_maxBodies}DestroyBody(){for(let e=0;ethis.CreateBody(0)),G("2","Create Flat Triangle",()=>this.CreateBody(1)),G("3","Create Octagon",()=>this.CreateBody(2)),G("4","Create Box",()=>this.CreateBody(3)),G("5","Create Circle",()=>this.CreateBody(4)),G("6","Create Edge",()=>this.CreateBody(5)),G("d","Destroy Body",()=>this.DestroyBody())]}Step(e,r){super.Step(e,r);let o=pt(this.m_degrees),s=11,l=new n(0,10),a=new n(s*Math.cos(o),s*Math.sin(o)),c=n.Add(l,a,new n);switch(this.addText("Shape 1 is intentionally ignored by the ray"),this.m_mode){case"Closest":this.addDebug("Ray-Cast Mode","Find closest fixture along the ray"),this.rayCastClosest(l,c,e);break;case"Any":this.addDebug("Ray-Cast Mode","Check for obstruction"),this.rayCastAny(l,c,e);break;case"Multiple":this.addDebug("Ray-Cast Mode","Gather multiple fixtures"),this.rayCastMultiple(l,c,e);break}}rayCastClosest(e,r,o){let s=!1,l=new n,a=new n;this.m_world.RayCast(e,r,(u,m,p,d)=>{let{rayCastBodyIndex:h}=u.GetBody().GetUserData();return h===0?-1:(s=!0,l.Copy(m),a.Copy(p),d)});let c=o.m_debugDraw;if(s){c.DrawPoint(l,5,new F(.4,.9,.4)),c.DrawSegment(e,l,new F(.8,.8,.8));let u=n.Add(l,n.Scale(.5,a,n.s_t0),new n);c.DrawSegment(l,u,new F(.9,.9,.4))}else c.DrawSegment(e,r,new F(.8,.8,.8))}rayCastAny(e,r,o){let s=!1,l=new n,a=new n;this.m_world.RayCast(e,r,(u,m,p,d)=>{let{rayCastBodyIndex:h}=u.GetBody().GetUserData();return h===0?-1:(s=!0,l.Copy(m),a.Copy(p),0)});let c=o.m_debugDraw;if(s){c.DrawPoint(l,5,new F(.4,.9,.4)),c.DrawSegment(e,l,new F(.8,.8,.8));let u=n.Add(l,n.Scale(.5,a,n.s_t0),new n);c.DrawSegment(l,u,new F(.9,.9,.4))}else c.DrawSegment(e,r,new F(.8,.8,.8))}rayCastMultiple(e,r,o){let l=H(3,n),a=H(3,n),c=0;this.m_world.RayCast(e,r,(m,p,d)=>{let{rayCastBodyIndex:h}=m.GetBody().GetUserData();return h===0?-1:(l[c].Copy(p),a[c].Copy(d),++c,c===3?0:1)});let u=o.m_debugDraw;u.DrawSegment(e,r,new F(.8,.8,.8));for(let m=0;m{this.m_enableLimit=this.m_joint1.EnableLimit(r)}),jt("Motor",this.m_enableMotor,r=>{this.m_enableMotor=this.m_joint1.EnableMotor(r)}),_t("Speed",-20,20,1,this.m_motorSpeed,r=>{this.m_motorSpeed=this.m_joint1.SetMotorSpeed(r)})])}getCenter(){return{x:0,y:5}}Step(e,r){super.Step(e,r);let o=this.m_joint1.GetMotorTorque(e.m_hertz);this.addDebug("Motor Torque 1",o.toFixed(0));let s=this.m_joint2.GetMotorTorque(e.m_hertz);this.addDebug("Motor Torque 2",s.toFixed(0))}};P("Joints","Revolute",pf);var df=class i extends R{constructor(){super();this.m_bodies=new Array(i.e_count);this.m_force=100;this.m_touching=X2(i.e_count);let e=this.m_world.CreateBody();{let r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r})}{let r=new V;r.m_radius=5,r.m_p.Set(0,10),this.m_sensor=e.CreateFixture({shape:r,isSensor:!0})}{let r=new V;r.m_radius=1;for(let o=0;o{this.m_force=e})])}getCenter(){return{x:0,y:5}}BeginContact(e){let r=e.GetFixtureA(),o=e.GetFixtureB();if(r===this.m_sensor){let{sensor:s}=o.GetBody().GetUserData();s!==void 0&&(this.m_touching[s]=!0)}if(o===this.m_sensor){let{sensor:s}=r.GetBody().GetUserData();s!==void 0&&(this.m_touching[s]=!0)}}EndContact(e){let r=e.GetFixtureA(),o=e.GetFixtureB();if(r===this.m_sensor){let{sensor:s}=o.GetBody().GetUserData();s!==void 0&&(this.m_touching[s]=!1)}if(o===this.m_sensor){let{sensor:s}=r.GetBody().GetUserData();s!==void 0&&(this.m_touching[s]=!1)}}Step(e,r){super.Step(e,r);for(let o=0;o{if(this.m_fixture2===null){let e=new V;e.m_radius=3,e.m_p.Set(.5,-4),this.m_fixture2=this.m_body.CreateFixture({shape:e,density:10}),this.m_body.SetAwake(!0)}}),G("d","Destroy a Shape",()=>{this.m_fixture2!==null&&(this.m_body.DestroyFixture(this.m_fixture2),this.m_fixture2=null,this.m_body.SetAwake(!0))}),G("s","Toggle Sensor",()=>{this.m_fixture2!==null&&(this.m_sensor=!this.m_sensor,this.m_fixture2.SetSensor(this.m_sensor))})]}Step(e,r){super.Step(e,r),this.addDebug("Sensor",this.m_sensor)}};P("Examples","Shape Editing",ff);var bf=class extends R{constructor(){super();let t=this.m_world.CreateBody(),e=8,r=-30,o=10,s=2,l=.2,a=-r*Math.PI/180,c=a-o*Math.PI/180;this.m_platform_width=e;let u=new n(-e,0),m=new n,p=new n(s*Math.cos(a),-s*Math.sin(a)),d=new n(p.x+s*Math.cos(c),p.y-s*Math.sin(c)),f=[new n(d.x,d.y-1),d,p,m,u],b=new dt;b.CreateLoop(f),t.CreateFixture({shape:b,density:0,friction:l});{let B=this.m_world.CreateBody({type:2,position:{x:-this.m_platform_width/2,y:1.55}}),C=new v,A=H(4,n);A[0].Set(-3/2-.3,-2.5/2),A[1].Set(-3/2,-2.5/2-.3),A[2].Set(3/2,-2.5/2-.3),A[3].Set(3/2+.3,-2.5/2),C.Set(A),B.CreateFixture({density:1,friction:0,restitution:.15,shape:C}),B.SetLinearVelocity(new n(.5,0)),this.m_skier=B}ot.setPositionAndZoom(this.m_platform_width/2,0,.4),this.m_fixed_camera=!0}GetDefaultViewZoom(){return 50}getHotkeys(){return[G("c","Switch Camera Fixed/Tracking",()=>{this.m_fixed_camera=!this.m_fixed_camera,this.m_fixed_camera&&ot.setPosition(this.m_platform_width/2,0)})]}Step(t,e){if(!this.m_fixed_camera){let r=this.m_skier.GetPosition();ot.setPosition(r.x,r.y)}super.Step(t,e)}};P("Bugs","Skier",bf);var _f=class extends R{constructor(){super();let t=null;{t=this.m_world.CreateBody();let e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e})}{let e=t;{let r=new v;r.SetAsBox(.5,2);let o=this.m_world.CreateBody({type:2,position:{x:0,y:7}});o.CreateFixture({shape:r,density:2});let s=new q;s.Initialize(e,o,new n(0,5)),s.motorSpeed=1*Math.PI,s.maxMotorTorque=1e4,s.enableMotor=!0,this.m_joint1=this.m_world.CreateJoint(s),e=o}{let r=new v;r.SetAsBox(.5,4);let o=this.m_world.CreateBody({type:2,position:{x:0,y:13}});o.CreateFixture({shape:r,density:2});let s=new q;s.Initialize(e,o,new n(0,9)),s.enableMotor=!1,this.m_world.CreateJoint(s),e=o}{let r=new v;r.SetAsBox(1.5,1.5);let o=this.m_world.CreateBody({type:2,fixedRotation:!0,position:{x:0,y:17}});o.CreateFixture({shape:r,density:2});let s=new q;s.Initialize(e,o,new n(0,17)),this.m_world.CreateJoint(s);let l=new Zt;l.Initialize(t,o,new n(0,17),new n(0,1)),l.maxMotorForce=1e3,l.enableMotor=!0,this.m_joint2=this.m_world.CreateJoint(l)}{let r=new v;r.SetAsBox(1.5,1.5),this.m_world.CreateBody({type:2,position:{x:0,y:23}}).CreateFixture({shape:r,density:2})}}}getHotkeys(){return[G("f","Toggle Friction",()=>{this.m_joint2.EnableMotor(!this.m_joint2.IsMotorEnabled()),this.m_joint2.GetBodyB().SetAwake(!0)}),G("m","Toggle Motor",()=>{this.m_joint1.EnableMotor(!this.m_joint1.IsMotorEnabled()),this.m_joint1.GetBodyB().SetAwake(!0)})]}Step(t,e){super.Step(t,e);let r=this.m_joint1.GetMotorTorque(t.m_hertz);this.addDebug("Motor Torque",r.toFixed(0))}};P("Examples","Slider Crank 2",_f);var yf=class i extends R{constructor(){super();this.m_bodies=[];{let e=this.m_world.CreateBody(),r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r})}{let e=new V;e.m_radius=1;for(let r=0;r0){let f=[];f[0]=o,f[1]=s,f[2]=l,m.Set(f),f[0]=n.ZERO,f[1]=n.Subtract(c,a,new n),f[2]=n.Subtract(u,a,new n),p.Set(f)}else{let f=[];f[0]=o,f[1]=l,f[2]=s,m.Set(f),f[0]=n.ZERO,f[1]=n.Subtract(u,a,new n),f[2]=n.Subtract(c,a,new n),p.Set(f)}let d=this.m_world.CreateBody({type:2,position:this.m_offset,angularDamping:10}),h=this.m_world.CreateBody({type:2,position:n.Add(a,this.m_offset,new n),angularDamping:10});d.CreateFixture({filter:{groupIndex:-1},shape:m,density:1}),h.CreateFixture({filter:{groupIndex:-1},density:1,shape:p});{let f=new ke,b=.5,x=10;f.Initialize(d,h,n.Add(s,this.m_offset,new n),n.Add(c,this.m_offset,new n)),Ct(f,x,b,f.bodyA,f.bodyB),this.m_world.CreateJoint(f),f.Initialize(d,h,n.Add(l,this.m_offset,new n),n.Add(a,this.m_offset,new n)),Ct(f,x,b,f.bodyA,f.bodyB),this.m_world.CreateJoint(f),f.Initialize(d,this.m_wheel,n.Add(l,this.m_offset,new n),n.Add(r,this.m_offset,new n)),Ct(f,x,b,f.bodyA,f.bodyB),this.m_world.CreateJoint(f),f.Initialize(h,this.m_wheel,n.Add(u,this.m_offset,new n),n.Add(r,this.m_offset,new n)),Ct(f,x,b,f.bodyA,f.bodyB),this.m_world.CreateJoint(f)}{let f=new q;f.Initialize(h,this.m_chassis,n.Add(a,this.m_offset,new n)),this.m_world.CreateJoint(f)}}getHotkeys(){return[G("a","Left",()=>this.m_motorJoint.SetMotorSpeed(-this.m_motorSpeed)),G("s","Brake",()=>this.m_motorJoint.SetMotorSpeed(0)),G("d","Right",()=>this.m_motorJoint.SetMotorSpeed(this.m_motorSpeed)),G("m","Toggle Enabled",()=>this.m_motorJoint.EnableMotor(!this.m_motorJoint.IsMotorEnabled()))]}};P("Examples","Theo Jansen's Walker",xf);var gf=class i extends R{constructor(){super();this.m_fixtureCount=0;this.m_createTime=0;this.m_fixtureCount=0;let e=new ii;{let o=this.m_world.CreateBody({position:{x:0,y:-.5}});{let a=new n;a.y=0;for(let c=0;c<10;++c){a.x=-200*.5;for(let u=0;u<200;++u){let m=new v;m.SetAsBox(.5,.5,a,0),o.CreateFixture({shape:m}),++this.m_fixtureCount,a.x+=2*.5}a.y-=2*.5}}}{let o=new v;o.SetAsBox(.5,.5);let s=new n(-7,.75),l=new n,a=new n(.5625,1.25),c=new n(1.125,0);for(let u=0;uthis.LaunchBullet()),G("b","Toggle Block solving",()=>Pc(!Dc()))]}Destroy(){Pc(!0)}LaunchBullet(){this.m_bullet&&(this.m_world.DestroyBody(this.m_bullet),this.m_bullet=null);{let e=new V;e.m_radius=.25;let r={shape:e,density:20,restitution:.05};this.m_bullet=this.m_world.CreateBody({type:2,bullet:!0,position:{x:-31,y:5}}),this.m_bullet.CreateFixture(r),this.m_bullet.SetLinearVelocity(new n(400,0))}}Step(e,r){super.Step(e,r),this.addDebug("Blocksolve",Dc())}};P("Stacking","Box Stack",Cf);var Af=class extends R{constructor(){super();this.m_bodies=new Array(4);this.m_joints=new Array(8);let e=null;{e=this.m_world.CreateBody();let r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r})}{let r=new v;r.SetAsBox(.5,.5);let o=this.m_bodies[0]=this.m_world.CreateBody({type:2,position:{x:-5,y:5}});o.CreateFixture({shape:r,density:5});let s=this.m_bodies[1]=this.m_world.CreateBody({type:2,position:{x:5,y:5}});s.CreateFixture({shape:r,density:5});let l=this.m_bodies[2]=this.m_world.CreateBody({type:2,position:{x:5,y:15}});l.CreateFixture({shape:r,density:5});let a=this.m_bodies[3]=this.m_world.CreateBody({type:2,position:{x:-5,y:15}});a.CreateFixture({shape:r,density:5});let c=new ke,u,m,p,d=2,h=0;c.bodyA=e,c.bodyB=o,c.localAnchorA.Set(-10,0),c.localAnchorB.Set(-.5,-.5),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[0]=this.m_world.CreateJoint(c),c.bodyA=e,c.bodyB=s,c.localAnchorA.Set(10,0),c.localAnchorB.Set(.5,-.5),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[1]=this.m_world.CreateJoint(c),c.bodyA=e,c.bodyB=l,c.localAnchorA.Set(10,20),c.localAnchorB.Set(.5,.5),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[2]=this.m_world.CreateJoint(c),c.bodyA=e,c.bodyB=a,c.localAnchorA.Set(-10,20),c.localAnchorB.Set(-.5,.5),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[3]=this.m_world.CreateJoint(c),c.bodyA=o,c.bodyB=s,c.localAnchorA.Set(.5,0),c.localAnchorB.Set(-.5,0),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[4]=this.m_world.CreateJoint(c),c.bodyA=s,c.bodyB=l,c.localAnchorA.Set(0,.5),c.localAnchorB.Set(0,-.5),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[5]=this.m_world.CreateJoint(c),c.bodyA=l,c.bodyB=a,c.localAnchorA.Set(-.5,0),c.localAnchorB.Set(.5,0),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[6]=this.m_world.CreateJoint(c),c.bodyA=a,c.bodyB=o,c.localAnchorA.Set(0,-.5),c.localAnchorB.Set(0,.5),u=c.bodyA.GetWorldPoint(c.localAnchorA,new n),m=c.bodyB.GetWorldPoint(c.localAnchorB,new n),p=n.Subtract(m,u,new n),c.length=p.Length(),Ct(c,d,h,c.bodyA,c.bodyB),this.m_joints[7]=this.m_world.CreateJoint(c)}}JointDestroyed(e){for(let r=0;r<8;++r)if(this.m_joints[r]===e){this.m_joints[r]=null;break}}getHotkeys(){return[G("b","Delete a Body",()=>{for(let e=0;e<4;++e){let r=this.m_bodies[e];if(r){this.m_world.DestroyBody(r),this.m_bodies[e]=null;break}}}),G("j","Delete a Joint",()=>{for(let e=0;e<8;++e){let r=this.m_joints[e];if(r){this.m_world.DestroyJoint(r),this.m_joints[e]=null;break}}})]}Step(e,r){super.Step(e,r),this.addText("This demonstrates a soft distance joint.")}};P("Examples","Web",Af);var Tf=["Spring","PBD Ang","XPBD Ang","PBD Dist","PBD Height","PBD Triangle"],Pf=["PBD","XPBD"],Df=class extends R{constructor(){super();this.m_tuning1=new us;this.m_tuning2=new us;this.m_iterations=[8,8];this.m_position1=new n;this.m_position2=new n;this.m_speed=10;this.m_moveLeft=!1;this.m_moveRight=!1;let e=20,r=.5,o=H(e,n),s=je(e);for(let a=0;a{this.m_speed=e})])}ropeControls(e,r){return[rr("Bend Model#",Tf,Tf[r.bendingModel],o=>{r.bendingModel=Tf.indexOf(o)}),_t("Damping#b",0,4,.1,r.bendDamping,o=>{r.bendDamping=o}),_t("Hertz#b",0,60,1,r.bendHertz,o=>{r.bendHertz=o}),_t("Stiffness#b",0,1,.1,r.bendStiffness,o=>{r.bendStiffness=o}),jt("Isometric",r.isometric,o=>{r.isometric=o}),jt("Fixed Mass",r.fixedEffectiveMass,o=>{r.fixedEffectiveMass=o}),jt("Warm Start",r.warmStart,o=>{r.warmStart=o}),rr("Stretch Model",Pf,Pf[r.stretchingModel],o=>{r.stretchingModel=Pf.indexOf(o)}),_t("Damping#s",0,4,.1,r.stretchDamping,o=>{r.stretchDamping=o}),_t("Hertz#s",0,60,1,r.stretchHertz,o=>{r.stretchHertz=o}),_t("Stiffness#s",0,1,.1,r.stretchStiffness,o=>{r.stretchStiffness=o}),_t("Iterations",0,100,1,this.m_iterations[e],o=>{this.m_iterations[e]=o})]}GetDefaultViewZoom(){return 45}getCenter(){return{x:0,y:20}}getHotkeys(){return[Er("a","Move Left",this,"m_moveLeft"),Er("d","Move Right",this,"m_moveRight"),G("s","Reset Ropes",()=>{this.m_position1.Set(-5,15),this.m_position2.Set(5,15),this.m_rope1.Reset(this.m_position1),this.m_rope2.Reset(this.m_position2)})]}Step(e,r){let o=e.m_hertz>0?1/e.m_hertz:0;e.m_pause===!0&&e.m_singleStep===!1&&(o=0);let s=0;this.m_moveLeft&&(s-=1),this.m_moveRight&&(s+=1),s&&(this.m_position1.x+=s*this.m_speed*o,this.m_position2.x+=s*this.m_speed*o),this.m_rope1.SetTuning(this.m_tuning1),this.m_rope2.SetTuning(this.m_tuning2),this.m_rope1.Step(o,this.m_iterations[0],this.m_position1),this.m_rope2.Step(o,this.m_iterations[1],this.m_position2),super.Step(e,r);let l=e.m_debugDraw;this.m_rope1.Draw(l),this.m_rope2.Draw(l)}};P("Rope","Bending",Df);var Mf=class extends R{constructor(){super();let t;{t=this.m_world.CreateBody();let o=new I;o.SetTwoSided(new n(-20,0),new n(20,0)),t.CreateFixture({shape:o})}let e;{e=this.m_world.CreateBody({type:2,position:{x:0,y:4}});let o=new V;o.m_radius=1,e.CreateFixture({shape:o,friction:.6,density:2})}let r;{r=this.m_world.CreateBody({type:2,position:{x:4,y:8}});let o=new V;o.m_radius=1,r.CreateFixture({shape:o,friction:.6,density:2})}{let o=new ls;o.Initialize(e,r),o.maxForce=1e3,o.maxTorque=1e3,this.m_joint=this.m_world.CreateJoint(o)}}};P("Bugs","Motor Joint (Bug #487)",Mf);var Rf=class extends R{constructor(){super();let t=this.m_world.CreateBody();{let e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e}),e.SetTwoSided(new n(-40,0),new n(-40,25)),t.CreateFixture({shape:e}),e.SetTwoSided(new n(40,0),new n(40,25)),t.CreateFixture({shape:e})}{let e=new qa,r=0,o=10,s=5,l=5,a=20,c=.5;for(let p=0;p{let d=c.CreateBody({type:2,position:u}),h=new v;m?h.SetAsBox(.5*e,.5*t):h.SetAsBox(.5*t,.5*e),d.CreateFixture({shape:h,density:1/(t*e),friction:.6,restitution:0})},o=this.m_world,s=o.CreateBody(),l=new I;l.SetTwoSided(new n(-600,-240),new n(600,-240)),s.CreateFixture({shape:l,friction:1,restitution:1});let a=12;for(let c=0;c3&&(s*=.8);let m=r*.5+(r+2*t)*.99*u;for(let p=0;p{t.frictionModifier>this.m_currentTraction&&(this.m_currentTraction=t.frictionModifier)}))}getLateralVelocity(){let t=this.m_body.GetWorldVector(new n(1,0),new n);return t.Scale(n.Dot(t,this.m_body.GetLinearVelocity()))}getForwardVelocity(){let t=this.m_body.GetWorldVector(new n(0,1),new n);return t.Scale(n.Dot(t,this.m_body.GetLinearVelocity()))}updateFriction(){let t=this.getLateralVelocity().Scale(-1*this.m_body.GetMass());t.Length()>this.m_maxLateralImpulse&&t.Scale(this.m_maxLateralImpulse/t.Length()),this.m_body.ApplyLinearImpulse(t.Scale(this.m_currentTraction),this.m_body.GetWorldCenter()),this.m_body.ApplyAngularImpulse(this.m_currentTraction*.1*this.m_body.GetInertia()*-this.m_body.GetAngularVelocity());let e=this.getForwardVelocity(),o=-2*e.Normalize();this.m_body.ApplyForce(e.Scale(this.m_currentTraction*o),this.m_body.GetWorldCenter())}updateDrive(t){if(t.up===t.down)return;let e=0;t.up?e=this.m_maxForwardSpeed:t.down&&(e=this.m_maxBackwardSpeed);let r=this.m_body.GetWorldVector(new n(0,1),new n),o=n.Dot(this.getForwardVelocity(),r),s=0;if(e>o)s=this.m_maxDriveForce;else if(e{u.updateFriction()}),this.m_tires.forEach(u=>{u.updateDrive(t)});let e=35*xu,o=160*xu/60,s=0;t.left&&(s+=e),t.right&&(s-=e);let l=this.flJoint.GetJointAngle(),a=s-l;a=tt(a,-o,o);let c=l+a;this.flJoint.SetLimits(c,c),this.frJoint.SetLimits(c,c)}},Ef=class i extends R{constructor(){super(n.ZERO);this.m_controlState={left:!1,right:!1,up:!1,down:!1};{this.m_groundBody=this.m_world.CreateBody();let e=new v,r={shape:e,isSensor:!0,userData:{groundArea:new gu(.5,!1)}};e.SetAsBox(9,7,new n(-10,15),20*xu),this.m_groundBody.CreateFixture(r),e.SetAsBox(9,5,new n(5,20),-40*xu),r.userData={groundArea:new gu(.2,!1)},this.m_groundBody.CreateFixture(r)}this.m_car=new Vf(this.m_world)}getHotkeys(){return[Er("a","Turn Left",this.m_controlState,"left"),Er("d","Turn Right",this.m_controlState,"right"),Er("w","Move Forward",this.m_controlState,"up"),Er("s","Move Backward",this.m_controlState,"down")]}static handleContact(e,r){let o=e.GetFixtureA(),s=e.GetFixtureB(),l=o.GetUserData(),a=s.GetUserData();l.tire||a.groundArea?i.tire_vs_groundArea(o,s,r):(l.groundArea||a.tire)&&i.tire_vs_groundArea(s,o,r)}BeginContact(e){i.handleContact(e,!0)}EndContact(e){i.handleContact(e,!1)}static tire_vs_groundArea(e,r,o){let{tire:s}=e.GetBody().GetUserData(),{groundArea:l}=r.GetUserData();s&&l&&(o?s.addGroundArea(l):s.removeGroundArea(l))}Step(e,r){this.m_car.update(this.m_controlState),super.Step(e,r)}};P("Examples","TopDown Car",Ef);var Uf=class extends R{constructor(){super();let t=this.m_world.CreateBody(),e=new I;e.SetTwoSided(new n(-40,0),new n(40,0)),t.CreateFixture({shape:e});let r={x:0,y:5},o=this.m_world.CreateBody({type:2,angularDamping:.1,position:r}),s=new v;s.SetAsBox(.5,.5),o.CreateFixture({shape:s,density:5}),this.m_hertz=1,this.m_dampingRatio=.7;let l=new ke;l.Initialize(t,o,new n(0,15),r),l.collideConnected=!0,this.m_length=l.length,this.m_minLength=this.m_length,this.m_maxLength=this.m_length,Ct(l,this.m_hertz,this.m_dampingRatio,l.bodyA,l.bodyB),this.m_joint=this.m_world.CreateJoint(l)}setupControls(){this.addTestControlGroup("Joint",[_t("Length",0,20,1,this.m_length,t=>{this.m_length=this.m_joint.SetLength(t)}),_t("Min Length",0,20,1,this.m_minLength,t=>{this.m_minLength=this.m_joint.SetMinLength(t)}),_t("Max Length",0,20,1,this.m_maxLength,t=>{this.m_maxLength=this.m_joint.SetMaxLength(t)}),_t("Hertz",0,10,.1,this.m_hertz,t=>{this.m_hertz=t,this.UpdateStiffness()}),_t("Damping Ratio",0,2,.1,this.m_dampingRatio,t=>{this.m_dampingRatio=t,this.UpdateStiffness()})])}UpdateStiffness(){let t={stiffness:0,damping:0};Ct(t,this.m_hertz,this.m_dampingRatio,this.m_joint.GetBodyA(),this.m_joint.GetBodyB()),this.m_joint.SetStiffness(t.stiffness),this.m_joint.SetDamping(t.damping)}};P("Joints","Distance Joint",Uf);var Yf=class extends R{constructor(){super();this.m_motorSpeed=10;this.m_enableMotor=!1;this.m_enableLimit=!0;let e=this.m_world.CreateBody();{let r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r,density:0})}{let r=new V(2),o=new n(0,10),s=this.m_world.CreateBody({type:2,position:o,allowSleep:!1});s.CreateFixture({shape:r,density:5});let l=new cs;l.Initialize(e,s,o,new n(0,1)),l.motorSpeed=this.m_motorSpeed,l.maxMotorTorque=1e4,l.enableMotor=this.m_enableMotor,l.lowerTranslation=-3,l.upperTranslation=3,l.enableLimit=this.m_enableLimit,Ct(l,1,.7,e,s),this.m_joint=this.m_world.CreateJoint(l)}this.addTestControlGroup("Joint",[jt("Limit",this.m_enableLimit,r=>{this.m_enableLimit=this.m_joint.EnableLimit(r)}),jt("Motor",this.m_enableMotor,r=>{this.m_enableMotor=this.m_joint.EnableMotor(r)}),_t("Speed",-100,100,1,this.m_motorSpeed,r=>{this.m_motorSpeed=this.m_joint.SetMotorSpeed(r)})])}Step(e,r){super.Step(e,r);let o=this.m_joint.GetMotorTorque(e.m_hertz);this.addDebug("Motor Torque",o);let s=this.m_joint.GetReactionForce(e.m_hertz,new n);this.addDebug("Reaction Force",`(${s.x.toFixed(1)}, ${s.y.toFixed(1)})`)}};P("Joints","Wheel",Yf);var Jf=class extends R{constructor(){super();this.m_distanceJointDef=new ke;this.m_stabilize=!1;let e=this.m_world.CreateBody();{let r=new I;r.SetTwoSided(new n(-40,0),new n(40,0)),e.CreateFixture({shape:r,density:0})}{let r=new v;r.SetAsBox(.5,.125);let o={shape:r,density:20,friction:.2,filter:{categoryBits:1,maskBits:65533}},s=new q;s.collideConnected=!1;let l=10,a=15;this.m_distanceJointDef.localAnchorA.Set(0,a);let c=e;for(let m=0;m{this.m_stabilize=e,e&&this.m_distanceJoint===null?this.m_distanceJoint=this.m_world.CreateJoint(this.m_distanceJointDef):!e&&this.m_distanceJoint!==null&&(this.m_world.DestroyJoint(this.m_distanceJoint),this.m_distanceJoint=null)})])}Step(e,r){super.Step(e,r),this.addDebug("Distance Joint",this.m_distanceJoint?"ON":"OFF")}};P("Examples","Wrecking Ball",Jf);var jf=class extends R{constructor(){super();let t=new n(0,-10);this.m_world.SetGravity(t);let e=[];e[0]=this.m_world.CreateBody({type:0});{let r=new n(0,1),o=new n(0,0),s=new n(4,0),l=new I;l.SetTwoSided(r,o),e[0].CreateFixture({shape:l}),l.SetTwoSided(o,s),e[0].CreateFixture({shape:l})}e[1]=this.m_world.CreateBody({type:2,position:new n(1,3)});{let r=new v,o=H(8,n);o[0].Set(.5,-3),o[1].Set(.5,3),o[2].Set(-.5,3),o[3].Set(-.5,-3),r.Set(o,4),e[1].CreateFixture({shape:r,friction:.2,density:10})}}};P("Bugs","Chain Problem",jf);var zf=class extends R{constructor(){super();let t=25,e=this.m_world.CreateBody();{let m=new v;m.SetAsBox(.5,2),e.CreateFixture({shape:m}),e=this.m_world.CreateBody({angle:.5*Math.PI,position:{x:t,y:2*t}}),m.SetAsBox(2*t,1.2),e.CreateFixture({shape:m}),e=this.m_world.CreateBody({angle:.5*Math.PI,position:{x:-t,y:2*t}}),e.CreateFixture({shape:m})}let r=26,s=.5*2,l=s*r/2,a=s/2,c=new v;c.SetAsBox(.5,.5);let u=5*r;for(let m=0;m{};this.setLeftTable=()=>{};this.setRightTable=()=>{};this.setTestControlGroups=()=>{};this.particleParameter=new su(this);for(let{tests:t}of this.groupedTests)this.flatTests.push(...t);this.ownHotKeys=[G("0","Reset Camera",()=>this.HomeCamera()),G("+","Zoom In",()=>this.ZoomCamera(1.1)),G("-","Zoom Out",()=>this.ZoomCamera(.9)),G("r","Reload Test",()=>this.LoadTest()),G("o","Single Step",()=>this.SingleStep()),G("p","Pause/Continue",()=>this.SetPause(!this.m_settings.m_pause)),G("PageUp","Previous Test",()=>this.DecrementTest()),G("PageDown","Next Test",()=>this.IncrementTest())]}init(t,e,r,o,s,l,a){this.setLeftTable=s,this.setRightTable=l,this.activateTest=o,this.setTestControlGroups=a,e.addEventListener("mousedown",u=>this.HandleMouseDown(u)),e.addEventListener("mouseup",u=>this.HandleMouseUp(u)),e.addEventListener("mousemove",u=>this.HandleMouseMove(u)),e.addEventListener("wheel",u=>this.HandleMouseWheel(u)),e.addEventListener("mouseenter",()=>{this.m_hoveringCanvas=!0}),e.addEventListener("mouseleave",()=>{this.m_hoveringCanvas=!1});let c=()=>{let{clientWidth:u,clientHeight:m}=r;(e.width!==u||e.height!==m)&&(e.width=t.width=u,e.height=t.height=m,ot.resize(u,m),this.m_test?.Resize(u,m),this.gl&&vd(t,this.gl,u,r.clientHeight))};if(window.addEventListener("resize",c),window.addEventListener("orientationchange",c),c(),this.m_ctx=e.getContext("2d"),!this.m_ctx)throw new Error("Could not create 2d context for debug-draw");this.m_settings.m_debugDraw=new Fn(this.m_ctx),window.addEventListener("contextmenu",u=>{u.target instanceof HTMLElement&&u.target.closest("main")&&u.preventDefault()},!0),window.addEventListener("keydown",u=>this.HandleKey(u,!0)),window.addEventListener("keyup",u=>this.HandleKey(u,!1)),this.LoadTest(),this.prepareGl(t)}async prepareGl(t){this.gl=W0(t),this.textures=await O0(this.gl),this.defaultShader=q0(this.gl),this.LoadTest()}setTest(t,e){this.testTitle=t,this.testConstructor=e,this.LoadTest()}HomeCamera(){let t=this.m_test?this.m_test.GetDefaultViewZoom():25,e=this.m_test?this.m_test.getCenter():n.ZERO;ot.setPositionAndZoom(e.x,e.y,t)}ZoomCamera(t){ot.setZoom(tt(ot.getZoom()*t,.5,500))}HandleMouseMove(t){let e=new n(t.offsetX,t.offsetY),r=ot.unproject(e,new n);if(this.m_mouse.Copy(e),this.m_test?.MouseMove(r,this.m_lMouseDown),this.m_rMouseDown){let{x:o,y:s}=ot.getCenter(),l=1/ot.getZoom();ot.setPosition(o-t.movementX*l,s+t.movementY*l)}}HandleMouseDown(t){let e=new n(t.offsetX,t.offsetY),r=ot.unproject(e,new n);switch(t.button){case 0:this.m_lMouseDown=!0,t.shiftKey?this.m_test?.ShiftMouseDown(r):this.m_test?.MouseDown(r);break;case 2:this.m_rMouseDown=!0;break}}HandleMouseUp(t){let e=new n(t.offsetX,t.offsetY),r=ot.unproject(e,new n);switch(t.button){case 0:this.m_lMouseDown=!1,this.m_test?.MouseUp(r);break;case 2:this.m_rMouseDown=!1;break}}HandleMouseWheel(t){this.m_hoveringCanvas&&(t.deltaY<0?this.ZoomCamera(1.1):t.deltaY>0&&this.ZoomCamera(1/1.1),t.preventDefault())}HandleKey(t,e){if(this.m_hoveringCanvas||!e){let{key:r}=t,o=this.allHotKeys.find(s=>s.key===r);o&&(!!this.m_keyMap[r]!==e&&(o.step||o.callback(e),this.m_keyMap[r]=e),this.m_hoveringCanvas&&t.preventDefault())}}DecrementTest(){let t=this.flatTests.findIndex(e=>e.name===this.testTitle)-1;t<0?this.activateTest(this.flatTests[this.flatTests.length-1]):t>=0&&this.activateTest(this.flatTests[t])}IncrementTest(){let t=this.flatTests.findIndex(e=>e.name===this.testTitle)+1;t>=this.flatTests.length?this.activateTest(this.flatTests[0]):t>0&&this.activateTest(this.flatTests[t])}LoadTest(t=!1){let e=this.testConstructor;if(!(!e||!this.m_ctx||!this.gl||!this.defaultShader||!this.textures||!this.m_settings.m_debugDraw)){t||this.particleParameter.Reset(),this.m_test?.Destroy(),this.m_test=new e({gl:this.gl,shader:this.defaultShader,textures:this.textures,particleParameter:this.particleParameter}),this.m_test.setupControls(),this.testBaseHotKeys=this.m_test.getBaseHotkeys(),this.testHotKeys=this.m_test.getHotkeys(),this.allHotKeys=[...this.ownHotKeys,...this.testBaseHotKeys,...this.testHotKeys],this.stepHotKeys=this.allHotKeys.filter(r=>r.step);for(let r of this.allHotKeys){let o=this.allHotKeys.find(s=>r.key===s.key);o&&r!==o&&console.error(`Conflicting keys "${r.description}" and "${o.description}"`)}t||this.HomeCamera(),this.setTestControlGroups(this.m_test.m_testControlGroups.slice())}}SetPause(t){this.m_settings.m_pause=t,this.onPauseChanged.emit(t)}SingleStep(){this.m_settings.m_pause||(this.m_settings.m_pause=!0,this.onPauseChanged.emit(!0)),this.m_settings.m_singleStep=!0}scheduleRestart(){this.shouldRestart=!0}SimulationLoop(){let t=this.m_settings.m_debugDraw;if(this.m_fpsCalculator.addFrame()<=0||!this.gl||!this.defaultShader||!this.m_ctx||!t)return;ds(this.gl,0,0,0,0),this.gl.enable(this.gl.BLEND),this.defaultShader.use(),this.defaultShader.uMVMatrix.set(!1,ot.modelView),this.defaultShader.uPMatrix.set(!1,ot.projection);let e=ot.getCenter(),r=ot.getZoom();if(t.Prepare(e.x,e.y,r,!0),this.m_test?.RunStep(this.m_settings),this.m_hoveringCanvas)for(let o of this.stepHotKeys)this.m_keyMap[o.key]&&o.callback(!0);t.Finish(),this.m_settings.m_drawFpsMeter&&this.DrawFpsMeter(this.m_ctx),this.UpdateText(),this.shouldRestart&&(this.shouldRestart=!1,this.LoadTest(!0))}DrawFpsMeter(t){t.save(),t.translate(0,ot.getHeight()),t.scale(1,-1),t.fillStyle=Fn.MakeStyleString(F.GREEN);let e=5;for(let r of this.m_fpsCalculator.getFrames())t.fillRect(e,5,1,r),e++;t.restore()}UpdateText(){let t=[],e=this.m_fpsCalculator.getFps(),r=[["Performance:","!"],["Avg. FPS",e.avgFps.toFixed(1)],["Max. Time in ms",e.maxTime.toFixed(1)],["Min. Time in ms",e.minTime.toFixed(1)],["",""]];this.m_test&&(this.m_test.m_textLines.length&&t.push(["Description:","!"],...this.m_test.m_textLines.map(o=>[o,"-"]),["",""]),this.m_settings.m_drawInputHelp&&(t.push(["Mouse:","!"],["Right Drag","Move Camera"],["Left Drag","Grab Objects"],["Wheel","Zoom"],["",""]),t.push(["Keyboard:","!"],...this.allHotKeys.map(o=>[yA(o),o.description]),["",""])),this.m_test.m_debugLines.length&&r.push(["Debug Info:","!"],...this.m_test.m_debugLines,["",""]),this.m_test.m_statisticLines.length&&r.push(["Statistics:","!"],...this.m_test.m_statisticLines,["",""])),this.setLeftTable(t),this.setRightTable(r)}},xA=(0,Su.createContext)(new Xf),tl=()=>(0,Su.useContext)(xA);var Rx=/[^a-z0-9-]+/gi,sr=({group:i,name:t})=>`/${i.replace(Rx,"_")}#${t.replace(Rx,"_")}`;function kx(i,t){return JSON.stringify(i)!==JSON.stringify(t)?t:i}var gA=({label:i,value:t})=>t==="!"?ft.default.createElement("tr",null,ft.default.createElement("th",{colSpan:2},i)):t==="-"?ft.default.createElement("tr",null,ft.default.createElement("td",{colSpan:2},i)):ft.default.createElement("tr",null,ft.default.createElement("td",null,t),ft.default.createElement("td",null,i)),Fx=({id:i,table:t})=>ft.default.createElement("div",{id:i},ft.default.createElement("table",null,ft.default.createElement("tbody",null,t.map(([e,r],o)=>ft.default.createElement(gA,{key:o,label:e,value:r}))))),SA=({entry:{name:i,TestClass:t},setTestControlGroups:e})=>{let[r,o]=(0,ft.useReducer)(kx,[]),[s,l]=(0,ft.useReducer)(kx,[]),a=(0,ft.useRef)(null),c=(0,ft.useRef)(null),u=(0,ft.useRef)(null),m=tl(),p=Zi(),d=Ix();return(0,ft.useEffect)(()=>{let h=a.current,f=c.current,b=u.current;if(h&&f&&b){let x=()=>{try{m.SimulationLoop(),window.requestAnimationFrame(x)}catch(y){console.error("Error during simulation loop",y)}},_=()=>{let y=g=>{p.history.push(sr(g))};m.init(h,f,b,y,o,l,e),window.requestAnimationFrame(x)};window.requestAnimationFrame(_)}},[c.current,a.current,u.current,m]),(0,ft.useEffect)(()=>{m.setTest(i,t)},[m,t]),ft.default.createElement("main",{ref:u},ft.default.createElement("canvas",{ref:a}),ft.default.createElement("canvas",{ref:c}),ft.default.createElement(Fx,{id:"left_overlay",table:r}),ft.default.createElement("div",{id:"title_overlay"},d?.name??""),ft.default.createElement(Fx,{id:"right_overlay",table:s}))};function Ix(){let i=Zi(),t=decodeURIComponent(i.path);return tl().flatTests.find(r=>sr(r)===t)}var Lx=({setTestControlGroups:i})=>{let t=Ix();return t?ft.default.createElement(SA,{entry:t,setTestControlGroups:i}):ft.default.createElement("main",null,"Select a test from the right sidebar")};var At=mt(Ut());var Or=mt(Ut());var Gx=mt(Ut());var Vx=()=>Gx.default.createElement("div",{className:"separator"});var vs=mt(Ut());var wu=({className:i,legend:t,legendClassName:e,children:r,defaultOpen:o=!1})=>{let[s,l]=(0,vs.useState)(o),a=[];return e&&a.push(e),s&&a.push("open-legend"),vs.default.createElement("fieldset",{className:`section ${i??""}`},vs.default.createElement("legend",{onClick:()=>l(!s),tabIndex:0,className:a.join(" ")},t),vs.default.createElement("div",{className:s?"section-content":"section-content section-content-hidden"},r))};var wA=({control:i})=>{switch(i.type){case"slider":return Or.default.createElement(Cx,{control:i});case"checkbox":return Or.default.createElement(px,{control:i});case"radio":return Or.default.createElement(Bx,{control:i});case"select":return Or.default.createElement(mx,{control:i});case"separator":return Or.default.createElement(Vx,null)}return null},Bs=({legend:i,controls:t,defaultOpen:e})=>Or.default.createElement(wu,{legend:i,defaultOpen:e,className:"settings-section"},t.map(r=>Or.default.createElement(wA,{key:r.name,control:r})));function re(i,t,e){let r=i[t];return jt(e,r,o=>{i[t]=o})}function el(i,t,e,r,o,s){let l=i[t];return _t(e,r,o,s,l,a=>{i[t]=a})}var Ex=mt(Ut());var vu=({label:i,onClick:t})=>Ex.default.createElement("button",{className:"button",onClick:t},i);var Hf=mt(Ut());var Ux=({name:i,link:t,tests:e})=>{let r=e.some(o=>t===sr(o));return Hf.default.createElement(wu,{legend:i,legendClassName:r?"active-legend":""},e.map(o=>Hf.default.createElement(I2,{href:sr(o),key:o.name,className:t===sr(o)?"active-link":""},o.name)))};var Yx=({testControlGroups:i})=>{let[t,e]=(0,At.useState)("controls"),[r,o]=(0,At.useState)(!1),s=tl(),l=Zi(),a=decodeURIComponent(l.path),c=(0,At.useMemo)(()=>s.groupedTests.some(f=>f.tests.some(b=>a===sr(b))),[s,a]);(0,At.useEffect)(()=>{let f=s.onPauseChanged.connect(o);return()=>{f.disconnect()}}),(0,At.useEffect)(()=>{!c&&t!=="tests"&&e("tests")},[c,t]);let u=s.m_settings,m=[el(u,"m_velocityIterations","Velocity Iters",0,50,1),el(u,"m_positionIterations","Position Iters",0,50,1),el(u,"m_particleIterations","Particle Iters",0,50,1),el(u,"m_hertz","Hertz",5,120,1)],p=[re(u,"m_enableSleep","Sleep"),re(u,"m_enableWarmStarting","Warm Starting"),re(u,"m_enableContinuous","Time of Impact"),re(u,"m_enableSubStepping","Sub-Stepping")],d=[re(u,"m_drawShapes","Shapes"),re(u,"m_drawParticles","Particles"),re(u,"m_drawJoints","Joints"),re(u,"m_drawAABBs","AABBs"),re(u,"m_drawContactPoints","Contact Points"),re(u,"m_drawContactNormals","Contact Normals"),re(u,"m_drawContactImpulse","Contact Impulses"),re(u,"m_drawFrictionImpulse","Friction Impulses"),re(u,"m_drawCOMs","Center of Masses")],h=[re(u,"m_drawStats","Statistics"),re(u,"m_drawInputHelp","Input Help"),re(u,"m_drawProfile","Profile"),re(u,"m_drawFpsMeter","FPS Meter")];return At.default.createElement("div",{className:"sidebar"},At.default.createElement("div",{className:"sidebar--tabs"},At.default.createElement("div",{onClick:()=>e("controls"),className:t==="controls"?"active-tab":""},"Controls"),At.default.createElement("div",{onClick:()=>e("tests"),className:t==="tests"?"active-tab":""},"Tests")),At.default.createElement("div",{className:t==="controls"?"tab-content":"tab-content tab-content-hidden"},At.default.createElement(Bs,{legend:"Iterations",controls:m}),At.default.createElement(Bs,{legend:"General",controls:p}),At.default.createElement(Bs,{legend:"Draw",controls:d}),At.default.createElement(Bs,{legend:"Overlay",controls:h}),i.groups.map((f,b)=>At.default.createElement(Bs,{defaultOpen:!0,legend:`[Test] ${f.legend}`,key:`${i.key}-${b}`,controls:f.controls}))),At.default.createElement("div",{className:t==="tests"?"tab-content":"tab-content tab-content-hidden"},s.groupedTests.map(({name:f,tests:b})=>At.default.createElement(Ux,{key:f,name:f,tests:b,link:a}))),t==="controls"&&At.default.createElement("div",{className:"sidebar--buttons"},At.default.createElement(vu,{label:r?"Continue (P)":"Pause (P)",onClick:()=>s.SetPause(!r)}),At.default.createElement(vu,{label:"Single Step (O)",onClick:()=>s.SingleStep()}),At.default.createElement(vu,{label:"Restart (R)",onClick:()=>s.LoadTest(!0)})))};var Jx={name:"@box2d/testbed",version:"0.10.0",private:!0,description:"A Testbed for all @box2d packages",homepage:"https://lusito.github.io/box2d.ts/",bugs:{url:"https://github.com/lusito/box2d.ts/issues"},repository:{type:"git",url:"https://github.com/lusito/box2d.ts.git"},license:"MIT",author:"Santo Pfingsten",files:["dist"],scripts:{build:"rimraf dist && node src/ui/esbuild.js",start:"node src/ui/esbuild.js serve"},browserslist:["> 5%"],dependencies:{"@box2d/controllers":"^0.11.0","@box2d/core":"^0.11.0","@box2d/debug-draw":"^0.11.0","@box2d/lights":"^0.11.0","@box2d/particles":"^0.11.0","@react-nano/router":"^0.15.0","@types/randomcolor":"^0.5.9","@types/react":"^18.3.3","@types/react-dom":"^18.3.0",esbuild:"^0.21.5","esbuild-sass-plugin":"^3.3.1","esbuild-ts-paths":"^1.1.3","gl-matrix":"^3.4.3",react:"^18.3.1","react-dom":"^18.3.1","sort-package-json":"^2.10.0","typed-glsl":"0.11.2","typed-signals":"^3.0.0","typeface-open-sans":"1.1.13"}};var BA={key:0,groups:[]};function CA(i,t){return{key:i.key+1,groups:t}}function AA(){let[i,t]=(0,Nr.useReducer)(CA,BA);return Nr.default.createElement("div",{className:"container"},Nr.default.createElement(Lx,{setTestControlGroups:t}),Nr.default.createElement(Yx,{testControlGroups:i}))}document.title=`@Box2D Testbed v${Jx.version}`;var TA=(0,jx.createRoot)(document.getElementById("root"));TA.render(Nr.default.createElement(U2,{mode:"hash"},Nr.default.createElement(AA,null)));})(); /*! Bundled license information: react/cjs/react.production.min.js: