The 2D planar coordinate system conversion I use at work, the demo code is implemented in Rust, and other code should be implemented in roughly the same way. 🤔

If you are using the standard right-handed Cartesian coordinate system you may need to transform the y-axis appropriately.

Rotation

clockwise

\[A= \begin{gathered} \begin{bmatrix} cos(\theta) & sin(\theta) & 0 \\ sin(\theta) & cos(\theta) & 0 \\ 0 & 0 & 1 \end{bmatrix} \end{gathered}\]

counterclockwise

\[A= \begin{gathered} \begin{bmatrix} cos(\theta) & sin(-\theta) & 0 \\ sin(\theta) & cos(\theta) & 0 \\ 0 & 0 & 1 \end{bmatrix} \end{gathered}\]

matrix equation

\[\begin{gathered} \begin{bmatrix} x_1 \\ y_1 \\ 1 \end{bmatrix} \end{gathered} = A \cdot \begin{gathered} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \end{gathered}\]

Rust demo code

use std::f64::consts::PI;
use ndarray::arr2;

fn rotation() {
    // 2D point rotation 45 degrees
    let theta = 45.0 * (PI / 180.0);

    // Convert Ordinary Coordinate (x, y) to Homogeneous Coordinate (x, y, 1)
    let point = arr2(&[[0.0], [10.0], [1.0]]);

    // 2D rotated matrices
    let a = arr2(&[
        [f64::cos(theta), f64::sin(theta), 0.0],
        [f64::sin(theta), f64::cos(theta), 0.0],
        [0.0, 0.0, 1.0],
    ]);

    let new_point = a.dot(&point);

    println!("{}", &new_point);
}
    Finished dev [unoptimized + debuginfo] target(s) in 0.11s
     Running `target/debug/two_d_cover`
[[7.071067811865475],
 [7.0710678118654755],
 [1]]

Rotate a point around another point

  1. Translate the specified point to the origin to obtain the translation matrix $T_1$
  2. Perform a rotation to obtain the rotation matrix $R$
  3. Translate to the specified point to get the translation matrix $T_2$.

the point $(x_0, y_0)$ is rotated around the point $(x_s, y_s)$

\[T_1= \begin{gathered} \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ -x_s & -y_s & 1 \end{bmatrix} \end{gathered}\] \[R= \begin{gathered} \begin{bmatrix} cos(\theta) & -sin(\theta) & 0 \\ sin(\theta) & cos(\theta) & 0 \\ 0 & 0 & 1 \end{bmatrix} \end{gathered}\] \[T_2= \begin{gathered} \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ x_s & y_s & 1 \end{bmatrix} \end{gathered}\] \[\begin{gathered} \begin{bmatrix} x_1 \\ y_1 \\ 1 \end{bmatrix} \end{gathered} = \begin{gathered} \begin{bmatrix} x_0 \\ y_0 \\ 1 \end{bmatrix} \end{gathered} \cdot T_1 \cdot R \cdot T_2\]

Translation

\[\begin{gathered} \begin{bmatrix} x_1 \\ y_1 \\ 1 \end{bmatrix} \end{gathered} = \begin{gathered} \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ dx & dy & 1 \end{bmatrix} \end{gathered} \cdot \begin{gathered} \begin{bmatrix} x_0 \\ y_0 \\ 1 \end{bmatrix} \end{gathered}\]

Zoom

\[\begin{gathered} \begin{bmatrix} x_1 \\ y_1 \\ 1 \end{bmatrix} \end{gathered} = \begin{gathered} \begin{bmatrix} s_x & 0 & 0 \\ 0 & s_y & 0 \\ 0 & 0 & 1 \end{bmatrix} \end{gathered} \cdot \begin{gathered} \begin{bmatrix} x_0 \\ y_0 \\ 1 \end{bmatrix} \end{gathered}\]

Rust demo code

use ndarray::arr2;

fn main() {
    zoom(10.0, 10.0, 1.2, 1.2);
}

fn zoom(x_0: f64, y_0: f64, s_x: f64, s_y: f64) {
    let point = arr2(&[[x_0], [y_0], [1.0]]);

    let s = arr2(&[
        [s_x, 0.0, 0.0],
        [0.0, s_y, 0.0],
        [0.0, 0.0, 1.0],
    ]);

    let new_point = s.dot(&point);
    println!("{}", &new_point);
}
    Finished dev [unoptimized + debuginfo] target(s) in 0.34s
     Running `target/debug/two_d_cover`
[[12],
 [12],
 [1]]