This notebook is part of https://github.com/AudioSceneDescriptionFormat/splines, see also http://splines.readthedocs.io/.

# Properties of Catmull--Rom Splines

CR splines are very popular because they are very easy to use.
Only a sequence of control points has to be specified, the tangents are calculated automatically from the given points.
Therefore, CR splines can be regarded as a subset of cubic Hermite splines.

CR splines are $C^1$ continuous, which means that the incoming and outgoing tangent vectors are equal for each control point.


```python
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
```


```python
import splines
```


```python
def plot_spline(spline, dots_per_second=15, ax=None, **kwargs):
    if ax is None:
        ax = plt.gca()
    total_duration = spline.grid[-1] - spline.grid[0]
    times = spline.grid[0] + np.arange(int(total_duration * dots_per_second) + 1) / dots_per_second
    ax.plot(*spline.evaluate(spline.grid).T,
            linestyle=':', color='lightgrey',
            marker='x', markeredgecolor='black')
    ax.plot(*spline.evaluate(times).T, '.', **kwargs)
    ax.axis('equal');
```

Let's choose a few points for an example:


```python
points = [
    (0.2, -0.5),
    (0, 1.3),
    (1.3, 0.6),
    (2.5, 1.9),
    (3.8, -0.2),
    (2.1, 0.1),
]
```


```python
s = splines.CatmullRom(points, endconditions='closed')
```


```python
plot_spline(s)
```

## Tangent Vectors

The tangent vectors at a given point are calculated based on the line connecting the preceding point and the following point.
In fact, the tangent vector has the same orientation as that line but only half of its length.

In other (more mathematical) words:

\begin{equation}
\boldsymbol{\dot{x}}_i = \frac{\boldsymbol{x}_{i+1} - \boldsymbol{x}_{i-1}}{2}
\end{equation}

This is illustrated for two points in the following plot:


```python
plot_spline(s)
for idx, color in zip([2, 5], ['purple', 'orange']):
    plt.quiver(*s.evaluate(s.grid[idx]), *s.evaluate(s.grid[idx], 1),
               angles='xy', scale_units='xy', scale=1, color=color)
    points = np.row_stack([s.evaluate(s.grid[idx - 1]),
                           s.evaluate(s.grid[idx + 1])])
    plt.plot(*points.T, '--', color=color, linewidth=2)
```

Note that the above equation is only valid for the uniform case, i.e. if the time intervals of all segments are equal to 1 unit of time.

Uniform parametrization typically works very well if the (Euclidean) distances between consecutive points are all quite similar.
However, if the distances are very different, the shape of the spline often turns out to be unexpected.
Most notably, in extreme cases there might be even cusps or self-intersections within a spline segment.


```python
points2 = [
    (0, 0),
    (1.45, 1.5),
    (1.55, 1.5),
    (3, 0),
]
```


```python
s2 = splines.CatmullRom(points2, endconditions='closed')
```


```python
plot_spline(s2)
```

In order to manipulate the shape of the spline (without moving control points), non-uniform times can be chosen.

For example, let's choose a few custom times:


```python
times3 = 0, 0.8, 1, 3, 5
```


```python
s3 = splines.CatmullRom(points2, times3, endconditions='closed')
```


```python
plot_spline(s3)
```

In the non-uniform case, the equation for the tangent vector gets a bit more complicated:

\begin{equation}
\boldsymbol{\dot{x}}_i =
\frac{
(t_{i+1} - t_i)^2 (\boldsymbol{x}_i - \boldsymbol{x}_{i-1}) +
(t_i - t_{i-1})^2 (\boldsymbol{x}_{i+1} - \boldsymbol{x}_i)
}{
(t_{i+1} - t_i)(t_i - t_{i-1})(t_{i+1} - t_{i-1})
}
\end{equation}

The derivation of this equation is shown in [a separate notebook](catmull-rom-non-uniform.ipynb).

Some sources use a simpler equation which is (arguably) not correct (except in the uniform case):

\begin{equation}
\boldsymbol{\dot{x}}_i = \frac{1}{2} \left(
\frac{\boldsymbol{x}_i - \boldsymbol{x}_{i-1}}{t_i - t_{i-1}} +
\frac{\boldsymbol{x}_{i + 1} - \boldsymbol{x}_i}{t_{i + 1} - t_i}
\right)
\end{equation}

See the [notebook about "finite difference" splines](finite-difference.ipynb).

There are even sources which show yet a simpler (but even less correct, except in the uniform case) equation:

\begin{equation}
\boldsymbol{\dot{x}}_i = \frac{\boldsymbol{x}_{i + 1} - \boldsymbol{x}_{i - 1}}{t_{i + 1} - t_{i - 1}}
\end{equation}

TODO: separate notebook with plots that illustrate the differences between those equations.

## Chordal Parametrization

TODO: explain "chordal"


```python
distances = np.linalg.norm(np.diff(points2 + points2[:1], axis=0), axis=1)
distances
```


```python
times4 = np.concatenate([[0], np.cumsum(distances)])
times4
```


```python
s4 = splines.CatmullRom(points2, times4, endconditions='closed')
```


```python
plot_spline(s4)
```

## Centripetal Parametrization

TODO: explain "centripetal"


```python
times5 = np.concatenate([[0], np.cumsum(np.sqrt(distances))])
times5
```


```python
s5 = splines.CatmullRom(points2, times5, endconditions='closed')
```


```python
plot_spline(s5)
```

TODO: `alpha` can be specified, see [API docs](python-module.rst#splines.CatmullRom).


```python
plot_spline(splines.CatmullRom(points2, endconditions='closed', alpha=0), label='uniform')
plot_spline(splines.CatmullRom(points2, endconditions='closed', alpha=0.5), label='centripetal')
plot_spline(splines.CatmullRom(points2, endconditions='closed', alpha=1), label='chordal')
plt.legend(loc='center');
```

0dd9d7ac54011d1cd848503535855785f842624d
ipynb
Jupyter Notebook
doc/catmull-rom-properties.ipynb
mgeier/splines
f54b09479d98bf13f00a183fd9d664b5783e3864
MIT
doc/catmull-rom-properties.ipynb
mgeier/splines
f54b09479d98bf13f00a183fd9d664b5783e3864
MIT
doc/catmull-rom-properties.ipynb
mgeier/splines
f54b09479d98bf13f00a183fd9d664b5783e3864
MIT
Qwen/Qwen-72B
1. YES
2. YES