# Math Types SimVX math types are `numpy.ndarray` subclasses -- they interoperate directly with NumPy operations while providing named accessors and convenience methods. No PyGLM dependency. ## Vec2 2D vector (`ndarray` subclass, shape `(2,)`, `float32`): ```python from simvx.core import Vec2 v = Vec2(3, 4) v.length() # 5.0 v.normalized() # Vec2(0.6, 0.8) v.dot(Vec2(1, 0)) # 3.0 # Arithmetic (numpy broadcasting) a + b, a - b, a * 2, a / 3 Vec2(1) # Vec2(1, 1) -- scalar broadcast # NumPy interop import numpy as np np.linalg.norm(v) # 5.0 -- works because Vec2 IS an ndarray ``` ## Vec3 3D vector (`ndarray` subclass, shape `(3,)`, `float32`): ```python from simvx.core import Vec3 v = Vec3(1, 2, 3) v.length() # 3.742 v.length_squared() # 14 v.normalized() # unit vector v.dot(other) # dot product v.cross(other) # cross product # Indexing v[0], v[1], v[2] # x, y, z v.x, v.y, v.z # named access Vec3(1) # Vec3(1, 1, 1) -- scalar broadcast ``` ## Quat Unit quaternion for 3D rotations. All angle arguments use **degrees**. ```python from simvx.core import Quat # Construction q = Quat() # identity q = Quat.from_euler(pitch=45, yaw=90) # degrees q = Quat.from_axis_angle((0, 1, 0), 90) # axis-angle (degrees) q = Quat.look_at((0, 0, -1)) # look direction # Operations q.inverse() # conjugate (inverse for unit quaternion) q.euler_angles() # Vec3(pitch, yaw, roll) in degrees q.slerp(other, 0.5) # spherical interpolation q.rotate((0, 1, 0), 30) # compose with additional rotation (degrees) q.to_mat4() # convert to 4x4 numpy matrix # Rotate a vector rotated = q * Vec3(1, 0, 0) ``` ## Rotation Convention All SimVX rotation APIs use **degrees**, not radians. This applies to: - `Quat.from_euler()`, `Quat.from_axis_angle()` - `Node3D.rotate(axis, degrees)` - `euler_angles()` return values ## Matrix Functions Pure NumPy matrix utilities in `simvx.core.math`: ```python from simvx.core import perspective, look_at, translate, rotate, scale, identity # Projection proj = perspective(fov_degrees=60, aspect=16/9, near=0.1, far=100.0) # View view = look_at(eye=(0, 5, 10), center=(0, 0, 0), up=(0, 1, 0)) # Model transforms model = translate((1, 2, 3)) @ rotate((0, 1, 0), degrees=90) @ scale((2, 2, 2)) ``` All matrices are NumPy arrays in row-major order. Vulkan shaders expect column-major, so matrices are transposed at the GPU boundary in the forward renderer. ### Edge Case Safety Matrix functions guard against degenerate inputs that would otherwise produce NaN or infinity: - **`look_at()`** -- When the forward direction is nearly parallel to the up vector (e.g. looking straight up/down), a fallback up vector is chosen automatically to avoid a zero cross product. - **`perspective()`** -- FOV is clamped to 1--179 degrees, aspect ratio floors at a small positive value, and near/far are validated to prevent division by zero. - **`orthographic()`** -- Degenerate bounds (left == right, top == bottom, near == far) are corrected to avoid division by zero. - **`Quat.slerp()`** -- Handles both nearly-identical quaternions (< 0.03 degrees apart) and nearly-opposite quaternions (~180 degrees apart) by falling back to normalised linear interpolation. ## Utility Functions ```python from simvx.core import normalize, length, dot, cross, mix, clamp, slerp normalize(v) # unit vector length(v) # magnitude dot(a, b) # dot product cross(a, b) # cross product (Vec3 only) mix(a, b, 0.5) # linear interpolation (slerp for Quat) clamp(x, 0.0, 1.0) # clamp scalar slerp(q1, q2, 0.5) # quaternion slerp ``` ## API Reference See {py:mod}`simvx.core.math.types` for the complete math API.