(renderable: Renderable, p: Vector2d)
| 650 | } |
| 651 | |
| 652 | setPosition(renderable: Renderable, p: Vector2d): void { |
| 653 | const body = this.bodyMap.get(renderable); |
| 654 | if (body) { |
| 655 | // `p` is in renderable.pos space (typically top-left). Matter's |
| 656 | // body.position is the centroid — convert via the offset stored |
| 657 | // at addBody time so syncFromPhysics keeps renderable.pos at the |
| 658 | // requested value. |
| 659 | const off = this.posOffsets.get(renderable); |
| 660 | Matter.Body.setPosition(body, { |
| 661 | x: p.x - (off?.x ?? 0), |
| 662 | y: p.y - (off?.y ?? 0), |
| 663 | }); |
| 664 | // Matter caches contact pairs across steps for solver continuity. |
| 665 | // After a discontinuous teleport the cached pair still holds the |
| 666 | // previous penetration depth + normal, and matter's Baumgarte |
| 667 | // position-correction phase applies that on the next step before |
| 668 | // narrow-phase invalidates the pair — yanking the body back |
| 669 | // toward the old contact point by ≈ penetration depth. Builtin |
| 670 | // and planck handle this natively (planck's `b2Body::SetTransform` |
| 671 | // documents "contacts are updated on the next call to |
| 672 | // b2World::Step"); matter doesn't, so do it ourselves. |
| 673 | this.invalidateContactsFor(body); |
| 674 | } |
| 675 | renderable.pos.x = p.x; |
| 676 | renderable.pos.y = p.y; |
| 677 | } |
| 678 | |
| 679 | private invalidateContactsFor(body: Matter.Body): void { |
| 680 | // Flush the per-body cached position-correction impulse so matter's |
nothing calls this directly
no test coverage detected