MatrixBuilder

Introduction

The vtkMatrixBuilder class provides a system to create a mat4 transformation matrix. All functions return the MatrixBuilder Object instance, allowing transformations to be chained.

Example:

let point = [2,5,12];
vtkMatrixBuilder.buildfromDegree().translate(1,0,2).rotateZ(45).apply(point);

Creating an instance

The vtkMatrixBuilder class has two functions, vtkMatrixBuilder.buildFromDegree() and vtkMatrixbuilder.buildFromRadian(), predefining the angle format used for transformations and returning a MatrixBuilder instance. The matrix is initialized with the Identity Matrix.

Transformation Functions

rotateFromDirections(originDirection, targetDirection)

Multiplies the current matrix with a transformation matrix created by normalizing both direction vectors and rotating around the axis of the crossProduct by the angle from the dotProduct of the two directions.

rotate(angle, axis)

Normalizes the axis of rotation then rotates the current matrix angle degrees/radians around the provided axis.

rotateX(angle)

Rotates angle degrees/radians around the X axis.

rotateY(angle)

Rotates angle degrees/radians around the Y axis.

rotateZ(angle)

Rotates angle degrees/radians around the Z axis.

translate(x, y, z)

Translates the matrix by x, y, z.

scale(sx, sy, sz)

Scales the matrix by sx, sy, sz.

identity()

Resets the MatrixBuilder to the Identity matrix.

setMatrix(mat4)

Copies the given mat4 into the builder. Useful if you already have a transformation matrix and want to transform it further. Returns the instance for chaining.

Using the MatrixBuilder result

apply(typedArray, offset = 0, nbIterations = -1)

Multiplies the array by the MatrixBuilder’s internal matrix, in sets of 3. Updates the array in place.
If specified, offset starts at a given position in the array, and nbIterations will determine the number of iterations (sets of 3) to loop through. Assumes the typedArray is an array of multiples of 3, unless specifically handling with offset and iterations.
Returns the instance for chaining.

getMatrix()

Returns the internal mat4 matrix.

Source

index.js
import { vec3, mat4, glMatrix } from 'gl-matrix';
import { areMatricesEqual } from 'vtk.js/Sources/Common/Core/Math';

const NoOp = (v) => v;

const IDENTITY = new Float64Array(16);
mat4.identity(IDENTITY);

const EPSILON = 1e-6;

class Transform {
constructor(useDegree = false) {
this.matrix = new Float64Array(16);
mat4.identity(this.matrix);
this.tmp = new Float64Array(3);
this.angleConv = useDegree ? glMatrix.toRadian : NoOp;
}

rotateFromDirections(originDirection, targetDirection) {
const src = new Float64Array(3);
const dst = new Float64Array(3);
const transf = new Float64Array(16);

vec3.set(src, originDirection[0], originDirection[1], originDirection[2]);
vec3.set(dst, targetDirection[0], targetDirection[1], targetDirection[2]);
vec3.normalize(src, src);
vec3.normalize(dst, dst);
const cosAlpha = vec3.dot(src, dst);
if (cosAlpha >= 1) {
return this;
}

vec3.cross(this.tmp, src, dst);
if (vec3.length(this.tmp, this.tmp) < EPSILON) {
// cross product is 0, so pick arbitrary axis perpendicular
// to originDirection.
if (originDirection[0] === 0 && originDirection[1] === 0) {
vec3.set(this.tmp, 0, 1, 0);
} else {
vec3.set(this.tmp, 0, -originDirection[2], -originDirection[1]);
}
}
mat4.fromRotation(transf, Math.acos(cosAlpha), this.tmp);
mat4.multiply(this.matrix, this.matrix, transf);

return this;
}

rotate(angle, axis) {
vec3.set(this.tmp, ...axis);
vec3.normalize(this.tmp, this.tmp);
mat4.rotate(this.matrix, this.matrix, this.angleConv(angle), this.tmp);
return this;
}

rotateX(angle) {
mat4.rotateX(this.matrix, this.matrix, this.angleConv(angle));
return this;
}

rotateY(angle) {
mat4.rotateY(this.matrix, this.matrix, this.angleConv(angle));
return this;
}

rotateZ(angle) {
mat4.rotateZ(this.matrix, this.matrix, this.angleConv(angle));
return this;
}

translate(x, y, z) {
vec3.set(this.tmp, x, y, z);
mat4.translate(this.matrix, this.matrix, this.tmp);
return this;
}

scale(sx, sy, sz) {
vec3.set(this.tmp, sx, sy, sz);
mat4.scale(this.matrix, this.matrix, this.tmp);
return this;
}

identity() {
mat4.identity(this.matrix);
return this;
}

//-----------

apply(typedArray, offset = 0, nbIterations = -1) {
if (areMatricesEqual(IDENTITY, this.matrix)) {
// Make sure we can chain apply...
return this;
}

const size =
nbIterations === -1 ? typedArray.length : offset + nbIterations * 3;
for (let i = offset; i < size; i += 3) {
vec3.set(this.tmp, typedArray[i], typedArray[i + 1], typedArray[i + 2]);
vec3.transformMat4(this.tmp, this.tmp, this.matrix);
typedArray[i] = this.tmp[0];
typedArray[i + 1] = this.tmp[1];
typedArray[i + 2] = this.tmp[2];
}

// Make sure we can chain apply...
return this;
}

getMatrix() {
return this.matrix;
}

setMatrix(mat4x4) {
if (!!mat4x4 && mat4x4.length === 16) {
mat4.copy(this.matrix, mat4x4);
}
return this;
}
}

function buildFromDegree() {
return new Transform(true);
}

function buildFromRadian() {
return new Transform(false);
}

export default {
buildFromDegree,
buildFromRadian,
};