<p>In practice if you have a continuously twice differentiable regular curve <span class="math-container">$p(t) = (x(t), y(t), z(t))$</span> you can compute its velocity as <span class="math-container">$v(t) = \dot{p}(t) = \frac{dp}{dt}(t)$</span> and its acceleration as <span class="math-container">$a(t) = \dot{v}(t) = \ddot{p}(t)$</span> and use those to form an orthonormal basis (this wouod fail if <span class="math-container">$v(t)=0$</span> or <span class="math-container">$a(t)=0$</span>). The velocity <span class="math-container">$v$</span> is tangent to the curve, but <span class="math-container">$a$</span> is not necessarily orthogonal to it, so if we wish to construct an orthonormal basis we need to orthonormalize those, e.g., by using Gram-Schmidt:
<span class="math-container">\begin{equation}
f_1(t) = \frac{v(t)}{\|v(t)\|_2}, \quad a_{\perp v}(t) = a(t) - (f_1(t)\cdot a(t))f_1(t), \quad f_2(t) = \frac{a(t)}{\|a(t)\|_2}.
\end{equation}</span></p>
<p>We can compute the third vector by using the cross product <span class="math-container">$f_3(t) = f_1(t)\times f_2(t)$</span>. Now <span class="math-container">$f_1(t), f_2(t), f_3(t)$</span> form an orthonormal basis, and you can use the following for your camera matrix <span class="math-container">$C$</span> and view matrix <span class="math-container">$V$</span>:
<span class="math-container">\begin{align}
C &amp;= \begin{bmatrix}
| &amp; | &amp; | &amp; | \\
f_1(t) &amp; f_2(t) &amp; f_3(t) &amp; p(t) \\
| &amp; | &amp; | &amp; | \\
0 &amp; 0 &amp; 0 &amp; 1
\end{bmatrix} = \begin{bmatrix} F(t) &amp; p(t) \\ 0^T &amp; 1 \end{bmatrix}, \\
V &amp;= C^{-1} = \begin{bmatrix} (F(t))^T &amp; -(F(t))^Tp(t) \\ 0^T &amp; 1 \end{bmatrix}.
\end{align}</span></p>
<p>Looking at your code the specific curve you have is parwmetrized as follows:
<span class="math-container">\begin{align}
x(t) = \frac{a \cos(t)}{1+\sin^2(t)}, \quad
y(t) = \frac{a \cos(t)\sin(t)}{1+\sin^2(t)},\quad
z(t) = b \cos(c t).
\end{align}</span></p>
<p>The first derivatives (the components of the velocity) can be computed in the following way (I derived this without pen and paper so you should double check it):
<span class="math-container">\begin{align}
\dot{x}(t) &amp;= -\frac{a\sin(t)}{1+\sin^2(t)}-\frac{2a\cos^2(t)\sin(t)}{(1+\sin^2(t))^2}, \\
\dot{y}(t) &amp;= \frac{a \cos(2t)}{1+\sin^2(t)} - \frac{a\sin^2(2t)}{2(1+\sin^2(t))^2}, \\
\dot{z}(t) &amp;= -bc\sin(ct).
\end{align}</span></p>
<p>The second derivatives (components of the acceleration) are too tedious to compute so I suggest using wolfram alpha or some symbolic solver to do that for you.</p>

CC BY-SA 4.0
2024-08-03T10:13:03.650Z
2024-08-03T11:33:16.493Z
2024-08-03T11:33:16.493Z
lightxbulb
<p>I am trying with no avail to create a camera path that follows a lemniscate shape as shown in the figure. <a href="https://i.sstatic.net/OlWjDEV1.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/OlWjDEV1.png" alt="enter image description here" /></a></p>
<p>I do it in 3D but just set the vertical dimension (y) to a fixed number so really is 2D. the x and z coordinates are generated with a closed form function. I calculated the derivative of the function and that should give me the normal at each point. With the normal and a world UP axis I estimate the lookat direction and build a lookat matrix. However, this is not working for me. The camera does not turn properly at the lemniscate curves. Is there any other way to do this?</p>
<p>I have the 3D points of the lemniscate and I know world coordinates are  X-Right, Y-Down and Z-Forward.</p>
<p>This is the code:</p>
<pre><code>    # Parameters
    a = 0.2  # Scale of the lemniscate

    # Create theta values
    theta1 = np.linspace(0.5*np.pi, 0.75*np.pi, 25)
    theta2 = np.linspace(0.75*np.pi, 1.25*np.pi, 80)
    theta3 = np.linspace(1.25*np.pi, 1.75*np.pi, 45)
    theta4 = np.linspace(1.75*np.pi, 2.25*np.pi, 80)
    theta5 = np.linspace(2.25*np.pi, 2.5*np.pi, 25)
    theta = np.concatenate((theta1, theta2, theta3, theta4, theta5))

    # Convert to Cartesian coordinates
    x = a * np.cos(theta) / (np.sin(theta)**2 + 1)
    z = a * np.cos(theta) * np.sin(theta) / (np.sin(theta)**2 + 1)
    y = a * 0.2 * np.cos(4*theta)
    Cs = np.stack((x, y, z)).T

    dx_dtheta = -a*(np.sin(theta) * (np.sin(theta) ** 2 + 2*np.cos(theta)**2 + 1)) / (np.sin(theta)**2 + 1)**2
    dz_dtheta = -a*(np.sin(theta)**4 + np.sin(theta)**2 + (np.sin(theta)**2-1)*np.cos(theta)**2) / (np.sin(theta)**2 + 1)**2
    dy_dtheta = - 4 * a * 0.2 * np.sin(4*theta)  # If I use this, it gets worse

    normal = np.stack((dx_dtheta, np.zeros_like(x), dz_dtheta)).T
    normal = normal / np.linalg.norm(normal, axis=1, keepdims=True)

    UP = np.array([0, -1, 0])[None, :]
    lookat = np.cross(normal, UP)
    lookat = lookat / np.linalg.norm(lookat, axis=1, keepdims=True)
    right = normal
    up = np.cross(right, lookat)
    up /= np.linalg.norm(up, axis=1, keepdims=True)

    Rs = np.concatenate((right, up, lookat), axis=1).reshape(-1, 3, 3).transpose(0, 2, 1)
</code></pre>

CC BY-SA 4.0
2024-08-03T01:44:02.833Z
Can you clarify the following question for me? (1) why is `y` related to `theta`, when you say `but just set the vertical dimension (y) to a fixed number`? (2) It seems that you are using [Lemniscate of Bernoulli](https://en.wikipedia.org/wiki/Lemniscate_of_Bernoulli), and you present the curve in its parametric equation form. So, lets say if it is 2D. Taking the derivative of `theta` will end up in tangent vector on the x-z plane. If I understand it correctly, this should be the look-at vector (after normalization).
Enigmatisms
CC BY-SA 4.0
2024-08-03T07:50:33.343Z
Sorry, You are right I set `y` related to `theta`. What I want is the lookat vector to be corresponding to the one in 2D, i.e. tangent vector on the x-z plane. Regardless if `y` is zero or not. That why in my normal I set `y` to zero `normal = np.stack((dx_dtheta, np.zeros_like(x), dz_dtheta)).T`.
ElPotac
CC BY-SA 4.0
2024-08-03T07:54:35.260Z
Do not use lookat, use the Frenet frame's vectors as the basis vectors for the camera.
lightxbulb
CC BY-SA 4.0
2024-08-03T08:11:55.333Z
@Enigmatisms OMG you are right. Taking the derivative of theta will end up in tangent vector on the x-z plane. I was assuming that was the normal of a plane tangent to the line and so it pointed orthogonal to the lookat direction. Thanks for this. So the tangent line could also be interpreted as the normal of a plane orthogonal to the line?
ElPotac
CC BY-SA 4.0
2024-08-03T11:22:51.627Z
Take a look at this figure: [figure](https://postimg.cc/68H0b1DR)
Enigmatisms
CC BY-SA 4.0
2024-08-03T14:25:18.880Z
Doesnt that plot contradict what you said? Taking the derivative of theta will end up in tangent vector on the x-z plane. In that plot the derivative is orthogonal to the tangent
ElPotac
<3d><camera-matrix><coordinates>
Generate camera trajectory with cameras' lookat following shape
ElPotac
# Generate camera trajectory with cameras' lookat following shape

(Asked by: username_0 on 2024-08-02)

I am trying with no avail to create a camera path that follows a lemniscate shape as shown in the figure. [![enter image description here](https://i.sstatic.net/OlWjDEV1.png)](https://i.sstatic.net/OlWjDEV1.png)

I do it in 3D but just set the vertical dimension (y) to a fixed number so really is 2D. the x and z coordinates are generated with a closed form function. I calculated the derivative of the function and that should give me the normal at each point. With the normal and a world UP axis I estimate the lookat direction and build a lookat matrix. However, this is not working for me. The camera does not turn properly at the lemniscate curves. Is there any other way to do this?

I have the 3D points of the lemniscate and I know world coordinates are X-Right, Y-Down and Z-Forward.

This is the code:

```
    # Parameters
    a = 0.2  # Scale of the lemniscate

    # Create theta values
    theta1 = np.linspace(0.5*np.pi, 0.75*np.pi, 25)
    theta2 = np.linspace(0.75*np.pi, 1.25*np.pi, 80)
    theta3 = np.linspace(1.25*np.pi, 1.75*np.pi, 45)
    theta4 = np.linspace(1.75*np.pi, 2.25*np.pi, 80)
    theta5 = np.linspace(2.25*np.pi, 2.5*np.pi, 25)
    theta = np.concatenate((theta1, theta2, theta3, theta4, theta5))

    # Convert to Cartesian coordinates
    x = a * np.cos(theta) / (np.sin(theta)**2 + 1)
    z = a * np.cos(theta) * np.sin(theta) / (np.sin(theta)**2 + 1)
    y = a * 0.2 * np.cos(4*theta)
    Cs = np.stack((x, y, z)).T

    dx_dtheta = -a*(np.sin(theta) * (np.sin(theta) ** 2 + 2*np.cos(theta)**2 + 1)) / (np.sin(theta)**2 + 1)**2
    dz_dtheta = -a*(np.sin(theta)**4 + np.sin(theta)**2 + (np.sin(theta)**2-1)*np.cos(theta)**2) / (np.sin(theta)**2 + 1)**2
    dy_dtheta = - 4 * a * 0.2 * np.sin(4*theta)  # If I use this, it gets worse

    normal = np.stack((dx_dtheta, np.zeros_like(x), dz_dtheta)).T
    normal = normal / np.linalg.norm(normal, axis=1, keepdims=True)

    UP = np.array([0, -1, 0])[None, :]
    lookat = np.cross(normal, UP)
    lookat = lookat / np.linalg.norm(lookat, axis=1, keepdims=True)
    right = normal
    up = np.cross(right, lookat)
    up /= np.linalg.norm(up, axis=1, keepdims=True)

    Rs = np.concatenate((right, up, lookat), axis=1).reshape(-1, 3, 3).transpose(0, 2, 1)

```

**Tags:** 3d, camera-matrix, coordinates

> **Comments:**
> **username_1**: Can you clarify the following question for me? (1) why is `y` related to `theta`, when you say `but just set the vertical dimension (y) to a fixed number`? (2) It seems that you are using [Lemniscate of Bernoulli](https://en.wikipedia.org/wiki/Lemniscate_of_Bernoulli), and you present the curve in its parametric equation form. So, lets say if it is 2D. Taking the derivative of `theta` will end up in tangent vector on the x-z plane. If I understand it correctly, this should be the look-at vector (after normalization).  
> **username_0**: Sorry, You are right I set `y` related to `theta`. What I want is the lookat vector to be corresponding to the one in 2D, i.e. tangent vector on the x-z plane. Regardless if `y` is zero or not. That why in my normal I set `y` to zero `normal = np.stack((dx_dtheta, np.zeros_like(x), dz_dtheta)).T`.  
> **username_2**: Do not use lookat, use the Frenet frame's vectors as the basis vectors for the camera.  
> **username_0**: @username_1 OMG you are right. Taking the derivative of theta will end up in tangent vector on the x-z plane. I was assuming that was the normal of a plane tangent to the line and so it pointed orthogonal to the lookat direction. Thanks for this. So the tangent line could also be interpreted as the normal of a plane orthogonal to the line?  
> **username_1**: Take a look at this figure: [figure](https://postimg.cc/68H0b1DR)  
> **username_0**: Doesnt that plot contradict what you said? Taking the derivative of theta will end up in tangent vector on the x-z plane. In that plot the derivative is orthogonal to the tangent  

---

## 1 answers

### Answer by username_2 on 2024-08-03. Score: 0

In practice if you have a continuously twice differentiable regular curve $p(t) = (x(t), y(t), z(t))$ you can compute its velocity as $v(t) = \dot{p}(t) = \frac{dp}{dt}(t)$ and its acceleration as $a(t) = \dot{v}(t) = \ddot{p}(t)$ and use those to form an orthonormal basis (this wouod fail if $v(t)=0$ or $a(t)=0$). The velocity $v$ is tangent to the curve, but $a$ is not necessarily orthogonal to it, so if we wish to construct an orthonormal basis we need to orthonormalize those, e.g., by using Gram-Schmidt:
\begin{equation}
f\_1(t) = \frac{v(t)}{\|v(t)\|\_2}, \quad a\_{\perp v}(t) = a(t) - (f\_1(t)\cdot a(t))f\_1(t), \quad f\_2(t) = \frac{a(t)}{\|a(t)\|\_2}.
\end{equation}

We can compute the third vector by using the cross product $f\_3(t) = f\_1(t)\times f\_2(t)$. Now $f\_1(t), f\_2(t), f\_3(t)$ form an orthonormal basis, and you can use the following for your camera matrix $C$ and view matrix $V$:
\begin{align}
C &= \begin{bmatrix}
| & | & | & | \\
f\_1(t) & f\_2(t) & f\_3(t) & p(t) \\
| & | & | & | \\
0 & 0 & 0 & 1
\end{bmatrix} = \begin{bmatrix} F(t) & p(t) \\ 0^T & 1 \end{bmatrix}, \\
V &= C^{-1} = \begin{bmatrix} (F(t))^T & -(F(t))^Tp(t) \\ 0^T & 1 \end{bmatrix}.
\end{align}

Looking at your code the specific curve you have is parwmetrized as follows:
\begin{align}
x(t) = \frac{a \cos(t)}{1+\sin^2(t)}, \quad
y(t) = \frac{a \cos(t)\sin(t)}{1+\sin^2(t)},\quad
z(t) = b \cos(c t).
\end{align}

The first derivatives (the components of the velocity) can be computed in the following way (I derived this without pen and paper so you should double check it):
\begin{align}
\dot{x}(t) &= -\frac{a\sin(t)}{1+\sin^2(t)}-\frac{2a\cos^2(t)\sin(t)}{(1+\sin^2(t))^2}, \\
\dot{y}(t) &= \frac{a \cos(2t)}{1+\sin^2(t)} - \frac{a\sin^2(2t)}{2(1+\sin^2(t))^2}, \\
\dot{z}(t) &= -bc\sin(ct).
\end{align}

The second derivatives (components of the acceleration) are too tedious to compute so I suggest using wolfram alpha or some symbolic solver to do that for you.


---

