core icon indicating copy to clipboard operation
core copied to clipboard

Using a simpler version of quaternion rotation of a vector (and renames)

Open rcoreilly opened this issue 2 weeks ago • 0 comments

Describe the bug

This is just to document a change made in the pdf branch of math32/quaternion.go to use a simpler and possibly faster version of quaternion rotation. The method was moved from vector3 MulQuat to MulVector as a method on Quat, along with adding an inverse version as well.

This has the change in the pdf branch: 95b6a48d7

How to reproduce

Pasting the set of different versions from different sources below. Not sure where the original came from. Interestingly, all produce different results on non-normalized quaternions. Test added.

Example code

// MulVector applies the rotation encoded in the quaternion to the [Vector3].
func (q Quat) MulVector(v Vector3) Vector3 {
	// note: these each produce the same results when q is normalized
	// but produce very different results for non-normalized,
	// which is presumably not well defined anyway.
	// According to IsaacLab, the cross-product version is faster,
	// but the old version that it replaced is pretty weird.
	// In any case, it is much simpler!
	// https://github.com/isaac-sim/IsaacLab/issues/1711
	// https://github.com/isaac-sim/IsaacLab/pull/2129/files

	// original version:
	// // calculate quat * vector
	// ix := q.W*v.X + q.Y*v.Z - q.Z*v.Y
	// iy := q.W*v.Y + q.Z*v.X - q.X*v.Z
	// iz := q.W*v.Z + q.X*v.Y - q.Y*v.X
	// iw := -q.X*v.X - q.Y*v.Y - q.Z*v.Z
	// // calculate result * inverse quat
	// return Vec3(ix*q.W+iw*-q.X+iy*-q.Z-iz*-q.Y,
	// 	iy*q.W+iw*-q.Y+iz*-q.X-ix*-q.Z,
	// 	iz*q.W+iw*-q.Z+ix*-q.Y-iy*-q.X)

	// warp version:
	// c := 2*q.W*q.W - 1
	// d := 2 * (q.X*v.X + q.Y*v.Y + q.Z*v.Z)
	// return Vec3(v.X*c+q.X*d+(q.Y*v.Z-q.Z*v.Y)*q.W*2,
	// 	v.Y*c+q.Y*d+(q.Z*v.X-q.X*v.Z)*q.W*2,
	// 	v.Z*c+q.Z*d+(q.X*v.Y-q.Y*v.X)*q.W*2)

	// isaacLab
	xyz := Vec3(q.X, q.Y, q.Z)
	t := xyz.Cross(v).MulScalar(2)
	return v.Add(t.MulScalar(q.W)).Add(xyz.Cross(t))
}

// MulVectorInverse applies the inverse of the rotation encoded in the quaternion
// to the [Vector3].
func (q Quat) MulVectorInverse(v Vector3) Vector3 {
	xyz := Vec3(q.X, q.Y, q.Z)
	t := xyz.Cross(v).MulScalar(2)
	return v.Sub(t.MulScalar(q.W)).Add(xyz.Cross(t))
}

Relevant output


Platform

macOS

rcoreilly avatar Dec 15 '25 11:12 rcoreilly