This is a demonstration how any rotation of a vector s can be achieved by two successive reflections.
A reflection of vector s at vector u is achieved by first projecting s onto u and then adding the difference betwen s and the projection.
A reflection of vector s at vector u followed by a reflection at vector v results
in a rotation of s in the plane spanned by
vectors u and v.
The angle of the rotation is twice as large as the angle between vector u and vector v.
In general to construct a rotation of angle α you just need to construct two vectors that enclose an angle and then use them
as reflectors for the subject vector.α / 2
The pair of vector u and v is called a rotor.
Notice that only the relative orientation between u and v affect the rotation result. Try drag the arc segment called rotor below to change the direction of both
reflectors at once.
Subject together with the pair (First, Second) of reflection vectors of the rotor.Subject is decomposed into the
component Projected onto First reflector and the orthogonal component. The Reflected vector
is recomposed from those components.First reflection is then reflected again. This time it is Projected onto the Second Reflector. The result is the Rotated vector.Subject and Rotated is the
sum of two angles. The one angle is twice the difference between Subject and First Reflector. The second angle is
twice the difference between the First Reflection and the Rotated result.Take a look at the implementation below. No trigonometric use of sin or cos is needed to perform the rotations.
The construction of any transformation by only composing reflections sits at the core of Geometric Algebra.
// dot ~ similarity in direction
const dot = (a, b) => a.x * b.x + a.y * b.y
// det ~ deviation in direction
const det = (u, v) => u.x * v.y - u.y * v.x
const len2 = (v) => dot(v,v)
const len = (v) => Math.sqrt(len2(v))
const scale = (s,v) => ({x: s * v.x, y: s * v.y})
const norm = (v) => scale(1 / len(v), v)
const add = (u, v) => ({x: u.x + v.x, y: u.y + v.y})
const subtract = (u, v) => add(u, scale(-1, v))
const project = (t, s) => scale(dot(t, s) / len2(s), s)
const reflect = (r,s) => {
const projected = project(s, r);
const reflected = subtract(scale(2, projected), s);
return reflected;
}
const rotate =
(a, b, s) => reflect(b, reflect(a, s))
const rotateHalf =
(a, b, s) => rotate(scale(0.5, add(a, b)), b, s)
The same approch also works for 3 dimensions and higher dimensions.
Below you can see the subject vector rotated
in the plane spanned by the first and second reflector.
The reflection at the first reflector mirrors the subject onto the first reflection on the opposite side of the plane. The following reflection at the second reflector brings the
vector back to the original side of the plane. So the orientation
between the subject and the rotation plane is restored.
During this zic-zac motion across the plane the vector still rotates along the plane in the except same ways as in the 2d case above.