This page was generated from doc/rotation/end-conditions-natural.ipynb. Interactive online version: Binder badge.

“Natural” End Conditions§

notebook about “natural” end conditions for Euclidean splines

[1]:
import numpy as np

helper.py

[2]:
from helper import angles2quat, animate_rotations, display_animation

splines.quaternion.DeCasteljau

[3]:
from splines.quaternion import DeCasteljau
[4]:
def calculate_rotations(control_quaternions):
    times = np.linspace(0, 1, 50)
    return DeCasteljau(
        segments=[control_quaternions],
    ).evaluate(times)
[5]:
q0 = angles2quat(45, 0, 0)
q1 = angles2quat(-45, 0, 0)

Begin§

[6]:
def natural_begin(begin, end_control, end):
    """Return second control quaternion given the other three."""
    return (
        (end_control * end.inverse()) *
        (end * begin.inverse())
    )**(1 / 2) * begin
[7]:
q1_control = angles2quat(-45, 0, -90)
[8]:
ani = animate_rotations({
    'natural begin': calculate_rotations(
        [q0, natural_begin(q0, q1_control, q1), q1_control, q1]),
}, figsize=(4, 3))
[9]:
display_animation(ani, default_mode='reflect')

End§

[10]:
def natural_end(begin, begin_control, end):
    """Return third control quaternion given the other three."""
    return (
        end.inverse() *
        (
            (end * begin.inverse()) *
            (begin * begin_control.inverse())
        )**(1 / 2)).inverse()
[11]:
q0_control = angles2quat(45, 0, 90)
[12]:
ani = animate_rotations({
    'natural end': calculate_rotations(
        [q0, q0_control, natural_end(q0, q0_control, q1), q1]),
}, figsize=(4, 3))
[13]:
display_animation(ani, default_mode='reflect')

(Non-)Symmetries§

Instead of using the function for the begin condition, we could of course also reverse the control quaternions, use the function for the end condition and time-reverse the result. And vice-versa.

Let’s make sure that works:

[14]:
ani = animate_rotations({
    'natural end': calculate_rotations(
        [q0, q0_control, natural_end(q0, q0_control, q1), q1]),
    'natural begin, time-reversed': calculate_rotations(
        [q1, natural_begin(q1, q0_control, q0), q0_control, q0])[::-1],
}, figsize=(6, 3))
[15]:
display_animation(ani, default_mode='reflect')
[16]:
begin = natural_begin(q0, q1_control, q1)
[17]:
ani = animate_rotations({
    'natural begin': calculate_rotations(
        [q0, begin, q1_control, q1]),
    'natural end from natural begin': calculate_rotations(
        [q0, begin, natural_end(q0, begin, q1), q1]),
}, figsize=(6, 3))
[18]:
display_animation(ani, default_mode='reflect')
[19]:
end = natural_end(q0, q0_control, q1)
[20]:
ani = animate_rotations({
    'natural end': calculate_rotations(
        [q0, q0_control, end, q1]),
    'natural begin from natural end': calculate_rotations(
        [q0, natural_begin(q0, end, q1), end, q1]),
}, figsize=(6, 3))
[21]:
display_animation(ani, default_mode='reflect')
[ ]: