Vector Calculus
Being a 3D-CAD system, trCAD works mainly on 3-dimensional space ℝ3. Therefore it contains a wide set of operations for calculating in 3-dimensional vector space. This includes
- the coordinate space
- the vector data type and vector operators
- the matrix data type, matrix operators and matrix inversion
and all available operators and functions for these, what is explained in this section. This may be difficult to understand for readers without some basic knowledge of vector calculus. In this case, we recommend to read only the section about coordinate space and the first part about vectors that describes the vector notation used in trCAD. The sections about vector operators and matrices are not required for the next chapters.
Coordinate Space
trCAD works on a 3-dimensional, right-handed coordinate system of neutral units. The three axis directions are labeled as X, Y and Z (in this order). By default, the Z direction is meant to point upwards, while the X and Y directions extend horizontally. The origin is the point with the coordinate values ( 0.0, 0.0, 0.0 ).
The vector Data Type
In trCAD, a vector is the coordinate representation of a point in the 3d construction space. It is defined by three floating point values that are the coordinates in X, Y and Z directions. A common way to imagine a vector is an "arrow" pointing from the origin to the respective point. If a vector is used to describe the difference between two space points, the arrow is imagined to point from one of the two points to the other.
(More information about 3d vector space: https://en.wikipedia.org/wiki/Euclidean_vector.)
A pure vector value can be written by either the vector object constructor or by using the trCAD vector bracket notation:
Example
vector( 1.0, 1.0, 0.0 ) // Vector value written as vector object constructor
<[ 1.0, 1.0, 0.0 ]> // Vector value written by vector bracket notation
Note
The vector bracket notation <[ X, Y, Z ]> can be used to define vector values.
A vector variable can thus be defined in multiple ways: by using the constructor or by copying of a vector value. Thus the three following definitions are identical:
Example
vector v1( 1.0, 1.0, 0.0 )
vector v2 = vector( 1.0, 1.0, 0.0 )
vector v3 = <[ 1.0, 1.0, 0.0 ]>
The Z coordinate can also be omitted in the vector definition. It will then automatically be set to zero. This is useful for working in a 2-dimensional environment:
Example
vector v = <[ 1.0, 2.0 ]>
echo( v )
Output
<[1,2,0]>
The different coordinate values of a vector variable can be accessed by the vector methods x, y and z in the following way:
Example
vector v( 1.0, 1.0, 0.0 )
echo( v.x + " " + v.y + " " + v.z )
Output
1 1 0
Vector Operators
Several additional operators are available to work with vector values. The first to be mentioned are the vector addition and vector subtraction operators
+ | - |
+= | -= |
They work like their default arithmetic operators counterpart but work on vectors. They return a vector that is the component-by-component sum or difference of two vectors:
Example
vector v1 = <[ 1.0, 0.0, 0.0 ]> + <[ 0.0, 2.0, 1.0 ]>
echo( v1 )
v1 -= <[ 1.0, 0.0, 0.0 ]>
echo( v1 )
Output
<[1,2,1]>
<[0,2,1]>
Similar but with a slight difference are the multiplication and division with and by a scalar. (A scalar is simply a floating point number. It is called like this to distinguish it from other objects as vectors or matrices.) They are
* | / |
*= | /= |
For the two operators in the upper line, one of the operands must be a vector and the other a scalar. For the division, the counter must be a vector and the denominator a scalar. The operators in the lower line work equally but the multiplication/division is directly applied to the left hand of the operator. The return value of all of these operations is the vector with each component multiplied/divided by the scalar value:
Example
vector v1 = <[ 1.0, 0.0, 2.0 ]> * 2.0
vector v2 = 2.0 * <[ 1.0, 0.0, 2.0 ]>
vector v3 = <[ 1.0, 0.0, 2.0 ]> / 2.0
// vector v4 = 2.0 / <[ 1.0, 0.0, 2.0 ]> // This is not allowed: the vector cannot be the denominator.
echo( v1 + "; " + v2 + "; " + v3 + ";" )
v1 *= 3.0
v2 /= 1.0
echo( v1 + "; " + v2 + ";" )
Output
<[2,0,4]>; <[2,0,4]>; <[0.5,0,1]>;
<[6,0,12]>; <[2,0,4]>;
The norm operator returns the norm of a vector, what corresponds to the length of the imagined arrow. Its operator sign is two pipe characters '|' that embrace the vector value:
| v | |
Example
vector v = <[ 1.0, 2.0, -1.5 ]>
echo( "The norm of vector " + v + " is " + |v| );
Output
The norm of vector <[1,2,-1.5]> is 2.6925824
Sometimes we may want to normalize a vector, i.e. making it have a length of one. Unless the norm is not zero, we can get a normalized version of the vector by dividing by its norm:
Example
vector v = <[ 1.0, 2.0, -1.5 ]>
v /= |v|
echo( "The norm of the normalized vector v is " + |v| );
Output
The norm of the normalized vector v is 1
The scalar product (or dot product) is a second type of multiplication operation for vectors. Unlike the multiplication of a vector by a scalar that results in a vector that was described above, the scalar product is a multiplication of two vectors that results in a scalar. The scalar product uses the same operator sign:
* |
but with one vector on each side:
Example
float d = <[ 1.0, 2.0, 0.0 ]> * <[ 2.0, 1.0, -1.0 ]>
echo( d )
Output
4
For more information about the scalar product see https://en.wikipedia.org/wiki/Dot_product.
A third type of multiplication that involves vector values is the vector product or cross product:
<*> |
It is a multiplcation of two vectors that results in a third one with a direction perpendicular to the first two:
Example
vector v = <[ 1.0, 2.0, 0.0 ]> <*> <[ 2.0, 1.0, -1.0 ]>
echo( v )
Output
<[-2,1,-3]>
More information about the vector product see https://en.wikipedia.org/wiki/Cross_product.
The matrix Data Type
A matrix is the numerical presentation of a linear operator that can be applied to vectors in order to transform these (see https://en.wikipedia.org/wiki/Transformation_matrix). In trCAD, matrices can be used as 3x3 matrices (classical transformation matrix) or as 3x4 matrices (affine transformation including translations). The definition for matrices resembles the one for vectors:
Example
// Defining of a 3x3 matrix (spaces and linebreaks used for a clearer code structure):
matrix m1 = <[<[ 0.5, 0.0, -0.5 ]>,
<[ 0.0, 1.0, 0.0 ]>,
<[ 2.0, 0.0, 0.5 ]>]>
echo( m1 )
Output
<[<[0.5,0,-0.5]>,<[0,1,0]>,<[2,0,0.5]>]>
In addition a 3x3 matrix can be written in a reduced 2x2 form, defining the upper left part of the matrix only. The not defined entries of the matrix are set to zero implicitly:
Example
// Defining of a 2x2 subgrid of a 3x3 matrix:
matrix m2 = <[<[ 0.5, -0.5 ]>,
<[ 2.0, 0.5 ]>]>
echo( m2 )
Output
<[<[0.5,-0.5,0]>,<[2,0.5,0]>,<[0,0,0]>]>
A 3x4 matrices has one additional column that is used to store a translation vector. This allows to include translations into the matrix form. The definition has the following form:
Example
// Defining of a 3x4 matrix with a translational vector <[1,2,1]>:
matrix m3 = <[<[ 0.5, 0.0, -0.5, 1.0 ]>,
<[ 0.0, 1.0, 0.0, 2.0 ]>,
<[ 2.0, 0.0, 0.5, 1.0 ]>]>
echo( m3 )
Output
<[<[0.5,0,-0.5,1]>,<[0,1,0,2]>,<[2,0,0.5,1]>]>
Matrix Operators
For calculations with matrices there are three types of operations for the three data types a matrix can intract with: matrix-scalar interaction, matrix-vector interaction and matrix-matrix interaction.
Matrix-Scalar Interaction
The simplest case of matrix operations is the matrix-scalar multiplication where a matrix is multiplied by a scalar value (i.e. a floating point number). Each component of the matrix is multiplied by the scalar separately. For this the matrix-scalar product operators are used:
* | *= |
In the following example, the matrix m1 is multiplied by scalar value 2.0, first using the '*' operator and then using the '*=' operator:
Example
matrix m1 = <[<[ 0.5, 0.0, -0.5 ]>,
<[ 0.0, 1.0, 0.0 ]>,
<[ 2.0, 0.0, 0.5 ]>]>
matrix m2 = 2.0 * m1
echo( m2 )
m1 *= 2.0
echo( m1 )
Output
<[<[1,0,-1]>,<[0,2,0]>,<[4,0,1]>]>
<[<[1,0,-1]>,<[0,2,0]>,<[4,0,1]>]>
The matrix-scalar division operators
/ | /= |
work in analogy to the matrix-scalar product, each component of the matrix is devided by the scalar value:
Example
matrix m1 = <[<[ 0.5, 0.0, -0.5 ]>,
<[ 0.0, 1.0, 0.0 ]>,
<[ 2.0, 0.0, 0.5 ]>]>
echo( m1 / 2.0 )
Output
<[<[0.25,0,-0.25]>,<[0,0.5,0]>,<[1,0,0.25]>]>
All matrix-scalar operations work on 3x4 matrices the same way as on 3x3 matrices.
Matrix-Vector Interaction
A matrix is multiplied by a vector v1 using the matrix-vector product operator.
* |
The operation returns another vector v2 that is the result of the application of the matrix transformation to v1:
Example
matrix m1 = <[<[ 0.5, 0.0, -0.5 ]>,
<[ 0.0, 1.0, 0.0 ]>,
<[ 2.0, 0.0, 0.5 ]>]>
vector v1 = <[ 0.0, 1.0, 1.0 ]>
vector v2 = m1 * v1
echo( v2 )
Output
<[-0.5,1,0.5]>
Note
Since only a matrix can be applied to a vector, not a vector to a matrix, the form v1 * m1, where v1 would need to be a transposed vector, is not supported. Therefore there is as well no operator '*=' for the matrix-vector product, which would imply evaluating v1 * m1.
Note
When applying a 3x4 matrix to a vector, a fourth component with the vaue 1 is added to the vector implicitly and virtually for that operation. That makes it possible to use the fourth column of the 3x4 matrix for a translation of the vector. Please note, that trCAD does not support "real" homogeneous coordinates (see https://en.wikipedia.org/wiki/Affine_transformation), the added component lives only virtually during the operation and is not considered in other vector operations as the scalar product.
Matrix-Matrix Interaction
The matrix-matrix multiplication operators
* | *= |
are used for multiplying m1 by another matrix m2 (see https://en.wikipedia.org/wiki/Matrix_multiplication). If the resulting matrix m3 = m1 * m2 is later applied to a vector, this corresponds to a sequential application of the two matrices, first of m2 and then of m1. So in the sequence m1 * m2 * v1 the matrix m1 is applied to m2 * v1. Therefore it is as well said, that m1 is applied to m2.
Example
matrix m1 = <[<[ 0.5, 0.0, -0.5 ]>,
<[ 0.0, 1.0, 0.0 ]>,
<[ 2.0, 0.0, 0.5 ]>]>
matrix m2 = <[<[ 3.0, 2.0, 0.5 ]>,
<[ 2.0, 1.0, 0.0 ]>,
<[ 0.5, 0.0, 0.0 ]>]>
vector v1 = <[ 0.0, 1.0, 2.0 ]>
// Appliying m1 to m2 by multiplying to gain m3
matrix m3 = m1 * m2
// Appliying matrices separately
vector v2 = m2 * v1
echo( v2 )
vector v3 = m1 * v2
echo( v3 )
// Appliying mulitplied matrix at once
vector v4 = m3 * v1
echo( v4 )
Output
<[3,1,0]>
<[1.5,1,6]>
<[1.5,1,6]>
Note
When applying a 3x4 matrix to another matrix, this other matrix is for this operation implicitly and virtually extended to a 4x4 matrix in a way that the fourth diagonal component is 1 and all other added components are 0. The result of this operation is again a 3x4 matrix. As noted above, these added components live only virtually during the operation, they are not considered in other operations and can not be changed or calculated with freely.
Note
In a matrix-matrix multiplication the order of the matrices matters. It makes a difference, if m1 is applied to m2 * v1 or m2 is applied to m1 * v1. Speaking in mathematical term, they do not commute. That means that building the matrix-matrix product m1 * m2 yields a different result than m2 * m1. Therefore it is as well important to note that m1 *= m2 is equivalent m1 = m1 * m2:
Example
matrix m1 = <[<[ 0.5, 0.0, -0.5 ]>,
<[ 0.0, 1.0, 0.0 ]>,
<[ 2.0, 0.0, 0.5 ]>]>
matrix m2 = <[<[ 3.0, 2.0, 0.5 ]>,
<[ 2.0, 1.0, 0.0 ]>,
<[ 0.5, 0.0, 0.0 ]>]>
echo( m2 * m1 )
echo( m1 * m2 )
// Is equivalent to m1 = m1 * m2
m1 *= m2
echo( m1 )
Output
<[<[2.5,2,-1.25]>,<[1,1,-1]>,<[0.25,0,-0.25]>]>
<[<[1.25,1,0.25]>,<[2,1,0]>,<[6.25,4,1]>]>
<[<[1.25,1,0.25]>,<[2,1,0]>,<[6.25,4,1]>]>
Matrix Inversion
Beside the operators there is one important predefined function that works on matrices: the inv( matrix ) function. It takes one matrix as argument and returns the inverted matrix as a result (see https://en.wikipedia.org/wiki/Invertible_matrix):
Example
matrix m1 = <[<[ 0.5, 0.0, -0.5 ]>,
<[ 0.0, 1.0, 0.0 ]>,
<[ 2.0, 0.0, 0.5 ]>]>
echo( inv( m1 ) )
echo ( inv(m1) * m1)
Output
<[<[0.4,0,0.4]>,<[0,1,0]>,<[-1.6,-0,0.4]>]>
<[<[1,0,0]>,<[0,1,0]>,<[0,0,1]>]>
Note
When inverting a 3x4 matrix, the matrix and its inverted result matrix are for this operation implicitly and virtually extended to 4x4 in an analog way to the procedure mentioned for the matrix-matrix interactions in section Matrix Operators. As noted above, these added components live only virtually during the operation, they are not considered in other operations and can not be changed or calculated with freely. The result of the inversion of a 3x4 matrix is another 3x4 matrix representing the reversed transformation including the correct translatory part.
Example
matrix m1 = <[<[ 2.0, 0.0, 0.0, 1.0 ]>,
<[ 0.0, 2.0, 0.0, 0.0 ]>,
<[ 0.0, 0.0, 2.0, 0.0 ]>]>
echo( inv( m1 ) )
echo ( inv(m1) * m1)
Output
<[<[0.5,0,0,-0.5]>,<[0,0.5,0,-0]>,<[0,0,0.5,-0]>]>
<[<[1,0,0]>,<[0,1,0]>,<[0,0,1]>]>
Note
Since inverting a matrix is a operation including several numerical operations on floating point numbers the result is only accurate within the general floating point accuracy of 10E-16. So it can happen that inv(m1) * m1 is deviating from the identity matrix in this range.