Maths behind Transform.TransformDirection()?

Hi there. I’d like to know how Transform.TransformDirection() works in vector maths terms.

“TransformDirection() Transforms direction from local space to world space. This operation is not affected by scale or position of the transform. The returned vector has the same length as direction.”

So is it just:

float originalMagnitude = v.magnitude;
Vector3 worldV = transform.localToWorldMatrix.MultiplyVector(v);
worldV = worldV.normalized * originalMagnitude;

Probably not, since that would not work with scaling.
So I guess it uses the transpose of the inverse of localToWorld matrix, like so:

float originalMagnitude = v.magnitude;
Vector3 worldV = transform.worldToLocalMatrix.transpose.MultiplyVector(v);
worldV = worldV.normalized * originalMagnitude;

This would make this function suitable for transforming normals.
Is this code correct?
Is there a better (implicit?) way to preserve vector length than normalizing and multiplying by originalMagnitude?

Edit:
From discussion below it appears that transform.TransformDirection(v) is equivalent to transform.rotation * v.

Take Vector3 B = transform.TransformDirection(Vector3.right); as an example.

Says to apply your rotation to real-world right, to get my right. Rotations are quaternions, which apply using *, so the statement is really:

Vector3 B = transform.rotation * Vector3.right;

Using matrixes (quaternions replace them,) the worldToLocal matrix upper-left 3x3 (no translation data) seems to work.

“This operation is not affected by scale or position of the transform. The returned vector has the same length as direction”

For example, bob.TransformDirection(Vector3.forward*-10) returns a length 10 vector pointing along Bob’s -Z. Preserves the length. Easy to think of it saying “make a 10 -z arrow, using the transform’s directions.” You’d commonly use bob.position + bob.TransDir(...) to maybe place a camera 10 behind him.

People can be sloppy about scaling. Bob may be tiny but scaled *10 to be a meter tall. Seems natural to ignore that. 10 behind means 10 behind. Plus the shortcuts – bob.forward – can now always be unit vectors.

The other one, TransformPoint, using scale (and pos) is odder, but: say you’re using transform.TransPoint(V3.forward*1.5) to spawn a bullet at the end of your turret (the docs use this example.) The alternate is placing an empty child mountPoint 1.5 units forward. Children offsets currently scale with the parent. So, it sort of makes sense that TransPoint would also use scale.

In simple terms, you can say “use TransPoint to place objects around you.” If you sloppily change the scale, the bullet or jet engine blast will move to be correct.