|Simple Vector Library (1.5)|
The Simple Vector Library (SVL) provides vector and matrix classes, as well as a number of functions for performing vector arithmetic with them. Equation-like syntax is supported via class operators, for example:
#include "svl/SVL.h"Both generic (arbitrarily-sized), and fixed-size (2, 3 and 4 element) vectors and matrices are supported. The latter are provided for the efficient manipulation of vectors or points in 2D or 3D space, and make heavy use of inlining for frequently-used operations. (One of the design goals of SVL was to ensure that it was as fast as the C-language, macro-based libraries it was written to replace.)
Vec3 v(1.0, 2.0, 3.0);
Mat3 m(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
v = 2 * v + m * v;
v *= (m / 3.0) * norm(v);
cout << v << endl;
SVL is a subset of VL, a more extensive vector library, which in addition contains classes for sparse vector/matrices, sub-vector/matrices, and implementations of some iterative solvers. Whereas with SVL the component type of vectors and matrices is defined with a compile-time switch, with VL you specify the type explicitly (Vec2f, Vec2d) and can, for example, mix matrices of doubles with vectors of floats.
SVL requires C++. It is known to compile under Irix CC, g++, and MSVC.
The latest version can be retrieved from http://www.cs.cmu.edu/~ajw/public/dist/.
This documentation can be found online at http://www.cs.cmu.edu/~ajw/doc/svl.html.
SVL contains the following types and classes:
Real component type (float or double)
Mat2 2 x 2 matrix
Mat3 3 x 3 matrix
Mat4 4 x 4 matrix
Mat n x m matrix
The elements of a vector or matrix are accessed with standard C array notation:
For the resizeable vector types, the current size can be obtained from the Elts() method for vectors, and the Rows() and Cols() methods for matrices. To iterate over all members of these types, you can use code of the form:
v = 4.0; // set element 2 of the vector
m = 5.0 // set row 3, column 4 of the matrix
m = v; // set row 2 of the matrix
for (i = 0; i < v.Elts(); i++)
v[i] = i;
for (i = 0; i < m.Rows(); i++)Though it seems slightly unintuitive, if you have a pointer to a vector or matrix, you must dereference it first before indexing it:
for (j = 0; j < m.Cols(); j++)
m[i][j] = i + j;
(*vPtr) = 3.0;If you need a pointer to the data belonging to a vector or matrix, use the Ref() method. (Matrices are stored by row.)
Real *vecDataPtr = v.Ref(), *matDataPtr = m.Ref();Warning: Any pointer to a generic matrix or vector will become invalid as soon as it is resized.
Note: If you compile with the symbol
VL_DEBUG, index range checks will be performed on all element accesses.
|Arithmetic Operators and Functions|
Basic arithmetic: + - * /Vector multiplication and division is pairwise: (a * b)[i] = a[i] * b[i]. (See below for how to form the dot product of two vectors with dot().) Matrix multiplication is defined as usual, and matrix division is undefined.
Accumulation arithmetic: += -= *= /=
Comparison: ==, !=
For both matrices and vectors, multiplication and division by a scalar is also allowed. Matrices can be multiplied either on the left or the right by a vector. In the expression m * v, v is treated as a column vector; in the expression v * m, it is treated as a row vector.
In the above, VecN is either a Vec or a Vec, and Real is a Float or a Double, depending on the argument type. For more on the use of the proj() operator, see Transformations.
Real dot(const VecN &a, const VecN &b); // inner product of a and b
Real len(const VecN &v); // length of v: || v ||
Real sqrlen(const VecN &v); // length of v, squared
VecN norm(const VecN &v); // v / || v ||
Vec2 cross(const Vec2 &a); // vector orthogonal to a
Vec3 cross(const Vec3 &a, const Vec3 &b); // vector orthogonal to a and b
Vec4 cross(const Vec4 &a, const Vec4 &b, const Vec4 &c);
// vector orthogonal to a, b and c
Vec2 proj(const Vec3 &v); // homog. projection: v[0..1] / v
Vec3 proj(const Vec4 &v); // homog. projection: v[0..2] / v
MatN trans(const MatN &m); // Transpose of mIn the above, MatN is any matrix type, a, though the det() and adj() functions are only defined for Mat.
Real trace(const MatN &m); // Trace of m
MatN adj(const MatN &m); // Adjoint of m
Real det(const MatN &m); // Determinant of m
MatN inv(const MatN &m); // Inverse of m, if it exists.
There are a number of 'magic' constants in SVL that can be used to initialise vectors or matrices with simple assignment statements. For example:
Vec3 v; Mat3 m; Vec v8(8);Below is a summary of the constants defined by SVL.
v = vl_0 [0, 0, 0]
v = vl_y [0, 1, 0]
v = vl_1 [1, 1, 1]
m = vl_0; 3 x 3 matrix, all elts. set to zero.
m = vl_1; 3 x 3 identity matrix
m = vl_B; 3 x 3 matrix, all elts. set to one.
v8 = vl_axis(6); [0, 0, 0, 0, 0, 0, 1, 0]
vl_one/vl_1/vl_I vector of all 1s, or identity matrix
vl_zero/vl_0/vl_Z vector or matrix of all 0s
vl_B matrix of all 1s
vl_x, vl_y, vl_z, vl_w x, y, z and w axis vectors
vl_axis(n) zero vector with element n set to 1
In general, a vector or matrix constructor should be given either one of the initialiser constants listed above, or a list of values for its elements. If neither of these is supplied, the variable will be uninitialised. The first arguments to the constructor of a generic vector or matrix should always be the required size. Thus matrices and vectors are declared as follows:
Vec v([initialisation_constant | element_list]);If generic vectors or matrices are not given a size when first created, they are regarded as empty, with no associated storage. This state persists until they are assigned a matrix/vector or the result of some computation, at which point they take on the dimensions of that result.
Vec v([elements, [initialisation_constant | element_list]]);
Mat m([initialisation_constant | element_list]);
Mat m([rows, columns, [initialisation_constant | element_list]]);
Vec3 v(vl_1);Warning: When initialising a generic vector or matrix with a list of elements, you must always ensure there is no possibility of the element being mistaken for an integer. (This is due to limitations of the stdarg package.) Make sure that each element value has either an exponent or a decimal point, i.e., use '2.0' rather than just '2'.
Vec3 v(1.0, 2.0, 3.0);
Vec v(6, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
Vec v(20, vl_axis(10));
Mat2 m(1.0, 2.0, 3.0, 4.0);
Mat m(10, 20, vl_I);
Finally, to set the size of a empty matrix or vector explicitly, or resize an existing matrix or vector, use the SetSize method:
|Input and Output|
All of the vector and matrix types in SVL can be used in iostream-type expressions. For example:
#include <iostream.h>will output
cout << v << 2 * v << endl;
cin >> v;
[1 1 1][2 2 2]and then prompt for input. Vectors and matrices are parsed in the same format that they are output: vectors are delimited by square brackets, elements separated by white space, and matrices consist of a series of row vectors, again delimited by square brackets.
The following are the transformations supported by SVL.
Mat2 Rot2(Real theta)Transformations with a prefix of 'H' operate in the homogeneous coordinate system, which allows translation and shear transformations, as well as the usual rotation and scale. In this coordinate system an n-vector is embedded in a (n+1)-dimensional space, e.g., a homogeneous point in 2d is represented by a 3-vector.
// rotate a 2d vector CCW by theta
Mat2 Scale2(const Vec2 &s)
// scale by s around the origin
Mat3 HRot3(Real theta)
// rotate a homogeneous 2d vector CCW by theta
Mat3 HScale3(const Vec2 &s)
// scale by s around the origin, in homogeneous 2d coords.
Mat3 HTrans3(const Vec2 &t)
// translate a homogeneous 2d vector by t
Mat3 Rot3(const Vec3 &axis, Real theta)
// rotate a 3d vector CCW around axis by theta
Mat3 Rot3(const Vec4 &q)
// rotate a 3d vector by the quaternion q
Mat3 Scale3(const Vec3 &s)
// scale by s around the origin
Mat4 HRot4(const Vec3 &axis, Real theta)
// rotate a homogeneous 3d vector CCW around axis by theta
Mat4 HRot4(const Vec4 &q)
// rotate a homogeneous 3d vector by the quaternion q
Mat4 HScale4(const Vec3 &s)
// scale by s around the origin, in homogeneous 3d coords
Mat4 HTrans4(const Vec3 &t)
// translate a homogeneous 3d vector by t
To convert from non-homogeneous to homogeneous vectors, make the extra coordinate (usually 1) the second argument in a constructor of/cast to the next-higher dimension vector. To project from a homogeneous vector down to a non-homogeneous one (doing a homogeneous divide in the process), use the proj() function. This process can be simplified by the use of the xform() function, which applies a transform to a vector, doing homogeneous/nonhomogeneous conversions if necessary. For example:
Vec3 x,y;By default, SVL assumes that transformations should operate on column vectors (v = T * v), though it can be compiled to assume row vectors instead (v = v * T).
// apply homogeneous transformations to a 3-vector
x = proj(Scale4(...) * Rot4(...) * Trans4(...) * Vec4(y, 1.0));
// do the same thing with xform()
x = xform(Scale4(...) * Rot4(...) * Trans4(...), y);
|Compiling with SVL|
VL_FLOAT - use floats instead of doubles
VL_DEBUG - turn on index checking and assertions
VL_ROW_ORIENT - transformations operate on row vectors instead of column vectors
|Using SVL With OpenGL|
SVL comes with a header file, SVLgl.h, which makes using SVL vectors with OpenGL more convenient. For example:
Vec3 x(24, 0, 100), y(40, 20, 10);
Please forward bug reports, comments, or suggestions to:Andrew Willmott (firstname.lastname@example.org), Graphics Group, SCS, CMU.