Vector API Reference#
The Vector Type#
- class pyevspace.Vector#
- class pyevspace.Vector(x: SupportsFloat, y: SupportsFloat, z: SupportsFloat)
- class pyevspace.Vector(container: Iterable)
The Vector type represents a vector of a 3-dimensional vector space over the real numbers. Vectors are mathematical objects that describe a length and direction. The three components of a 3D vector describe how many units the vector points in each direction of the x, y, and z axis of a reference frame. The distance from the origin to the (x, y, z) coordinate is the length of the vector. Vectors can represent positions in space, as well as other physical quantities such as velocities, accelerations, and forces. For example, a force vector’s length represents the quantity of the force (in units of force, like Newtons or pounds), and the direction of the vector represents the direction the force is applied.
The vector is the basic unit of PyEVSpace. The Vector type defines most arithmetic operators, as well as many methods useful for working with vectors, such as dot and cross product, or projecting one vector onto another. Vectors can also be rotated, within a reference frame or to or from another reference frame. The
Matrixis used for such cases. While matrices can directly be used to apply transformations to vectors, the library also provides several functions for handling these rotation for you.A vector can be initialized by specifying its components directly, or inside an iterable container that contains exactly those three elements. By not supplying any arguments the vector components will be initialized to zero. If initializing by provided components, all components must be specified.
Direct elements or elements of container must be a
float, or compatible withSupportsFloat, meaning they can be converted to a float by defining a__float__()method.Changed in version 0.16.0: Vector is now subclassable, and is no longer an immutable type, meaning class members can be directly modified or added.
- Parameters:
x – value to initialize the x-component to
y – value to initialize the y-component to
z – value to initialize the z-component to
container – an iterable object of length three with elements compatible with
SupportsFloat(this must be the only parameter if present)
- Raises:
TypeError – if a value of container cannot be converted to a
floatValueError – if
len(container) != 3
Other Constructors#
- classmethod Vector.__new__(cls: type) Self#
Creates a new, uninitialized cls object if cls is a subclass of
Vector. All components of the vector default to zero.Changed in version 0.16.0: type can be a subtype of
Vector, and an object of that type will be created and returned.
- Vector.__init__()#
- Vector.__init__(x: SupportsFloat, y: SupportsFloat, z: SupportsFloat)
- Vector.__init__(container: Iterable)
Initialize a
Vectordepending on the arguments. This method and arguments behave identically to theVectorconstructor.- Parameters:
x – value to initialize the x-component to
y – value to initialize the y-component to
z – value to initialize the z-component to
container – an iterable object of length three with elements compatible with
SupportsFloat(this must be the only parameter if present)
- Raises:
TypeError – if a value of container cannot be converted to a
floatValueError – if
len(container) != 3
Subclassing Vector#
As of version 0.16.0 the Vector class supports subclassing. As the
Vector class is implemented as a C struct, it has a specific memory
layout, and as a consequence of that it is a @distjoint-base. This
means Vector cannot be combined with any other types whose layout
is defined as a C struct when inheriting, which most Python built-in types
such as list or tuple are. For example combining Vector
and list as base classes will raise a TypeError:
>>> class Foo(Vector, list):
... pass
...
TypeError: multiple bases have instance lay-out conflict
This limitation does not apply when combining a @distjoin-base with a
pure python class, and of course is not an issue if Vector is the only
base class for a derived type.
While Vector supports inheritance, there is no way for the
constructor to know how to initialize an inherited type. Because of this,
any function (except Vector.__new__()) that returns a Vector
type will always return a Vector instance, even on inherited types.
To change this, you will need to override the function you want to return your
type, and wrap the returned value in the constructor of your type. For example:
>>> class MyType(Vector):
... def __init__(self, container, foo, bar):
... super().__init__(self, container)
... self.foo = foo
... self.bar = bar
... def __add__(self, other: MyType) -> MyType:
... super().__add__(other)
... return MyType(result, self.foo, self.bar)
In this example the result of the Vector.__add__() call is used to
create a MyType instance. Any method you want to have this behavior must
be implemented yourself, including module level functions that normally
return a Vector type.
Instance Methods#
General Protocols#
- Vector.__len__() int#
Returns the length of the
Vector. This always returns 3.- Returns:
the method always returns 3
- Vector.__getitem__(index: int) float#
Retrieves the indexed value of the underlying array.
- Parameters:
index – the index of the value to retrieve
- Returns:
the indexed value of self
- Raises:
TypeError – if index is not or cannot be converted to an
intValueError – if index is not in the interval [0, 2] (after adjusting for negative indexing)
- Vector.__setitem__(index: int, value: SupportsFloat) float#
Sets the indexed value of the underlying array.
- Parameters:
index – the index of the value to set
value – the value to set the array component to
- Returns:
value
- Raises:
TypeError – if index is not or cannot be converted to an
intTypeError – if value is not or cannot be converted to a
floatValueError – if index is not in the interval [0, 2]
- Vector.__repr__() str#
Returns a string representation of self, representative of a constructor call with the component values of the vector. The format of the components follows the same as
Vector.__str__().- Returns:
a string representation of self
Changed in version 0.16.0: The method now prepends the output with the module name for better scope resolution support when using the output with
exec().
- Vector.__str__() str#
Returns a string representation of self. The format of the output string is similar to a
list, the components enclosed by square brackets. The format of the components are either decimal or scientific notation, whichever requires fewer bytes to store their string representations.- Returns:
a string representation of self
- Vector.__reduce__()#
Allows support for pickling and deep copying. This function returns a 2-tuple containing a reference to the type constructor and a tuple of arguments to initialize an equivalent instance. For types inheriting from
Vector, this function may need to be overloaded and modified to return your own type.
Iterability#
While the Vector class does not define an __iter__() method
directly, the class does support iterability via the sequence protocol. Attempting
to access Vector.__iter__() will raise an AttributeError, however
a Vector can be used anywhere an iterable is expected.
>>> v = Vector(1, 2, 3)
>>> itr = iter(v)
>>> print(list(itr))
[1.0, 2.0, 3.0]
>>> for i in v:
... print(i)
...
1.0
2.0
3.0
Arithmetic Operators#
- Vector.__matmul__(matrix: Matrix) Vector#
Multiplication with the vector on the left side.
- Parameters:
matrix – the matrix to multiply self by
- Returns:
the transformation of self by matrix
- Raises:
TypeError – if matrix is not a Matrix type
- Vector.__neg__() Vector#
Negates each component of a vector. This results in a vector of the same length as self, but points in the opposite direction.
- Returns:
the negative of a vector
Logical Operators#
- Vector.__eq__(other: Vector) bool#
Compares each element of two vectors for equality.
See also
Checkout
Vector.compare_to_tol()andVector.compare_to_ulp()for finer control over evaluating equality.
Note
All other logic operators are not implemented and will raise a
TypeError.
Vector Operators#
- Vector.compare_to_tol(other: Vector, rel_tol: float = 1e-9, abs_tol: float = 1e-15) bool#
Compares self to other using tolerance based mechanics. This is the same way
Vector.__eq__()andVector.__ne__()compare vectors. This function allows customization of the realtive and absolute tolerance values. Using the default tolerance values, this function is equivalent toVector.__eq__(). For comparing any two components of self and other, the components are considered equal if, fordiff = abs(a - b),diff <= abs_tol + rel_tol * max((abs(a), abs(b))).- Parameters:
other – the vector to compare to self
rel_tol – the relative tolerance of the comparison
abs_tol – the absolute tolerance of the comparison
- Returns:
True if all components are equal, False otherwise
- Raises:
TypeError – if other is not a
Vectortype or rel_tol or abs_tol are not float types.
- Vector.compare_to_ulp(other: Vector, max_ulps: int) bool#
Compares self to other using ULP (Unit in the Last Place) based mechanics. The number of ULPs between two floating-point numbers is the number of representable floating-point value between the two numbers. This can be a valid way of comparing two floating-point numbers in certan domains, however for the types of projects PyEVSpace was built to support, it is not sufficient, which is why
Vector.__eq__()uses tolerance based comparisons.The most obvious example of why ULP based comparison does not suffice for our uses is comparing two values that would be very common to encounter. The values
cos(math.pi / 4)andsin(math.pi / 4)should be identical, however because of the binary representation of floating-point values, the number of ULPs between these values is quite large. In fact these values evaluate as False using the built-in Python equality operator:>>> print(cos(pi / 4)) 0.7071067811865476 >>> print(sin(pi / 4)) 0.7071067811865475 >>> # ^ notice the rounding differences >>> print(cos(pi / 4) == sin(pi / 4)) False
Using a default number of ULPs that accommodates this equality would also allow other, less equivalent values to evaluate as equal which should not be accepted.
This method still allows users who would like an ULP based comparison to be able to use it. In some cases, a very high level of vector equality may be waranted. If tolerance based comparison is still ideal for you but you want finer controls over the tolerance values, see
Vector.compare_to_tol().
- Vector.magnitude() float#
Computes the geometric length of the vector.
- Returns:
the magnitude of self
- Vector.magnitude_squared() float#
Computes the square of the magnitude of the vector. Because
Vector.magnitude()computes the square of a vector’s magnitude and returns it’s square root, callingVector.magnitude()and squaring the result will result in rounding errors. Calling this method directly preserves accuracy in such cases.Note
This is identical to
vector_dot(self, self), but is more explicit and preserves intent when reading code.- Returns:
the square of the magnitude of self
- Vector.norm() Vector#
Compute a new vector equal to the norm of self. This vector points in the same direction as self, but has a length of one, also called a unit vector.
- Returns:
self as a unit vector
- Vector.normalize() None#
Normalizes self by dividing each element by the magnitude of the vector. This results in a vector with length equal to one.
- Returns:
None
- Vector.mag()#
Removed in version 0.15.0: Use
Vector.magnitude()
- Vector.mag2()#
Removed in version 0.15.0: Use
Vector.magnitude_squared()
Attributes#
- Vector.E1#
Elementary vector that represents the x-axis of the standard basis. This value should not be modified.
- Value:
Vector(1, 0, 0)- Type:
- Vector.E2#
Elementary vector that represents the y-axis of the standard basis. This value should not be modified.
- Value:
Vector(0, 1, 0)- Type:
- Vector.E3#
Elementary vector that represents the z-axis of the standard basis. This value should not be modified.
- Value:
Vector(0, 0, 1)- Type:
Module Methods#
- pyevspace.vector_angle(lhs: Vector, rhs: Vector) float#
Computes the shortest angle between vectors lhs and rhs in radians. The order of operands does not matter, and
vector_angle(lhs, rhs) == vector_angle(rhs, lhs)is always True.- Parameters:
lhs – left-hand side of the operation
rhs – right-hand side of the operation
- Returns:
the shortest angle between lhs and rhs in radians
- Raises:
TypeError – if lhs or rhs is not a Vector type
- pyevspace.vector_cross(lhs: Vector, rhs: Vector) Vector#
Computes the cross product of lhs and rhs. PyEVSpace uses right-handed a coordinate system. If a left-handed cross product is needed, negate the result or swap the order of arguents.
>>> v1 = Vector(1, 2, 3) >>> v2 = Vector(4, 5, 6) >>> print(vector_cross(v1, v2)) # opposite direction for left-handed system [-3, 6, -3] >>> # either negate the answer >>> print(-vector_cross(v1, v2)) [3, -6, 3] >>> # or swap the order of arguments >>> print(vector_cross(v2, v1)) [3, -6, 2]
- Parameters:
lhs – left-hand side of the operation
rhs – right-hand side of the operation
- Returns:
the right-handed cross product of lhs and rhs
- Raises:
TypeError – if lhs or rhs is not a Vector type
- pyevspace.vector_dot(lhs: Vector, rhs: Vector) float#
Computes the dot product between two vectors. The order of arguments is not significant as
vector_dot(v1, v2) == vector_dot(v2, v1).- Parameters:
lhs – first vector argument
rhs – second vector argument
- Returns:
the dot product of lhs and rhs
- Raises:
TypeError – if lhs or rhs is not a Vector type
- pyevspace.vector_exclude(vector: Vector, exclude: Vector) Vector#
Computes a vector from vector with all direction of exclude removed from it. This results in a linearly independent vector from exclude such that
vector_dot(result, exclude)is 0.0. In other words, the resulting vector and exclude are othogonal to one another.Another way of viewing this is, if exclude is viewed as a normal vector to a plane, the resulting vector is the projection of vector onto that plane. This resulting vector points in all directions of vector, except any portions in the direction of exclude, hence it lies in this plane.
- Parameters:
vector – base vector
exclude – vector whose direction will be excluded from vector
- Returns:
vector with any direction of exclude removed
- Raises:
TypeError – if vector or exclude is not a Vector type
- pyevspace.vector_proj(vector: Vector, onto: Vector) Vector#
Projects one vector onto another. This results in a vector pointing in the same direction as onto, but whose length is
vector.magnitude * cos(vector_angle(vector, onto)).- Parameters:
vector – vector to project
onto – vector being projected onto
- Returns:
vector projected onto onto
- Raises:
TypeError – if vector or onto is not a Vector type
- pyevspace.dot(lhs, rhs)#
Removed in version 0.15.0: Use
vector_dot()
- pyevspace.cross(lhs, rhs)#
Removed in version 0.15.0: Use
vector_cross()
- pyevspace.norm(vector)#
Removed in version 0.15.0: Use
Vector.norm()
- pyevspace.vang(lhs, rhs)#
Removed in version 0.15.0: Use
vector_angle()
- pyevspace.vxcl(vector, exclude)#
Removed in version 0.15.0: Use
vector_exclude()
- pyevspace.proj(vector, onto)#
Removed in version 0.15.0: Use
vector_proj()
Buffer Protocol#
- Vector.__buffer__(flags: int) memoryview#
Returns a memoryview object with self as the reference object.
- Parameters:
flags – a combined enumerated value from inspect.BufferFlags to control the exported
memoryviewobject- Returns:
the exported
memoryviewobject
The Vector class supports the buffer protocol and can be
used by other objects which also support the buffer interface. For
example it can be used to instantiate a memoryview object
>>> vector = Vector(1, 2, 3)
>>> view = memoryview(vector)
>>> view[2] = 3.14
>>> print(vector)
[1, 2, 3.14]
This can also be used for interfacing with buffer supporting libraries like NumPy.
>>> import numpy as np
>>> from pyevspace import Vector
>>>
>>> vector = Vector(1, 2, 3)
>>> print(vector)
[1, 2, 3]
>>> # give arr access to the underlying data buffer of vector
>>> arr = np.ndarray((3,), buffer=vector)
>>> print(arr)
[1, 2, 3]
>>> # changes to arr also modify vector as they share the same memory
>>> arr[0] = 10
>>> print(arr)
[10, 2, 3]
>>> print(vector)
[10, 2, 3]