The solution I’m currently using seems to be missing here.

Assuming that the plane normal is normalized (`|Vn| == 1`

), the signed angle is simply:

For the right-handed rotation from Va to Vb:

`atan2((Va x Vb) . Vn, Va . Vb)`

For the left-handed rotation from Va to Vb:

`atan2((Vb x Va) . Vn, Va . Vb)`

which returns an angle in the range [-PI, +PI] (or whatever the available atan2 implementation returns).

`.`

and `x`

are the dot and cross product respectively.

No explicit branching and no division/vector length calculation is necessary.

Explanation for why this works: let alpha be the direct angle between the vectors (0° to 180°) and beta the angle we are looking for (0° to 360°) with `beta == alpha`

or `beta == 360° - alpha`

```
Va . Vb == |Va| * |Vb| * cos(alpha) (by definition)
== |Va| * |Vb| * cos(beta) (cos(alpha) == cos(-alpha) == cos(360° - alpha)
Va x Vb == |Va| * |Vb| * sin(alpha) * n1
(by definition; n1 is a unit vector perpendicular to Va and Vb with
orientation matching the right-hand rule)
Therefore (again assuming Vn is normalized):
n1 . Vn == 1 when beta < 180
n1 . Vn == -1 when beta > 180
==> (Va x Vb) . Vn == |Va| * |Vb| * sin(beta)
```

Finally

```
tan(beta) = sin(beta) / cos(beta) == ((Va x Vb) . Vn) / (Va . Vb)
```