Matrices Specification Kernel

// Copyright (c) 2024 The Foundry Visionmongers Ltd.  All Rights Reserved.
kernel MatricesSpecification : ImageComputationKernel<eComponentWise>
{
  Image<eWrite, eAccessPoint> output;

param:
  float3x3 mtx3Param;
  float4x4 mtx4Param;

  void define()
  {
    /// Matrices can be defined and passed through as params.
    defineParam(mtx3Param, "mtx3Param", float3x3({1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f}));
    defineParam(mtx4Param, "mtx4Param", float4x4({1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f}));
  }

local:
  float3x3 mtx3Local;
  float4x4 mtx4Local;

  void init() {
    /// Matrices can be declared as locals and initialised in init().

    mtx3Local = {1.0f, 2.0f, 3.0f,
                 4.0f, 5.0f, 6.0f,
                 7.0f, 8.0f, 9.0f};

    mtx4Local = {1.0f, 2.0f, 3.0f, 4.0f,
                 5.0f, 6.0f, 7.0f, 8.0f,
                 9.0f, 10.0f, 11.0f, 12.0f,
                 13.0f, 14.0f, 15.0f, 16.0f};
    mtx3Local.transpose();
    mtx4Local = mtx4Local.rotateX(1.0f);
  }

  void MatrixOperationsExample() {
    /// There are two square row-major matrix types available in Blink kernels, 3x3 and 4x4.
    // They can be constructed with their elements set either by using an initialising list or regular constructor.
    // Matrices can also be left uninitialised and set later.
    float3x3 m3x3 = {1.0f, 2.0f, 3.0f,
                     4.0f, 5.0f, 6.0f,
                     7.0f, 8.0f, 9.0f};

    float4x4 m4x4 (1.0f, 2.0f, 3.0f, 4.0f,
                   5.0f, 6.0f, 7.0f, 8.0f,
                   9.0f, 10.0f, 11.0f, 12.0f,
                   13.0f, 14.0f, 15.0f, 16.0f);

    // Values from a matrix can be accessed with the double subscript operator [row][column].
    float c01 = m3x3[0][1];

    /// Matrices defined in the process method can have their elements updated in a number of useful ways.
    // setElements(...): Updates the entire matrix.
    m3x3.setElements(2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f);
    // setArray(float[N*N]): Alternatively you can use an array to update the matrix values.
    // Ensure that the array is the same size as the matrix.
    float a[3*3] = {2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f};
    m3x3.setArray(a);
    // Individual values can be updated separately with the double subscript operator.
    m3x3[1][1] = 3.4f;

    /// Common matrix patterns have special methods to set them to reduce the amount of manual matrix construction.
    // setIdentity(): Creates the NxN identity matrix.
    m3x3.setIdentity();
    // setTranslate(floatN): Creates an identity matrix with a translation vector on the rightmost column.
    m3x3.setTranslate(float3(1.0f, 1.0f, 1.0f));
    // setScale(floatN): Creates a diagonal matrix with the values of the vector.
    m3x3.setScale(float3(2.0f, 2.0f, 2.0f));
    // setRotate(angle): Creates a 3x3 rotation matrix for the input angle in radians for the 2D coordinate system (3x3 matrices only).
    m3x3.setRotate(PI/4);
    // setRotateX(angle): Creates a 4x4 rotation matrix about the X axis for the input angle in radians for the 3D coordinate system (4x4 matrices only).
    m4x4.setRotateX(PI/4);
    // setRotateY(angle): Creates a 4x4 rotation matrix about the Y axis for the input angle in radians for the 3D coordinate system (4x4 matrices only).
    m4x4.setRotateY(PI/4);
    // setRotateZ(angle): Creates a 4x4 rotation matrix about the Z axis for the input angle in radians for the 3D coordinate system (4x4 matrices only).
    m4x4.setRotateZ(PI/4);

    /// There are a number of methods the matrix object provides.
    /// Each of the following methods operates on the matrix in place as well as returning a reference for assigning other matrices.
    /// E.g. mat.transpose() will transpose the m3x3 matrix.0f, mat2 = mat1.transpose() will transpose the mat1 matrix and assign the result to mat2.
    // translate(floatN): Creates a translation matrix T and left multiplies the matrix in place.
    m3x3.translate(float3(1.0f, 1.0f, 1.0f));
    // scale(floatN): Creates a scale matrix S and left multiplies the matrix in place.
    m3x3.scale(float3(2.0f, 3.0f, 4.0f));
    // rotate(angle): Creates a rotation matrix R and left multiplies the matrix in place (3x3 matrices only).
    m3x3.rotate(PI/4);
    // rotateX(angle): Creates a rotation matrix about the X axis and left multiplies the matrix in place (4x4 matrices only).
    m4x4.rotateX(PI/4);
    // rotateY(angle): Creates a rotation matrix about the Y axis and left multiplies the matrix in place (4x4 matrices only).
    m4x4.rotateY(PI/4);
    // rotateZ(angle): Creates a rotation matrix about the Z axis left multiplies the matrix in place (4x4 matrices only).
    m4x4.rotateZ(PI/4);
    // transpose(): Computes the transpose of the matrix.
    m3x3.transpose();
    // invert(): Computes the inverse of the matrix. Ensure that it can be inverted before running this.
    m3x3.invert();

    /// Matrix operators.
    float3x3 A = {1.0f, 5.0f, 1.0f, 1.0f, 1.0f, 3.0f, 1.0f, 1.0f, 1.0f};
    float3x3 B = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f};
    float3x3 C;
    // Matrix addition.
    C = A + B;
    // Matrix subtraction.
    C = A - B;
    // Matrix * Matrix multiplication.
    C = A * B;
    // Matrix * Vector multiplication.
    float3 AxV = A * float3(2.0f, 2.0f, 2.0f);

    // Write out result of matrix operation to image.
    output() = A[2][1];
  }

  void process() {
    MatrixOperationsExample();
  }
};