Skip to content

Instantly share code, notes, and snippets.

@potat-dev
Last active July 3, 2026 16:16
Show Gist options
  • Select an option

  • Save potat-dev/874ca517191759659bec2664d22d4775 to your computer and use it in GitHub Desktop.

Select an option

Save potat-dev/874ca517191759659bec2664d22d4775 to your computer and use it in GitHub Desktop.
Simple library to calculate Quaternion rotations in Python. No external libraries required!
import math
from typing import Self
from dataclasses import dataclass
from math import cos, sin, sqrt
@dataclass(slots=True)
class Vector:
x: float
y: float
z: float
@property
def length(self) -> float:
return sqrt(self.x**2 + self.y**2 + self.z**2)
@property
def normal(self) -> Self:
if (L := self.length) == 0:
raise ValueError("Cannot normalize vector with lenght 0")
return Vector(self.x / L, self.y / L, self.z / L)
def __add__(self, other: Self) -> Self:
return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
def __mul__(self, scalar: float) -> Self:
return Vector(self.x * scalar, self.y * scalar, self.z * scalar)
def __rmul__(self, scalar: float) -> Self:
return self.__mul__(scalar)
def dot(self, other: Self) -> float:
return self.x * other.x + self.y * other.y + self.z * other.z
def cross(self, other: Self) -> Self:
return Vector(
self.y * other.z - self.z * other.y,
self.z * other.x - self.x * other.z,
self.x * other.y - self.y * other.x,
)
@dataclass(slots=True)
class Quaternion:
w: float
x: float
y: float
z: float
@property
def vector(self) -> Vector:
return Vector(self.x, self.y, self.z)
def __mul__(self, other: Self) -> Self:
v1, v2 = self.vector, other.vector
w = self.w * other.w - v1.dot(v2)
v = (self.w * v2) + (other.w * v1) + v1.cross(v2)
return Quaternion(w, v.x, v.y, v.z)
@property
def normal(self) -> Self:
if (L := self.length) == 0:
raise ValueError("Cannot normalize quaternion with lenght 0")
return Quaternion(self.w / L, self.x / L, self.y / L, self.z / L)
@classmethod
def from_vector_angle(cls, vec: Vector, angle: float) -> Self:
"""
Constructs a rotational quaternion from vector and angle.
Angle should be in degrees. Vector can be lenght != 1.
"""
vec_norm = vec.normal
half_angle = math.radians(angle) / 2.0
w, s = cos(half_angle), sin(half_angle)
return cls(w, vec_norm.x * s, vec_norm.y * s, vec_norm.z * s)
@classmethod
def from_vector(cls, vec: Vector) -> Self:
return cls(0, vec.x, vec.y, vec.z)
@property
def conjugate(self) -> Self:
return Quaternion(self.w, -self.x, -self.y, -self.z)
@property
def length(self) -> float:
return sqrt(self.w**2 + self.x**2 + self.y**2 + self.z**2)
def rotated_by(self, other: Self) -> Self:
return other * self * other.conjugate
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment