A hit off to one side of a body's center does not just slow it - it spins it. Today you upgrade the impulse resolver with the r cross n terms that couple contacts to rotation.
Resolve a contact at a specific point, producing both a linear and an angular velocity change.
Until now every impulse acted as if it hit the body’s dead center. A real contact
happens at a point, and a point off to the side does two things at once: it changes
the linear velocity and it applies a torque that changes the angular velocity. The
upgraded resolver accounts for this. The velocity used in the closing-speed test is the
point velocity, Velocity + omega x r, where r is the contact point relative to
the center and omega x r is the CrossSV helper. And the impulse denominator gains a
rotational term per body, (r cross n)^2 * invInertia, which makes a body that is easy
to spin absorb more of the impulse as rotation.
Applying the impulse P = j * n now updates two quantities per body: velocity by
imB * P as before, and angular velocity by invInertia * (r cross B). A box caught on
its corner by the rising ground picks up spin as well as an upward push - here angular
velocity 1.2. A contact exactly at the center has r cross n = 0, so the rotational
term vanishes and you recover the plain linear impulse, which is why every earlier
center-contact test still holds. This is the real collision response that Chris Hecker’s
rigid-body series derives.
func CrossSV(s float64, v Vec2) Vec2 { return Vec2{-s * v.Y, s * v.X} } // omega x r// rA, rB = contact - center; point velocity = Velocity + CrossSV(AngularVelocity, r)// vn from point velocities; rnA, rnB = Cross(rA, n), Cross(rB, n)// denom = imA + imB + rnA*rnA*invIA + rnB*rnB*invIB// apply P = j*n: Velocity += imB*P; AngularVelocity += invIB*Cross(rB, P)