Component Wise Operations

Component wise operations between two quaternions are trivial to implement. The thing to keep in mind is that component wise operations affect the length of the quaternion. In games we mostly deal with unit quaternions, quaternions often need to be normalized after component wise operations.

Addition, Subtraction, Scale

Addition, subtraction and scale are all performed the same way. Apply the operation to each componetn of both quaternions.

Quaternion Add(Quaternion left, Quaternion right) {
    return Quaternion(
        left.x + right.x,
        left.y + right.y,
        left.z + right.z,
        left.w + right.w
    );
}

Quaternion Subtract(Quaternion left, Quaternion right) {
    return Quaternion(
        left.x - right.x,
        left.y - right.y,
        left.z - right.z,
        left.w - right.w
    );
}

Quaternion Scale(Quaternion quat, float scalar) {
    return Quaternion(
        quat.x * scalar,
        quat.y * scalar,
        quat.z * scalar,
        quat.w * scalar
    );
}

Negation

A quaternion can be negated by scaling it to the negative of its scale. The negative of a quaternion is NOT the same as its inverse. Perhaps un-intuitivley, a negated quaternion performs the same rotation as it's positive counterpart. That's because both the axis of rotation and angle of rotation are flipped when negating a quaternion.

Quaternion Negate(Quaternion quat) {
    return Quaternion(
        quat.x * -1.0f,
        quat.y * -1.0f,
        quat.z * -1.0f,
        quat.w * -1.0f
    );
}

Comparison

Comparing quaternions by component is trivial, but incomplete. A quaternion and it's negative represent the same rotation. To compare if two quaternions are the same, we need to check if the quaternions are component wise equal, or if one of them negated is equal component wise.

bool Same(Quaternion left, Quaternion right) {
    return (abs(left.x - right.x) <= EPSILON && abs(left.y - right.y) <= EPSILON && abs(left.z - right.z) <= EPSILON && abs(left.w - left.w) <= EPSILON)
        || (abs(left.x + right.x) <= EPSILON && abs(left.y + right.y) <= EPSILON && abs(left.z + right.z) <= EPSILON && abs(left.w + left.w) <= EPSILON);
}

Component wise comparison is still useful to check if a quaternion has changed or not.

bool Equals(Quaternion left, Quaternion right) {
    return (abs(left.x - right.x) <= EPSILON && abs(left.y - right.y) <= EPSILON && abs(left.z - right.z) <= EPSILON && abs(left.w - left.w) <= EPSILON);
}