2

I am writing a Program and I would like to calculate the $3\text{D}$ coordinates of $2\text{D}$ Points on a normal plane aligned to a vector, in order to rotate the $2\text{D}$ circle in $3\text{D}$ Space.

Therefore I would need to calculate a normal plane for a given vector and calculate the $3\text{D}$ coordinates of planar $2\text{D}$ Coordinates.

something like this

Since I have never done anything like this I don't know how to do either of these. Thanks in advance.

commie trivial
  • 984
  • 2
  • 4
  • 14

2 Answers2

1

The normal of the plane does not suffice, you need an additional vector perpendicular to the normal (i.e., along the plane) to indicate the orientation of the 2D coordinate system on that plane.

(The second basis vector for the 2D coordinate system is perpendicular to both, and can be obtained as a cross product between the two known ones, then scaling to the same length as the other 2D basis vector.)

A better approach is to use a set of orthogonal basis vectors for the plane, say $\vec{e}_u = (x_u, y_u, z_u)$ and $\vec{e}_v = (x_v, y_v, z_v)$, $\vec{e}_u \perp \vec{e}_v$ (i.e., $\vec{e}_u \cdot \vec{e}_v = 0$), plus a vector $\vec{o} = (x_o, y_o, z_o)$ specifying the origin of the 2D coordinate system. The length of the basis vectors specify the scaling; if they are unit vectors (length $1$), there is no scaling applied.

For given 2D coordinates $(u, v)$, the corresponding 3D point $\vec{p} = (x, y, z)$ is $$\vec{p} = \vec{o} + u \vec{e}_u + v \vec{e}_v \quad \iff \quad \left\lbrace ~ \begin{aligned} x &= x_o + u x_u + v x_v \\ y &= y_o + u y_u + v y_v \\ z &= z_o + u z_u + v z_v \\ \end{aligned} \right .$$


If you happen to have some vector $\vec{u} = (u_x, u_y, u_z)$ you want to use as one of the basis vectors, but it isn't exactly perpendicular to $\vec{n} = (n_x, n_y, n_z)$, you can orthogonalise $\vec{u}$ using one step of Gram–Schmidt process, which subtracts the part of $\vec{u}$ that is parallel to $\vec{n}$ from $\vec{u}$, and thus yields a vector that is perpendicular to $\vec{n}$: $$\vec{u}_\perp = \vec{u} - \frac{\vec{n} \cdot \vec{u}}{\vec{n} \cdot \vec{n}} \vec{n}$$ In Cartesian coordinate form, if we use $\vec{u}_\perp = (\chi, \gamma, \zeta)$, then $$\left\lbrace ~ \begin{aligned} d &= \frac{n_x u_x + n_y u_y + n_z u_z}{n_x^2 + n_y^2 + n_z^2} \\ \chi &= u_x - d n_x \\ \gamma &= u_y - d n_y \\ \zeta &= u_z - d n_z \\ \end{aligned} \right.$$ (The Gram–Schmidt process is doing that repeatedly for an already orthogonal set of vectors $\vec{n}_1, \vec{n}_2, \dots$, resulting in a vector that is perpendicular to all of them.)

Of course, if it happens that $\vec{u} \parallel \vec{n}$, then $\vec{u}_\perp = (0, 0, 0)$. In other words, we cannot just pick a fixed 3D vector as our additional vector, if there is any chance that it might be parallel to the plane normal vector. If it happens to be close enough, your computer program will exhibit some crazy visuals, and possibly crash due to division by zero...

This is used when you have a "right" or "up" vector specified by an user. However, you will want to warn that user when the result of the Gram–Schmidt orthogonalisation yields a vector so short it is basically a zero vector, because with floating point numbers, after a few operations sufficiently small numbers tend to cause division by zero, just like an actual zero.


For best results, start with $\vec{e}_u = (S, 0, 0)$ and $\vec{e}_v = (0, S, 0)$ (i.e., the same orientation as the 3D coordinate system), where $S$ is your scale factor ($S = 1$ if you want 2D and 3D lengths to correspond; otherwise, use $S = \text{2D length} / \text{3D length}$), and rotate them however you wish.

(You can also start with say $\vec{e}_u = (0, S, 0)$ and $\vec{e}_v = (0, 0, S)$, if you prefer your 2D plane is initially perpendicular to the $x$ axis, and so on.)

I warmly recommend you use either unit quaternions (versors) or bivectors to represent the rotation. (The actual elementary operations – multiplications and additions and so on – happen to be the exact same either way.) Unlike say Tait-Bryan and Euler angles, those are uniquely defined and do not suffer from gimbal lock.

Besides, it is very easy to implement trackball-like control – treat the user actions on the screen as if the user was rotating a trackball with their finger – using versors/bivectors:

Each on-screen drag corresponds to a specific rotation quaternion/bivector, which happens to be trivial to calculate.

If you consider the start and end points of dragging within a circular region (coordinates relative to the center of the region), the length corresponds to the rotation angle (typically full diameter of the ball corresponding to $180°$ of rotation), and the rotation axis is the drag vector rotated $90°$ clockwise.

Let $(x_0, y_0)$ be the drag start point and $(x_1, y_1)$ the drag end point, one or both within the circular region, and $\varphi = \pi / D$ ($\pi$ divided by the "ball" diameter in screen units) the rotation angle in radians per screen coordinate unit. Then, the rotation quaternion $\mathbf{q} = (r; i, j, k)$ is $$\left\lbrace ~ \begin{aligned} L &= \sqrt{ (x_1 - x_0)^2 + (y_1 - y_0)^2 } \\ \Delta_x &= \frac{x_1 - x_0}{L} \\ \Delta_y &= \frac{y_1 - y_0}{L} \\ \theta &= \frac{L \varphi}{2} \\ r &= \cos \theta \\ i &= \Delta_y \sin \theta \\ j &= -\Delta_x \sin \theta \\ k &= 0 \\ \end{aligned} \right . $$ noting that rotating $(i, j)$ $90°$ clockwise yields $(j, -i)$.

When both start and end points are outside the circular region, it is nice to rotate correspondingly in the screen plane, around the vector perpendicular to the screen. If $(x_0, y_0)$ and $(x_1, y_1)$ are again the start and end points of the dragging motion, relative to the center of the circular region, then $$\left\lbrace ~ \begin{aligned} \theta_0 &= \operatorname{atan2}(y_0, x_0) \\ \theta_1 &= \operatorname{atan2}(y_1, x_1) \\ \theta &= \frac{\theta_1 - \theta_0}{2} \mod \pi \\ r &= \cos\theta \\ i &= 0 \\ j &= 0 \\ k &= \sin\theta \\ \end{aligned} \right . $$ where $\operatorname{atan2}$ refers to the two-argument form of arcus tangent, covering full $360°$; and the $\mod \pi$ means adding or subtracting $\pi$ from the result until it is within $-\pi$ and $+\pi$.

When the dragging starts, you save the current orientation quaternion $\mathbf{q}$ as say $\mathbf{q}_0$. Then, during each screen update, you calculate a temporary new orientation $\mathbf{q}_T$ using the current drag end location as the endpoint, and calculate the effective orientation quaternion as $\mathbf{q} = \mathbf{q}_T \mathbf{q}_0$ (using Hamilton product shown below). When the dragging ends, you do that the final time with the drag endpoint.


Multiplying two quaternions (using Hamilton product), $\mathbf{q} = \mathbf{q}_2 \mathbf{q}_1$, and in practice normalizing the result to unit length (by dividing each component by the square root of the sum of squared original components, yields the quaternion that represents the rotation $\mathbf{q}_1$ (rightmost) followed by rotation represented by $\mathbf{q}_2$: $$\left\lbrace ~ \begin{aligned} r &= r_2 r_1 - i_2 i_1 - j_2 j_1 - k_2 k_1 \\ i &= r_2 i_1 + i_2 r_1 + j_2 k_1 - k_2 j_1 \\ j &= r_2 j_1 - i_2 k_1 + j_2 r_1 + k_2 i_1 \\ k &= r_2 k_1 + i_2 j_1 - j_2 i_1 + k_2 r_1 \\ \end{aligned} \right .$$

The key is to remember that when multiplying rotation quaternions, the first rotation is rightmost, and last rotation leftmost.

To invert a rotation, negate either $r$, or $i$, $j$, and $k$.

If you negate all four components of a rotation quaternion, you do not change the orientation it describes, but you do change which way around the great circle the rotation occurs. (It does not matter in this particular use case, but if you start interpolating between different rotation quaternions to smoothly turn your "camera" between specific orientations, if $r_1 r_2$ or $i_1 i_2 + j_1 j_2 + k_1 k_2$ is negative (but not both), you'll want to negate all components of one of the quaternions or the "camera" will turn the long way around during interpolation.)


To apply a rotation described by a quaternion, we convert it to a matrix $\mathbf{R}$: $$\mathbf{R} = \left[ \begin{matrix} R_{11} & R_{12} & R_{13} \\ R_{21} & R_{22} & R_{23} \\ R_{31} & R_{32} & R_{33} \\ \end{matrix} \right] = \left[ \begin{matrix} 1 - c (j^2 + k^2) & c (i j - k r) & c ( i k + j r ) \\ c ( i j + k r ) & 1 - c ( i^2 + k^2 ) & c ( j k - i r ) \\ c ( i k - j r ) & c ( j k + i r ) & 1 - c ( i^2 + j^2 ) \\ \end{matrix} \right ]$$ where $$c = \frac{1}{2 (q^2 + i^2 + j^2 + k^2 ) }$$ That $c$ handles unit normalization, too. If you want, you can normalize the quaternion at any point, by first calculating $L = \sqrt{q^2 + i^2 + j^2 + k^2}$, and then dividing each of the four components by $L$. For an unit quaternion, $c = 1/2 = 0.5$, above.

Then, to rotate $\vec{p} = (x, y, z)$ to get $\vec{P} = (X, Y, Z)$, we do $$\vec{P} = \mathbf{R} \vec{p} \quad \iff \quad \left\lbrace ~ \begin{aligned} X &= R_{11} x + R_{12} y + R_{13} z \\ Y &= R_{21} x + R_{22} y + R_{23} z \\ Z &= R_{31} x + R_{32} y + R_{33} z \\ \end{aligned} \right . $$

While it is possible to convert a matrix back to a quaternion, it is numerically sensitive: to implement it properly, you'll need to compare three elements in the rotation matrix, and depending on which one has the largest absolute value (magnitude), you use one of three formulae. It is much better to just keep the current orientation in a quaternion, and then apply additional rotations on top, by multiplying the additional rotation with the current orientation to get the new orientation (used as the current orientation during the next update).

Unlike rotation matrices, quaternions do not suffer from loss of orthogonality either. (Multiplying a hundred or so similar rotation matrices means any rounding errors are amplified, which leads to visual deformations. And you cannot easily re-orthonormalize rotation matrices without introducing a preferred direction or directions, which humans tend to easily notice.)

This may sound like "many" operations, but fact is, this approximately minimizes the number of multiplications needed. (You can get away with fewer if you restrict your rotations and so on.)

0

Let the unit normal vector to the plane that contains the $2D$ points be $\hat{n}$, and let $p_0$ be any point on that plane. The first thing to do is create a reference frame attached to the plane, with its origin at $p_0$ and its $z$ axis along the plane unit normal vector $\hat{n}$.


Next, create two unit vectors $\hat{u_1}, \hat{u_2}$ that are mutually perpendicular and are also perpendicular to the vector $\hat{n}$. One way to do this is by first expressing the vector $\hat{n}$ in spherical coordinates as follows. Find the angles $\theta$ and $\phi$ such that

$ \hat{n} = \begin{bmatrix} \sin(\theta) \cos(\phi) \\ \sin(\theta) \sin(\phi) \\ \cos(\theta) \end{bmatrix} $

Unique values of $\theta$ and $\phi$ are obtainable, except when $\hat{n} = [0, 0, 1]^T $ or $\hat{n} = [0, 0, -1]^T $, in either case we can take $\phi = 0 $.

Now the vectors $\hat{u_1}, \hat{u_2} $ are given by

$ \hat{u_1} = \begin{bmatrix} \cos(\theta) \cos(\phi) \\ \cos(\theta)\sin(\phi) \\ - \sin(\theta) \end{bmatrix} $

$ \hat{u_2} = \begin{bmatrix} - \sin(\phi) \\ \cos(\phi) \\ 0 \end{bmatrix}$


Next, define the $3 \times 3 $ matrix $R$ as follows

$ R = \begin{bmatrix} \hat{u_1} && \hat{u_2} && \hat{n} \end{bmatrix}$

Now, if you have a $2D$ point in the plane, for example $P(x_1, y_1) $, then

its $3D$ coordinates will be

$ P = p_0 + R [ x_1, y_1, 0]^T $

As another example, suppose you want to find the $3D$ coordinates of a circle of radius $a$ centered at $p_0$ (which is the origin of the created reference frame). Then parametrically,

$ [x_1, y_1] = a [ \cos(\psi), \sin(\psi) ] $

Then the parametric equation of this circle in $3D$ is

$ Q(\psi) = p_0 + a R [ \cos(\psi), \sin(\psi) , 0 ]^T $

which is precisely equal to

$ Q(\psi) = p_0 + a \cos(\psi) \hat{u_1} + a \sin(\psi) \hat{ u_2 } $

Hosam Hajeer
  • 21,978