Linear Algebra with Mastapy
This article is a brief overview of the linear algebra classes uniquely available to the Python API. These are handwritten classes specifically designed for ease-of-use in Python.
These linear algebra classes are not suitable for intensive operations. If you require high performance it is highly recommended you use the numpy
package. These classes can be easily converted to a numpy array (e.g. numpy.array(my_vector)
.)
The following classes are currently available:
Scalar
Vector2D
Vector3D
Vector4D
Matrix2x2
Matrix3x3
Matrix4x4
With the exception of Scalar
, all classes can be instantiated and boast features such as swizzling, useful operator overrides, value broadcasting and more. Examples of these features can be seen below.
In addition to being able to instantiate each class, there are also a large array of static mathematical functions available. These include things like interpolation, dot products and cross products for the vector classes, and transpose and inverse for the matrix classes. The full list of available properties and methods can be viewed via Intellisense or by calling the help
method on each type.
Additional static methods are available in the Scalar
class that work on scalar values.
Examples
The following are a range of examples demonstrating some of the uses of the linear algebra classes.
Vector3D Example
This first example shows basic usage of the Vector3D
class. Everything in this example is also applicable to Vector2D
and Vector4D
.
from mastapy import Vector3D
# You can create a vector in a few different ways
my_vector = Vector3D(1.0, 2.0, 3.0)
my_vector = Vector3D.broadcast(2.0)
my_vector = Vector3D.from_tuple((1.0, 2.0, 3.0))
# All x, y and z components can be accessed
x = my_vector.x
y = my_vector.y
z = my_vector.z
# They can also be accessed by index
x = my_vector[0]
y = my_vector[1]
z = my_vector[2]
# Swizzling is supported
xy = my_vector.xy
zyx = my_vector.zyx
yyy = my_vector.yyy
# As Vector3D behaves like a tuple, you can use built-in methods for
# manipulating sequences
length = len(my_vector)
maximum = max(my_vector)
# You can perform mathematical operations on vectors.
# Note that both of these are component-wise operations.
# If you wish to perform a cross product, use the @
# operator.
added = my_vector + Vector3D(3.0, 2.0, 1.0)
multiplied = my_vector * (4.0, 5.0, 6.0)
# You can broadcast mathematical operations
subtracted = my_vector - 1.0
divided = my_vector / 2.0
print('Original:', my_vector)
print('ZYX swizzle:', zyx)
print()
print('Length:', length)
print('Max:', maximum)
print('Added:', added)
print('Multiplied:', multiplied)
print('Subtracted:', subtracted)
print('Divided:', divided)
Output:
Original: (1.0, 2.0, 3.0)
ZYX swizzle: (3.0, 2.0, 1.0)
Length: 3
Max: 3.0
Added: (4.0, 4.0, 4.0)
Multiplied: (4.0, 10.0, 18.0)
Subtracted: (0.0, 1.0, 2.0)
Divided: (0.5, 1.0, 1.5)
Vector Linear Algebra Example
In addition to the basic usages in the previous example, a range of linear algebra methods are available. Again, most of the following methods are also available on the Vector2D
and Vector4D
classes.
from mastapy import Vector3D
# Note that we do not have to explicitly declare a vector as a Vector3D class
# to use it in any of the linear algebra methods. Any iterable object with
# a length of 3 can be used.
my_vector0 = Vector3D(1.0, 2.0, 3.0)
my_vector1 = 4.0, 5.0, 6.0
# Notice how all methods are static methods. This is to allow any iterable
# object to be passed into the methods.
dot_result = Vector3D.dot(my_vector0, my_vector1)
cross_result = Vector3D.cross(my_vector0, my_vector1)
interp_result = Vector3D.interpolate(my_vector0, my_vector1, 0.5)
my_vector0_n = Vector3D.normalize(my_vector0)
my_vector1_n = Vector3D.normalize(my_vector1)
reflect_result = Vector3D.reflect(my_vector0_n, my_vector1_n)
print('my_vector0:', my_vector0)
print('my_vector1:', my_vector1)
print()
print('Dot product:', dot_result)
print('Cross product:', cross_result)
print('Interpolation (0.5):', interp_result)
print()
print('my_vector0 normalized:', '{:.6f}'.format(my_vector0_n))
print('my_vector1 normalized:', '{:.6f}'.format(my_vector1_n))
print('Reflection:', '{:.6f}'.format(reflect_result))
Output:
my_vector0: (1.0, 2.0, 3.0)
my_vector1: (4.0, 5.0, 6.0)
Dot product: 32.0
Cross product: (-3.0, 6.0, -3.0)
Interpolation (0.5): (2.5, 3.5, 4.5)
my_vector0 normalized: (0.267261, 0.534522, 0.801784)
my_vector1 normalized: (0.455842, 0.569803, 0.683763)
Reflection: (-0.621296, -0.576174, -0.531052)
Matrix Example
Along with the vector classes are accompanying matrix classes. Currently only square matrices are supported.
from math import radians
from mastapy import Matrix4x4
# Matrices in the mastapy package are column-major. This means matrices are
# constructed as a series of columns and that order of transformation is
# right to left.
#
# Example:
#
# >>> rotation_matrix @ translation_matrix @ my_vector
#
# The order of transformation here is to translate first, then rotate.
my_point = 2.0, 5.0, 1.0, 1.0
my_direction = 2.0, 5.0, 1.0, 0.0
# There are a series of new constructors for matrices
identity_matrix = Matrix4x4.identity()
scale_matrix = Matrix4x4.diagonal(2.0)
translation_matrix = Matrix4x4.translation(1.0, 2.0, 3.0)
rotation_matrix = Matrix4x4.rotation_y(radians(75.0))
# You can access rows, columns and individual entries of a matrix
col0 = rotation_matrix[0]
col1 = rotation_matrix.column1
row2 = rotation_matrix.row2
elem = rotation_matrix.m33
# To transform a point or direction, there are a number of options available
# Notice how both transform_point and transform_direction return a Vector3D
# whereas the matrix multiplication operator returns either a Matrix4x4 or
# a Vector4D, depending on parameters
point_transform = Matrix4x4.transform_point(translation_matrix, my_point)
dir_transform = Matrix4x4.transform_direction(translation_matrix, my_point)
general_transform = scale_matrix @ rotation_matrix @ my_direction
# You can also manipulate matrices with a variety of built-in methods
inverse = Matrix4x4.inverse(rotation_matrix)
transpose = Matrix4x4.transpose(translation_matrix)
determinant = Matrix4x4.determinant(scale_matrix)
print('my_point:', my_point)
print('my_direction:', my_direction)
print()
print('Identity matrix:')
print(identity_matrix)
print()
print('Scale matrix:')
print(scale_matrix)
print()
print('Translation matrix:')
print(translation_matrix)
print()
print('Rotation matrix:')
print('{:.6f}'.format(rotation_matrix))
print()
print('col0:', '{:.6f}'.format(col0))
print('col1:', col1)
print('row2:', '{:.6f}'.format(row2))
print('elem:', elem)
print()
print('Point transform:', point_transform)
print('Direction transform:', dir_transform)
print('General transform:', '{:.6f}'.format(general_transform))
print()
print('Inverse:')
print('{:.6f}'.format(inverse))
print()
print('Transpose:')
print(transpose)
print()
print('Determinant:', determinant)
Output:
my_point: (2.0, 5.0, 1.0, 1.0)
my_direction: (2.0, 5.0, 1.0, 0.0)
Identity matrix:
(1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0)
Scale matrix:
(2.0, 0.0, 0.0, 0.0,
0.0, 2.0, 0.0, 0.0,
0.0, 0.0, 2.0, 0.0,
0.0, 0.0, 0.0, 2.0)
Translation matrix:
(1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 2.0,
0.0, 0.0, 1.0, 3.0,
0.0, 0.0, 0.0, 1.0)
Rotation matrix:
(0.258819, 0.000000, 0.965926, 0.000000,
0.000000, 1.000000, 0.000000, 0.000000,
-0.965926, 0.000000, 0.258819, 0.000000,
0.000000, 0.000000, 0.000000, 1.000000)
col0: (0.258819, 0.000000, -0.965926, 0.000000)
col1: (0.0, 1.0, 0.0, 0.0)
row2: (-0.965926, 0.000000, 0.258819, 0.000000)
elem: 0.25881904510252074
Point transform: (3.0, 7.0, 4.0)
Direction transform: (2.0, 5.0, 1.0)
General transform: (2.967128, 10.000000, -3.346065, 0.000000)
Inverse:
(0.258819, 0.000000, -0.965926, 0.000000,
0.000000, 1.000000, 0.000000, 0.000000,
0.965926, 0.000000, 0.258819, 0.000000,
0.000000, 0.000000, 0.000000, 1.000000)
Transpose:
(1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
1.0, 2.0, 3.0, 1.0)
Determinant: 16.0