srctools.math
This module implements three classes for representing vectors, Euler angles and rotation matrices, following Valve conventions. Vec represents an XYZ position and Angle represents a pitch-yaw-roll angle (in degrees). Matrix represents a rotation also, but in a format that can be manipulated properly. Angle will automatically compute a matrix when required, but it is more efficient to perform repeated operations on a matrix.
These classes will be replaced by Cython-optimized versions where possible, which are interchangable if pickled.
Rotations are performed via the matrix-multiplication operator @, where the left is rotated by the right. Vectors can be rotated by matrices and angles and matrices can be rotated by angles, but not vice-versa.
- Vec @ Angle -> Vec
- Vec @ Matrix -> Vec
- 3-tuple @ Angle -> Vec
- Angle @ Angle -> Angle
- Angle @ Matrix -> Angle
- Matrix @ Matrix -> Matrix
Implicit Tolerances
Repeated calculations, especially calculations involving rotation will inevitably acculumulate error, making exact comparison unreliable. However, it is quite useful to compare to values like (0, 0, 1)
, so to allow this comparison operations will treat a difference of less than 10-6 as equal. This precision was chosen since it is the number of decimal points permitted in SMD files. If exact comparisons are required, direct attribute comparisons can be used. To allow use as dictionary keys, Vec.as_tuple()
and Angle.as_tuple()
round to the same precision also.
-
srctools.math.
parse_vec_str
() Tuple[T1 | float, T2 | float, T3 | float] Convert a string in the form
(4 6 -4)
into a set of floats.If the string is unparsable or an invalid type, this uses the defaults
x
,y
,z
. The string can be surrounded by any of the()
,{}
,[]
,<>
bracket types, which are simply ignored.If the ‘string’ is already a
VecBase
orAngleBase
, this will be passed through. If you do want a specific class, useVecBase.from_str()
,VecBase.from_str()
orMatrixBase.from_angstr()
.
-
srctools.math.
lerp
() float Linearly interpolate from in to out.
Raises: ZeroDivisionError – If both in
values are the same.
-
srctools.math.
to_matrix
(value: Angle | FrozenAngle | Matrix | FrozenMatrix | VecBase | Vec_tuple | Tuple[float, float, float] | None,) Matrix | FrozenMatrix Convert various values to a rotation matrix.
-
srctools.math.
quickhull
(vertexes: Iterable[Vec]) List[Tuple[Vec, Vec, Vec]] Use the quickhull algorithm to construct a convex hull around the provided points.
This is only available when the C extension is compiled.
-
class
srctools.math.
VecBase
(*args: Any, **kwargs: Any) Internal Base class for 3D vectors, implementing common code.
-
INV_AXIS
= {'x': ('y', 'z'), 'y': ('x', 'z'), 'z': ('x', 'y'), ('y', 'z'): 'x', ('x', 'z'): 'y', ('x', 'y'): 'z', ('z', 'y'): 'x', ('z', 'x'): 'y', ('y', 'x'): 'z'} This is a dictionary containing complementary axes.
INV_AXIS["x", "y"]
gives"z"
, andINV_AXIS["y"]
returns("x", "z")
.
-
classmethod
from_str
() VecT Convert a string in the form
(4 6 -4)
into a Vector.If the string is unparsable, this uses the defaults
(x,y,z)
. The string can be surrounded by any of the()
,{}
,[]
,<>
bracket types, which are simply ignored.If the value is already a vector, a copy will be returned. To only do parsing, use
parse_vec_str()
.
-
classmethod
with_axes
(*args: str | int | float | SupportsFloat | _SupportsIndex | VecBase,) VecT
**kwargs: str | int | float | SupportsFloat | _SupportsIndex | VecBase, Create a Vector, given a number of axes and corresponding values.
This is a convenience for doing the following:
vec = Vec() vec[axis1] = val1 vec[axis2] = val2 vec[axis3] = val3
The magnitudes can also be Vectors, in which case the matching axis will be used from the vector.
-
classmethod
bbox
(*points: Iterable[VecBase] | VecBase) Tuple[VecT, VecT] Compute the bounding box for a set of points.
Pass either several Vecs, or an iterable of Vecs. Returns a
(min, max)
tuple
.
-
classmethod
iter_grid
() Iterator[VecT] Loop over points in a bounding box. All coordinates should be integers.
Both borders will be included.
-
iter_line
(end: VecBase, stride: int = 1) Iterator[VecT] Yield points in a line (including both endpoints).
Parameters: - stride – This specifies the distance between each point.
- end – The other end of the line.
If the distance is less than the stride, only end-points will be yielded. If they are the same, that point will be yielded.
-
axis
() Literal['x', 'y', 'z'] For an axis-aligned vector, return the axis it is on.
Raises: ValueError – If the vector is not on-axis.
-
to_angle
(roll: float = 0) Angle Convert a normal to a Source Engine angle.
The angle will point its
+x
axis in the direction of this vector. The inverse of this isVec(x=1) @ Angle(pitch, yaw, roll)
.Parameters: roll – The roll is not affected by the direction of the vector, so it can be provided separately.
-
__add__
(other: Vec | tuple | float) +
operation.This additionally works on scalars (adds to all axes).
-
__radd__
(other: VecBase | tuple | float) +
operation with reversed operands.This additionally works on scalars (adds to all axes).
-
__sub__
(other: Vec | tuple | float) -
operation.This additionally works on scalars (adds to all axes).
-
__rsub__
(other: VecBase | tuple | float) -
operation with reversed operands.This additionally works on scalars (adds to all axes).
-
__divmod__
(other: float) Tuple[VecT, VecT] Divide the vector by a scalar, returning the result and remainder.
-
__rdivmod__
(other: float) Tuple[VecT, VecT] Divide a scalar by a vector, returning the result and remainder.
-
__matmul__
(other: AngleBase | MatrixBase) VecT Rotate this vector by an angle or matrix.
-
__eq__
(other: object) bool Equality test.
Two Vectors are compared based on the axes. A Vector can be compared with a 3-tuple as if it was a Vector also. A tolerance of
1e-6
is accounted for automatically.
-
__ne__
(other: object) bool Inequality test.
Two Vectors are compared based on the axes. A Vector can be compared with a 3-tuple as if it was a Vector also. A tolerance of 1e-6 is accounted for automatically.
-
__lt__
() bool A<B
test.Two Vectors are compared based on the axes. A Vector can be compared with a 3-tuple as if it was a Vector also. A tolerance of 1e-6 is accounted for automatically.
-
__le__
() bool A<=B
test.Two Vectors are compared based on the axes. A Vector can be compared with a 3-tuple as if it was a Vector also. A tolerance of 1e-6 is accounted for automatically.
-
__gt__
() bool A>B
test.Two Vectors are compared based on the axes. A Vector can be compared with a 3-tuple as if it was a Vector also. A tolerance of 1e-6 is accounted for automatically.
-
__ge__
() bool A>=B
test.Two Vectors are compared based on the axes. A Vector can be compared with a 3-tuple as if it was a Vector also. A tolerance of 1e-6 is accounted for automatically.
-
classmethod
lerp
() VecT Linerarly interpolate between two vectors.
Raises: ZeroDivisionError – If in_min
andin_max
are the same.
-
join
(delim: str = ', ') str Return a string with all numbers joined by the passed delimiter.
This strips off the
.0
if no decimal portion exists.
-
__str__
() str Return the values, separated by spaces.
This is the main format in Valve’s file formats. This strips off the .0 if no decimal portion exists.
-
__format__
(format_spec: str) str Control how the text is formatted.
This returns each axis formatted with the provided specification, joined by spaces.
-
__getitem__
(ind: str | int) float Allow reading values by index instead of name if desired.
This accepts either
0
,1
,2
orx
,y
,z
to read values. Useful in conjunction with a loop to apply commands to all values.
-
in_bbox
(a: VecBase | Vec_tuple | Tuple[float, float, float],) bool
b: VecBase | Vec_tuple | Tuple[float, float, float], Check if this point is inside the specified bounding box.
-
static
bbox_intersect
() bool Check if the
(min1, max1)
bounding box intersects the(min2, max2)
bounding box.
-
norm
() VecT Normalise the Vector.
This is done by transforming it to have a magnitude of 1 but the same direction. The vector is left unchanged if it is equal to
(0, 0, 0)
, instead of raising.
-
dot
() float Return the dot product of both Vectors.
Tip: using this in the form
Vec.dot(a, b)
may be more readable.
-
cross
() VecT Return the cross product of both Vectors.
If this is called as a method (
a.cross(b)
), the result will have the same type asa
. Otherwise, if called asVec.cross(a, b)
orFrozenVec.cross(a, b)
, the type of the class takes priority.
-
norm_mask
(normal: Vec | FrozenVec) VecT Subtract the components of this vector not in the direction of the normal.
If the normal is axis-aligned, this will zero out the other axes. If not axis-aligned, it will do the equivalent.
-
__hash__
= None
-
-
class
srctools.math.
FrozenVec
() Immutable vector class. This cannot be changed once created, but is hashable.
-
static
__new__
() FrozenVec Create a
FrozenVec
.All values are converted to
float
s automatically. If no value is given, that axis will be set to0
. An iterable can be passed in (as thex
argument), which will be used forx
,y
, andz
.
-
classmethod
with_axes
(axis1: str,) FrozenVec
val1: int | float | SupportsFloat | _SupportsIndex | VecBase,
axis2: str | None = None,
val2: int | float | SupportsFloat | _SupportsIndex | VecBase = 0.0,
axis3: str | None = None,
val3: int | float | SupportsFloat | _SupportsIndex | VecBase = 0.0, Create a Vector, given a number of axes and corresponding values.
This is a convenience for doing the following:
vec = Vec() vec[axis1] = val1 vec[axis2] = val2 vec[axis3] = val3
The magnitudes can also be Vectors, in which case the matching axis will be used from the vector.
-
__reduce__
() Tuple[Callable[[float, float, float], FrozenVec], Tuple[float, float, float]] Pickling support.
This redirects to a global function, so C/Python versions interoperate.
-
static
-
class
srctools.math.
Vec
() A 3D Vector. This has most standard Vector functions.
>>> Vec(1, 2, z=3) # Positional or vec, defaults to 0. Vec(1, 2, 3) >> Vec(range(3)) # Any 1,2 or 3 long iterable Vec(0, 1, 2) >>> Vec(1, 2, 3) * 2 Vec(2, 4, 6) >>> Vec.from_str('<4 2 -45>') # Parse strings. Vec(4, 2, -45)
Operators and comparisons will treat 3-tuples interchangably with vectors, which is more convenient when specifying constant values. >>> Vec(3, 8, 7) - (0, 3, 4) Vec(3, 5, 3)
Addition/subtraction can be performed between either vectors or scalar values (applying equally to all axes). Multiplication/division must be performed between a vector and scalar to scale - use Vec.dot() or Vec.cross() for those operations.
Values can be modified by either setting/getting x, y and z attributes. In addition, the following indexes are allowed (case-insensitive): * 0 1 2 * “x”, “y”, “z”
-
__init__
() None Create a Vector.
All values are converted to
float
s automatically. If no value is given, that axis will be set to0
. An iterable can be passed in (as thex
argument), which will be used forx
,y
, andz
.
-
classmethod
with_axes
(axis1: str,) Vec
val1: int | float | SupportsFloat | _SupportsIndex | VecBase,
axis2: str | None = None,
val2: int | float | SupportsFloat | _SupportsIndex | VecBase = 0.0,
axis3: str | None = None,
val3: int | float | SupportsFloat | _SupportsIndex | VecBase = 0.0, Create a Vector, given a number of axes and corresponding values.
This is a convenience for doing the following:
vec = Vec() vec[axis1] = val1 vec[axis2] = val2 vec[axis3] = val3
The magnitudes can also be Vectors, in which case the matching axis will be used from the vector.
-
cross
() Vec Return the cross product of both Vectors.
If this is called as a method (
a.cross(b)
), the result will have the same type asa
. Otherwise, if called asVec.cross(a, b)
orFrozenVec.cross(a, b)
, the type of the class takes priority.
-
__iadd__
(other: VecBase | tuple | float) +=
operation.Like the normal one except without duplication.
-
__isub__
(other: VecBase | tuple | float) -=
operation.Like the normal one except without duplication.
-
__imatmul__
(other: AngleBase | MatrixBase) Vec We need to define this, so it’s in-place.
-
__reduce__
() Tuple[Callable[[float, float, float], Vec], Tuple[float, float, float]] Pickling support.
This redirects to a global function, so C/Python versions interoperate.
-
__setitem__
() None Allow editing values by index instead of name if desired.
This accepts either
0
,1
,2
orx
,y
,z
to edit values. Useful in conjunction with a loop to apply commands to all values.
-
rotate
() Vec Old method to rotate a vector by a Source rotational angle.
Deprecated: do Vec(...) @ Angle(...)
instead.If round is True, all values will be rounded to 6 decimals (since these calculations always have small inprecision.)
-
rotate_by_str
() Vec Rotate a vector, using a string instead of a vector.
Deprecated: use Vec(…) @ Angle.from_str(…) instead.
-
to_angle_roll
() Angle Produce a Source Engine angle with roll.
Deprecated: Use
MatrixBase.from_basis()
and thenMatrixBase.to_angle()
.from_basis()
can take any two direction pairs.Parameters: - z_norm – This must be at right angles to this vector. The resulting angle’s
+z
axis will point in this direction. - stride – is no longer used, it defined the roll angles to try.
- z_norm – This must be at right angles to this vector. The resulting angle’s
-
rotation_around
(rot: float = 90) Angle For an axis-aligned normal, return the angles which rotate around it.
Deprecated: Use MatrixBase.axis_angle()
and thenMatrixBase.to_angle()
.axis_angle()
works for any arbitary axis.
-
localise
(origin: VecBase | Vec_tuple | Tuple[float, float, float],) None
angles: Angle | FrozenAngle | Matrix | FrozenMatrix | None = None, Shift this point to be local to the given position and angles.
This effectively translates local-space offsets to a global location, given the parent’s origin and angles. This is an in-place version of
self @ angles + origin
.
-
-
class
srctools.math.
Vec_tuple
(**kwargs) An immutable tuple, useful for dictionary keys.
-
class
srctools.math.
AngleBase
(*args: Any, **kwargs: Any) Internal base class for Euler angles, implements common code.
-
classmethod
from_str
() AngleT Convert a string in the form
(4 6 -4)
into an Angle.If the string is unparsable, the provided default values are used instead. The string can be surrounded by any of the
()
,{}
,[]
,<>
bracket types, which are simply ignored.If the value is already an Angle, a copy will be returned. To only do parsing, use
parse_vec_str()
.
-
classmethod
from_basis
(**kwargs: Vec | FrozenVec) AngleT Return the rotation which results in the specified local axes.
At least two must be specified, with the third computed if necessary.
-
classmethod
with_axes
() AngleT Create an Angle, given a number of axes and corresponding values.
- This is a convenience for doing the following:
- ang = Angle() ang[axis1] = val1 ang[axis2] = val2 ang[axis3] = val3
The magnitudes can also be Angles, in which case the matching axis will be used from the angle.
-
join
(delim: str = ', ') str Return a string with all numbers joined by the passed delimiter.
This strips off the .0 if no decimal portion exists.
-
__str__
() str Return the values, separated by spaces.
This is the main format in Valve’s file formats, though identical to vectors. This strips off the .0 if no decimal portion exists.
-
__getitem__
(ind: str | int) float Allow reading values by index instead of name if desired.
This accepts the following indexes to read values: -
0
,1
,2
-"pitch"
,"yaw"
,"roll"
-"pit"
,"yaw"
,"rol"
-"p"
,"y"
,"r"
Useful in conjunction with a loop to apply commands to all values.
-
__eq__
(other: object) bool == test.
Two Angles are equal if all three axes are the same. An Angle can be compared with a 3-tuple as if it was an Angle also. A tolerance of 1e-6 is accounted for automatically.
-
__ne__
(other: object) bool != test.
Two Angles are equal if all three axes are the same. An Angle can be compared with a 3-tuple as if it was an Angle also. A tolerance of 1e-6 is accounted for automatically.
-
__matmul__
(other: AngleBase | MatrixBase) AngleT Angle @ Angle or Angle @ Matrix rotates the first by the second.
-
__hash__
= None
-
classmethod
-
class
srctools.math.
FrozenAngle
() Represents an immutable pitch-yaw-roll Euler angle.
-
static
__new__
(cls,) FrozenAngle
pitch: int | float | Iterable[int | float] = 0.0,
yaw: int | float = 0.0,
roll: int | float = 0.0, Create a FrozenAngle.
All values are converted to Floats automatically. If no value is given, that axis will be set to 0. An iterable can be passed in (as the pitch argument), which will be used for pitch, yaw, and roll. This includes Vectors and other Angles.
-
classmethod
with_axes
(axis1: str,) FrozenAngle
val1: float | AngleBase,
axis2: str | None = None,
val2: float | AngleBase = 0.0,
axis3: str | None = None,
val3: float | AngleBase = 0.0, Create an Angle, given a number of axes and corresponding values.
This is a convenience for doing the following:
ang = Angle() ang[axis1] = val1 ang[axis2] = val2 ang[axis3] = val3
The magnitudes can also be Angles, in which case the matching axis will be used from the angle.
-
__reduce__
() Tuple[Callable[[float, float, float], FrozenAngle], Tuple[float, float, float]] Pickling support.
This redirects to a global function, so C/Python versions interoperate.
-
copy
() FrozenAngle FrozenAngle is immutable.
-
__copy__
() FrozenAngle FrozenAngle is immutable.
-
__deepcopy__
(memodict: Any | None = None) FrozenAngle FrozenAngle is immutable.
-
static
-
class
srctools.math.
Angle
() Represents a pitch-yaw-roll Euler angle.
>>> Angle(45, 0, z=-90) # Positional or vec, defaults to 0. Angle(45, 0, 270) >> Vec(range(0, 270, 90)) # Any 1,2 or 3 long iterable Vec(0, 90, 180) >>> Vec(1, 2, 3) @ Angle(0, 0, 45) Vec(1, -0.707107, 3.53553) >>> Angle.from_str('(45 90 0)') # Parse strings. Angle(45, 90, 0)
Addition and subtraction can be performed between angles, while division/multiplication must be between an angle and scalar (to scale).
Like vectors, each axis can be accessed by getting/setting
pitch
/yaw
androll
attributes. In addition, the following indexes are allowed (case-insensitive):0
,1
,2
"p"
,"y"
,r
"pitch"
,"yaw"
,"roll"
"pit"
,"yaw"
,"rol"
All values are remapped to between
0-360
when set.-
__init__
() None Create an Angle.
All values are converted to :external:py:class`float`s automatically. If no value is given, that axis will be set to
0
. An iterable can be passed in (as thepitch
argument), which will be used forpitch
,yaw
, androll
. This includes Vectors and other Angles.
-
__reduce__
() Tuple[Callable[[float, float, float], Angle], Tuple[float, float, float]] Pickling support.
This redirects to a global function, so C/Python versions interoperate.
-
freeze
() FrozenAngle Return an immutable copy of this angle.
-
classmethod
with_axes
(axis1: str,) Angle
val1: float | AngleBase,
axis2: str | None = None,
val2: float | AngleBase = 0.0,
axis3: str | None = None,
val3: float | AngleBase = 0.0, Create an Angle, given a number of axes and corresponding values.
This is a convenience for doing the following:
ang = Angle() ang[axis1] = val1 ang[axis2] = val2 ang[axis3] = val3
The magnitudes can also be Angles, in which case the matching axis will be used from the angle.
-
__setitem__
() None Allow editing values by index instead of name if desired.
This accepts the following indexes to edit values: -
0
,1
,2
-"pitch"
,"yaw"
,"roll"
-"pit"
,"yaw"
,"rol"
-"p"
,"y"
,"r"
Useful in conjunction with a loop to apply commands to all values.
-
__imatmul__
(other: AngleBase | MatrixBase) Angle Angle @ Angle or Angle @ Matrix rotates the first by the second.
-
class
srctools.math.
MatrixBase
(matrix: MatrixBase | None = None) Common code for both matrix versions.
-
__init__
(matrix: MatrixBase | None = None) None MatrixBase cannot be instantiated.
-
classmethod
from_pitch
(pitch: float) MatrixT Return the matrix representing a pitch rotation (Y axis).
-
classmethod
from_roll
(roll: float) MatrixT Return the matrix representing a roll rotation (X axis).
-
classmethod
from_angle
() MatrixT Return the rotation representing an Euler angle.
Either an Angle can be passed, or the raw pitch/yaw/roll angles.
-
classmethod
from_angstr
() MatrixT Parse a string of the form “pitch yaw roll”, then convert to a Matrix.
This is equivalent to combining
MatrixBase.from_angle()
andAngleBase.from_str()
, except more efficient.
-
classmethod
axis_angle
() MatrixT Compute the rotation matrix forming a rotation around an axis by a specific angle.
-
__getitem__
(item: Tuple[int, int]) float Retrieve an individual matrix value by x, y position (0-2).
-
inverse
() MatrixT Return the inverse of this matrix.
Raises: ArithmeticError – If the matrix does not have an inverse.
-
classmethod
from_basis
(*,) MatrixT
x: Vec | FrozenVec | None = None,
y: Vec | FrozenVec | None = None,
z: Vec | FrozenVec | None = None, Construct a matrix from at least two basis vectors.
The third is computed, if not provided.
-
__hash__
= None
-
-
class
srctools.math.
FrozenMatrix
(matrix: MatrixBase | None = None) Represents an immutable rotation via a transformation matrix.
When performing multiple rotations, it is more efficient to create one of these instead of using an
Angle
directly. To construct a rotation, use one of the several classmethods available depending on what rotation is desired.-
static
__new__
(cls, matrix: MatrixBase | None = None) FrozenMatrix Create a new matrix.
If an existing matrix is supplied, it will be copied. Otherwise, an identity matrix is produced.
-
copy
() FrozenMatrix Frozen matrices are immutable.
-
__copy__
() FrozenMatrix Frozen matrices are immutable.
-
__deepcopy__
(memodict: object = Ellipsis) FrozenMatrix Frozen matrices are immutable.
-
__reduce__
() Tuple[Callable[[float, float, float, float, float, float, float, float, float], FrozenMatrix], Tuple[float, float, float, float, float, float, float, float, float]] Pickling support.
This redirects to a global function, so C/Python versions interoperate.
-
__weakref__
list of weak references to the object (if defined)
-
static
-
class
srctools.math.
Matrix
(matrix: MatrixBase | None = None) Represents a rotation via a transformation matrix.
When performing multiple rotations, it is more efficient to create one of these instead of using an
Angle
directly. To construct a rotation, use one of the several classmethods available depending on what rotation is desired.-
__init__
(matrix: MatrixBase | None = None) None Create a new matrix.
If an existing matrix is supplied, it will be copied. Otherwise, an identity matrix is produced.
-
freeze
() FrozenMatrix Return a frozen copy of this matrix.
-
__reduce__
() Tuple[Callable[[float, float, float, float, float, float, float, float, float], Matrix], Tuple[float, float, float, float, float, float, float, float, float]] Pickling support.
This redirects to a global function, so C/Python versions interoperate.
-
__weakref__
list of weak references to the object (if defined)
-