I'm trying to modify a physics engine for efficiency. Currently, as objects move around the world, their orientation (a quaternion) is updated every frame, by multiplying by the rotation (another quaternion).

newOrientation = orientation*angularVelocity

This is not very efficient when the objects are far apart and the orientation is not required. What I would like to do is remember the time that orientation was last updated, and rotate only once for a given period of time.

This gives the formula:

newOrientation = orientation*(angularVelocity^timeSinceLastUpdate)

angularVelocity is a quaternion, while timeSinceLastUpdate is a scalar/real value.

What would the formula be to take a quaternion to a scalar power n?

Solution:

The full java class has been posted at:

https://github.com/Kent-H/blue3D/blob/master/Blue3D/src/blue3D/type/QuaternionF.java


Solution 1:

What would the formula be to take a quaternion to a scalar power n?

You'd need some elementary definitions for that.

Let $p=a+bi+cj+dk\in\mathbb{H}$ be a quaternion. Define, conjugacy (1), vector part (2), sign (3) and argument (4), as:

$$\overline{p}=a-bi-cj-dk\,\,\,\,\text{(1)}$$

$$\vec{u}=\frac{p-\overline{p}}{2}\,\,\,\,(2)$$

$$sgn(p)= \begin{cases} \frac{p}{|p|}, & \text{if $p\neq 0$ (3)} \\ 0, & \text{if $p=0$} \end{cases}$$

$$\arg(p)= \begin{cases} \arccos\left(\frac{a}{|p|}\right), & \text{if $p\neq 0$ (4)} \\ undefined, & \text{if $p=0$} \end{cases}$$

Exponential and logarithmic functions can now be defined, because $\mathbb{H}$ has a division algebra, so

$$\exp(p)=\exp(a)\cdot(\cos(|\vec{u}|)+sgn(\vec{u})\sin(|\vec{u}|))\,\,\,\,\text{(5)}$$

and

$$\ln(p)=\ln(|p|)+sgn(\vec{u})\arg(p)\,\,\,\,\text{(6)}$$

Your scalar power will now be:

$$q^n=\exp(n\ln(q))\,\,\,\,(7)$$

(7) now allows you to calculate directly.

Note that in this case, $n$ is a scalar, so $n\ln(q)=\ln(q)n$, so scalar powers in this case are unique modulo the branch you are working in.

Solution 2:

For anyone who's interested, here is the working (java) code.

The full class will soon be posted at:

https://github.com/Kent-H/blue3D/blob/master/Blue3D/src/blue3D/type/QuaternionF.java

/**
 * sets this quaternion to this^n (for a rotation quaternion, this is equivalent to rotating this by itself n times)
 * This should only work for unit quaternions.
 * @param n power
 * @return this
 */
public final Quaternion pow(float n){
    ln().scale(n).exp();
    return this;
}


public final QuaternionF exp() {
    float r  = (float) Math.sqrt(x*x+y*y+z*z);
    float et = (float) Math.exp(w);
    float s  = r>=0.00001f? et*(float)Math.sin(r)/r: 0f;

    w=et*(float)Math.cos(r);
    x*=s;
    y*=s;
    z*=s;
    return this;
}


public final QuaternionF ln() {
    float r  = (float) Math.sqrt(x*x+y*y+z*z);
    float t  = r>0.00001f? (float)Math.atan2(r,w)/r: 0.f;
    w=0.5f*(float)Math.log(w*w+x*x+y*y+z*z);
    x*=t;
    y*=t;
    z*=t;
    return this;
}

public QuaternionF scale(float scale){
    w*=scale;
    x*=scale;
    y*=scale;
    z*=scale;
    return this;
}