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
(val: Union[str, Vec, Angle],) Tuple[Union[T1, float], Union[T2, float], Union[T3, float]]
x: Union[T1, float] = 0.0,
y: Union[T2, float] = 0.0,
z: Union[T3, float] = 0.0, 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
Vec
orAngle
, this will be passed through. If you do want a specific class, useVec.from_str()
,Angle.from_str()
orMatrix.from_angstr()
.
-
srctools.math.
lerp
() float Linearly interpolate from in to out.
Raises: ZeroDivisionError – If both in
values are the same.
-
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.
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”
-
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")
.
-
__init__
() None Create a Vector.
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
x
argument), which will be used forx
,y
, andz
.
-
__reduce__
() tuple Pickling support.
This redirects to a global function, so C/Python versions interoperate.
-
classmethod
from_str
() Vec 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
(axis1: str, val1: Union[float, Vec]) Vec -
classmethod
with_axes
() Vec -
classmethod
with_axes
(axis1: str,) Vec
val1: Union[float, Vec],
axis2: str,
val2: Union[float, Vec],
axis3: str,
val3: Union[float, Vec], 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.
-
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.
-
static
bbox
(__point: Iterable[Vec]) Tuple[Vec, Vec] -
static
bbox
(*points: Vec) Tuple[Vec, Vec] 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[Vec] Loop over points in a bounding box. All coordinates should be integers.
Both borders will be included.
-
iter_line
(end: Vec, stride: int = 1) Iterator[Vec] 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
() str 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.
-
to_angle_roll
(z_norm: Vec, stride: int = 0) Angle Produce a Source Engine angle with roll.
Deprecated: Use
Matrix.from_basis()
and thenMatrix.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 Matrix.axis_angle()
and thenMatrix.to_angle()
.axis_angle()
works for any arbitary axis.
-
__add__
(other: Union[Vec, tuple, float]) +
operation.This additionally works on scalars (adds to all axes).
-
__radd__
(other: Union[Vec, tuple, float]) +
operation with reversed operands.This additionally works on scalars (adds to all axes).
-
__iadd__
(other: Union[Vec, tuple, float]) +=
operation.Like the normal one except without duplication.
-
__sub__
(other: Union[Vec, tuple, float]) -
operation.This additionally works on scalars (adds to all axes).
-
__rsub__
(other: Union[Vec, tuple, float]) -
operation with reversed operands.This additionally works on scalars (adds to all axes).
-
__isub__
(other: Union[Vec, tuple, float]) -=
operation.Like the normal one except without duplication.
-
__divmod__
(other: float) Tuple[Vec, Vec] Divide the vector by a scalar, returning the result and remainder.
-
__rdivmod__
(other: float) Tuple[Vec, Vec] Divide a scalar by a vector, returning the result and remainder.
-
__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
() Vec 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 formated with the provided specification, joined by spaces.
-
__getitem__
(ind: Union[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.
-
__setitem__
(ind: Union[str, int], val: float) 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.
-
in_bbox
(a: Union[Vec, Vec_tuple, Tuple[float, float, float]],) bool
b: Union[Vec, 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
() Vec 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
() Vec Return the cross product of both Vectors.
Tip: using this in the form
Vec.cross(a, b)
may be more readable.
-
localise
() 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
.
-
norm_mask
(normal: Vec) Vec 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.
-
transform
() Iterator[Matrix] Perform rotations on this Vector efficiently.
Used as a context manager, which returns a matrix. When the body is exited safely, the matrix is applied to the angle.
-
__hash__
= None
-
-
class
srctools.math.
Vec_tuple
(x: float, y: float, z: float) An immutable tuple, useful for dictionary keys.
-
class
srctools.math.
Angle
(pitch: Union[int, float, Iterable[Union[int, float]]] = 0.0,)
yaw: Union[int, float] = 0.0,
roll: Union[int, float] = 0.0, 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__
(pitch: Union[int, float, Iterable[Union[int, float]]] = 0.0,) None
yaw: Union[int, float] = 0.0,
roll: Union[int, float] = 0.0, Create an Angle.
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.
-
__reduce__
() tuple Pickling support.
This redirects to a global function, so C/Python versions interoperate.
-
classmethod
from_str
() Angle 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()
.
-
__hash__
= None
-
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.
-
classmethod
with_axes
(axis1: str, val1: Union[float, Angle]) Angle -
classmethod
with_axes
() Angle -
classmethod
with_axes
(axis1: str,) Angle
val1: Union[float, Angle],
axis2: str,
val2: Union[float, Angle],
axis3: str,
val3: Union[float, Angle], 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.
-
classmethod
from_basis
(*, x: Vec, y: Vec, z: Vec) Angle -
classmethod
from_basis
(*, x: Vec, y: Vec) Angle -
classmethod
from_basis
(*, y: Vec, z: Vec) Angle -
classmethod
from_basis
(*, x: Vec, z: Vec) Angle Return the rotation which results in the specified local axes.
At least two must be specified, with the third computed if necessary.
-
__getitem__
(ind: Union[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.
-
__setitem__
(ind: Union[str, int], val: float) 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.
-
__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 a 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 a Angle also. A tolerance of 1e-6 is accounted for automatically.
-
class
srctools.math.
Matrix
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.-
__reduce__
() tuple Pickling support.
This redirects to a global function, so C/Python versions interoperate.
-
classmethod
from_pitch
(pitch: float) Matrix Return the matrix representing a pitch rotation (Y axis).
-
classmethod
from_roll
(roll: float) Matrix Return the matrix representing a roll rotation (X axis).
-
classmethod
from_angle
(__angle: Angle) Matrix -
classmethod
from_angle
(pitch: float, yaw: float, roll: float) Matrix Return the rotation representing an Euler angle.
Either an Angle can be passed, or the raw pitch/yaw/roll angles.
-
classmethod
from_angstr
() Matrix Parse a string of the form “pitch yaw roll”, then convert to a Matrix.
This is equivalent to combining
Matrix.from_angle()
andAngle.from_str()
, except more efficient.
-
classmethod
axis_angle
() Matrix 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).
-
__setitem__
(item: Tuple[int, int], value: float) None Set an individual matrix value by x, y position (0-2).
-
__iter__
= None Iteration doesn’t make much sense.
-
classmethod
from_basis
(*, x: Vec, y: Vec, z: Vec) Matrix -
classmethod
from_basis
(*, x: Vec, y: Vec) Matrix -
classmethod
from_basis
(*, y: Vec, z: Vec) Matrix -
classmethod
from_basis
(*, x: Vec, z: Vec) Matrix Construct a matrix from at least two basis vectors.
The third is computed, if not provided.
-
__hash__
= None
-