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.

Usage

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

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.

Methods

apply

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.

Argument Type Required Description
typedArray Array. or TypedArray Yes The Array value.
offset Number No
nbIterations Number No

getMatrix

Returns the internal mat4 matrix.

identity

Resets the MatrixBuilder to the Identity matrix.

invert

Inverts the MatrixBuilder matrix.

multiply

Argument Type Required Description
mat4x4 mat4 Yes

multiply3x3

Multiply the current matrix with the provided 3x3 matrix.

Argument Type Required Description
mat3x3 mat3 Yes column-first matrix

new

Argument Type Required Description
useDegree Boolean No

rotate

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

Argument Type Required Description
angle Number Yes
axis Vector3 Yes

rotateFromDirections

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.

Argument Type Required Description
originDirection Array. Yes
targetDirection Array. Yes

rotateX

Rotates angle degrees/radians around the X axis.

Argument Type Required Description
angle Number Yes

rotateY

Rotates angle degrees/radians around the Y axis.

Argument Type Required Description
angle Number Yes

rotateZ

Rotates angle degrees/radians around the Z axis.

Argument Type Required Description
angle Number Yes

scale

Scales the matrix by sx, sy, sz.

Argument Type Required Description
sx Number Yes
sy Number Yes
sz Number Yes

setMatrix

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.

Argument Type Required Description
mat4x4 mat4 Yes

translate

Translates the matrix by x, y, z.

Argument Type Required Description
x Number Yes The x coordinate.
y Number Yes The y coordinate.
z Number Yes The z coordinate.

Source

index.d.ts
import { mat3, mat4 } from 'gl-matrix';
import { TypedArray, Vector3 } from '../../../types';

declare interface Transform {
/**
*
* @param {Boolean} [useDegree]
*/
new (useDegree?: boolean);

/**
* 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.
* @param {Number[]} originDirection
* @param {Number[]} targetDirection
*/
rotateFromDirections(
originDirection: number[],
targetDirection: number[]
): Transform;

/**
* Normalizes the axis of rotation then rotates the current matrix `angle`
* degrees/radians around the provided axis.
* @param {Number} angle
* @param {Vector3} axis
*/
rotate(angle: number, axis: Vector3): Transform;

/**
* Rotates `angle` degrees/radians around the X axis.
* @param {Number} angle
*/
rotateX(angle: number): Transform;

/**
* Rotates `angle` degrees/radians around the Y axis.
* @param {Number} angle
*/
rotateY(angle: number): Transform;

/**
* Rotates `angle` degrees/radians around the Z axis.
* @param {Number} angle
*/
rotateZ(angle: number): Transform;

/**
* Translates the matrix by x, y, z.
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @param {Number} z The z coordinate.
*/
translate(x: number, y: number, z: number): Transform;

/**
* Scales the matrix by sx, sy, sz.
* @param {Number} sx
* @param {Number} sy
* @param {Number} sz
*/
scale(sx: number, sy: number, sz: number): Transform;

/**
*
* @param {mat4} mat4x4
*/
multiply(mat4x4: mat4): Transform;

/**
* Multiply the current matrix with the provided 3x3 matrix.
* @param {mat3} mat3x3 column-first matrix
*/
multiply3x3(mat3x3: mat3): Transform;

/**
* Resets the MatrixBuilder to the Identity matrix.
*/
identity(): Transform;

/**
* Inverts the MatrixBuilder matrix.
*/
invert(): Transform;

/**
* 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.
* @param {Number[]|TypedArray} typedArray The Array value.
* @param {Number} [offset]
* @param {Number} [nbIterations]
*/
apply(
typedArray: number[] | TypedArray,
offset?: number,
nbIterations?: number
): Transform;

/**
* Returns the internal `mat4` matrix.
*/
getMatrix(): 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.
* @param {mat4} mat4x4
*/
setMatrix(mat4x4: mat4): Transform;
}

/**
*
* @return {Transform}
*/
declare function buildFromDegree(): Transform;

/**
*
* @return {Transform}
*/
declare function buildFromRadian(): Transform;

/**
* 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
* ```js
* let point = [2,5,12];
* vtkMatrixBuilder.buildFromDegree().translate(1,0,2).rotateZ(45).apply(point);
* ```
*
* 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.
*
*/
export declare const vtkMatrixBuilder: {
buildFromDegree: typeof buildFromDegree;
buildFromRadian: typeof buildFromRadian;
};

export default vtkMatrixBuilder;
index.js
import { vec3, mat4, glMatrix } from 'gl-matrix';

// eslint-disable-next-line import/no-cycle
import { areMatricesEqual } from 'vtk.js/Sources/Common/Core/Math';
import { IDENTITY } from 'vtk.js/Sources/Common/Core/Math/Constants';

const NoOp = (v) => v;

const EPSILON = 1e-6;

class Transform {
constructor(useDegree = false) {
this.matrix = mat4.identity(new Float64Array(16));
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) < EPSILON) {
// cross product is 0, so pick arbitrary axis perpendicular
// to originDirection.
vec3.cross(this.tmp, [1, 0, 0], originDirection);
if (vec3.length(this.tmp) < EPSILON) {
vec3.cross(this.tmp, [0, 1, 0], originDirection);
}
}
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;
}

multiply(mat4x4) {
mat4.multiply(this.matrix, this.matrix, mat4x4);
return this;
}

multiply3x3(mat3x3) {
mat4.multiply(this.matrix, this.matrix, [
mat3x3[0],
mat3x3[1],
mat3x3[2],
0,
mat3x3[3],
mat3x3[4],
mat3x3[5],
0,
mat3x3[6],
mat3x3[7],
mat3x3[8],
0,
0,
0,
0,
1,
]);
return this;
}

invert() {
mat4.invert(this.matrix, this.matrix);
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,
};