UNPKG

planck

Version:

2D JavaScript/TypeScript physics engine for cross-platform HTML5 game development

80 lines (70 loc) • 3.5 kB
// Reproduces https://github.com/piqnt/planck.js/issues/315 on planck 1.5.0. Needed, all at once: // - an edge or chain segment short enough to lie entirely under a polygon face, entering the polygon // from the face side: the manifold then takes the polygon face as its reference face and its two // points are the segment's ends, which 1.5.0 tagged with the same contact id; // - uneven load on those two points (here: the joint pulls off-centre); // - a body that can never resolve the overlap: a joint pulling it into the segment, and a pocket that // leaves it no way out. // Warm starting then puts the stored impulses on the wrong points every step and they grow by a constant // factor until they overflow, and World.step() throws from the time-of-impact solver. Exact values matter; // a plain Edge works too but is more fragile, the chain's ghost segments help hold the jam. import { World, Vec2, Box, Chain, RevoluteJoint, Testbed } from "planck"; const world = new World({ gravity: { x: 0, y: -10 } }); const testbed = Testbed.mount(); testbed.x = 0; testbed.y = 0; testbed.width = 6; testbed.height = 4; testbed.hz = 240; testbed.info("Box wedged in a pocket and pulled into a chain (#315): the joint impulse should stay small"); testbed.start(world); const hw = 0.8420396029949189; // box half width const hh = 0.4911016643047333; // box half height const top = 0.31806490421295164; // the box's top face; the chain runs along y = 0, 0.31806490421295164 below it const clearance = 0.01; // the pocket const pocket = world.createBody({ type: "static" }); pocket.createFixture({ shape: new Box(hw + 1, 0.5, { x: 0, y: top - 2 * hh - clearance - 0.5 }) }); // floor pocket.createFixture({ shape: new Box(0.5, 2, { x: -hw - clearance - 0.5, y: top - hh }) }); // left wall pocket.createFixture({ shape: new Box(0.5, 2, { x: hw + clearance + 0.5, y: top - hh }) }); // right wall pocket.createFixture({ shape: new Box(hw + 1, 0.5, { x: 0, y: top + clearance + 0.5 }) }); // ceiling // the chain segment through the box, with a ghost vertex at each end const edgeX = 0.11306583743303378; const edgeLen = 0.9754352510894264; const ghostY = 0.18355602025985718; const chain = world.createBody({ type: "static" }); chain.createFixture({ shape: new Chain( [ new Vec2(edgeX - edgeLen / 2 - 0.4, ghostY), new Vec2(edgeX - edgeLen / 2, 0), new Vec2(edgeX + edgeLen / 2, 0), new Vec2(edgeX + edgeLen / 2 + 0.4, ghostY), ], false, ), }); // the box, frictionless, never allowed to sleep const box = world.createBody({ type: "dynamic", position: { x: 0, y: top - hh }, allowSleep: false }); box.createFixture({ shape: new Box(hw, hh), density: 1, friction: 0 }); // the joint, created already violated: its ground anchor is `pull` above its box anchor const px = -0.5868638832898313; const pull = 0.4969154834747314; const pivot = world.createBody({ type: "static" }); const joint = world.createJoint( new RevoluteJoint( { localAnchorA: { x: px, y: top + pull }, localAnchorB: box.getLocalPoint({ x: px, y: top }) }, pivot, box, ), )!; let time = 0; testbed.step = function (dt) { time += dt / 1000; const impulse = Math.hypot(joint.m_impulse.x, joint.m_impulse.y); const a = joint.getAnchorA(); const b = joint.getAnchorB(); testbed.status("time", time.toFixed(2) + " s"); testbed.status("joint impulse", impulse.toExponential(2)); testbed.status("anchor error", Math.hypot(a.x - b.x, a.y - b.y).toFixed(3) + " m"); };