Documentation/Source example
Example source

docs/Matrix.js

/**
 * Matrix helpers<br>
 * @module Matrix
*/

/**
 * Matrix class to provide helper functions for transforming the image in 3 dimensions.<br>
 * For more information, see: {@link https://learn.microsoft.com/en-us/windows/win32/api/d2d1helper/nl-d2d1helper-matrix3x2f}<br>
 * May be used for {@link D2DGraphics#SetTransform SetTransform}, {@link D2DGraphics#ApplyTransform ApplyTransform} or {@link module:Effects.Effects Effects.AffineTransform}<br>
 * @memberof module:Matrix
 * @class
 * @hideconstructor
 */
class Matrix3x2 {

    /**
     * Generates identity matrix.
     * @method
     * @return {Float32Array}
     */
    static Identity() {
        return new Float32Array([
            1, 0,
            0, 1,
            0, 0
        ]);
    }

    /**
     * Creates a translation transformation that has the specified x and y displacements.
     * @method
     * @param {float} dx
     * @param {float} dy
     * @return {Float32Array}
     */
    static Translation(dx, dy) {
        return new Float32Array([
            1, 0,
            0, 1,
            dx, dy
        ]);
    }

    /**
     * Creates a scale transformation that has the specified scale factors and center point.
     * @method
     * @param {float} sx
     * @param {float} sy
     * @param {float} centerX
     * @param {float} centerY
     * @return {Float32Array}
     */
    static Scale(sx, sy, centerX, centerY) {
        return new Float32Array([
            sx, 0,
            0, sy,
            centerX - sx * centerX, centerY - sy * centerY
        ]);
    }

    /**
     * Creates a rotation transformation that has the specified angle and center point.
     * @method
     * @param {float} angle
     * @param {float} centerX
     * @param {float} centerY
     * @return {Float32Array}
     */
    static Rotation(angle, centerX, centerY) {
        const angleInRadian = angle * (Math.PI / 180);
        const sinAngle = Math.sin(angleInRadian);
        const cosAngle = Math.cos(angleInRadian);
        return new Float32Array([
            cosAngle, sinAngle,
            -sinAngle, cosAngle,
            centerX - centerX * cosAngle + centerY * sinAngle, centerY - centerX * sinAngle - centerY * cosAngle
        ]);
    }

    /**
     * Creates a skew transformation that has the specified x-axis and y-axis values and center point.
     * @method
     * @param {float} degreeX
     * @return {Float32Array}
     */
    static Skew(angleX, angleY, centerX, centerY) {
        const angleXInRadian = angleX * (Math.PI / 180);
        const angleYInRadian = angleY * (Math.PI / 180);
        const tanAngleX = Math.tan(angleXInRadian);
        const tanAngleY = Math.tan(angleYInRadian);
        return new Float32Array([
            1, tanAngleY,
            tanAngleX, 1,
            -centerY * tanAngleX, -centerX * tanAngleY
        ]);
    }

    /**
     * Returns determinant for specified matrix.
     * @method
     * @param {Float32Array} m
     * @return {float}
     */
    static Determinant(m) {
        return m[0] * m[3] - m[1] * m[2];
    }

    /**
     * Returns product of two or more matrices.
     * @method
     * @param {Array<Float32Array>} matrices
     * @return {Float32Array}
     */
    static Multiply(...matrices) {
        if (matrices.length < 2) {
            throw new Error('At least two matrices are required for multiplication');
        }
        for (const m of matrices) {
            if (!(m instanceof Float32Array) || m.length !== 6) {
                throw new Error('Each matrix must be a Float32Array of 6 numbers');
            }
        }

        function mul(a, b) {
            return new Float32Array([
                a[0] * b[0] + a[1] * b[2],
                a[0] * b[1] + a[1] * b[3],

                a[2] * b[0] + a[3] * b[2],
                a[2] * b[1] + a[3] * b[3],

                a[4] * b[0] + a[5] * b[2] + b[4],
                a[4] * b[1] + a[5] * b[3] + b[5]
            ]);
        }

        let result = matrices[0];
        for (let i = 1; i < matrices.length; i++) {
            result = mul(result, matrices[i]);
        }
        return result;
    }

    /**
     * Uses this matrix m to transform the specified point and returns the result.
     * @method
     * @param {Array<Float32Array>} matrices
     * @return {Object(x, y)}
     */
    static TransformPoint(pointX, pointY, m) {
        return { 
            x: pointX * m[0] + pointY * m[2] + m[4],
            y: pointX * m[1] + pointY * m[3] + m[5]
        };
    }
}

/**
 * Matrix class to provide helper functions for transforming the image in 3 dimensions.<br>
 * For more information, see: {@link https://learn.microsoft.com/en-us/windows/win32/api/d2d1_1helper/nl-d2d1_1helper-matrix4x4f}<br>
 * May be used for {@link module:Effects.Effects Effects.D3DTransform}<br>
 * @memberof module:Matrix
 * @class
 * @hideconstructor
 */
class Matrix4x4 {

    /**
     * Generates identity matrix.
     * @method
     * @return {Float32Array}
     */
    static Identity() {
        return new Float32Array([
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        ]);
    }

    /**
     * Generates a transform matrix that translates the projection plane in the X, Y, or Z direction.
     * @method
     * @param {float} x
     * @param {float} y
     * @param {float} z
     * @return {Float32Array}
     */
    static Translation(x, y, z) {
        return new Float32Array([
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            x, y, z, 1
        ]);
    }

    /**
     * Generates a transform matrix that scales the projection plane in the X, Y, and/or Z direction.
     * @method
     * @param {float} x
     * @param {float} y
     * @param {float} z
     * @return {Float32Array}
     */
    static Scale(sx, sy, sz) {
        return new Float32Array([
            sx, 0, 0, 0,
            0, sy, 0, 0,
            0, 0, sz, 0,
            0, 0, 0, 1
        ]);
    }

    /**
     * Generates a transform matrix that rotates the projection plane about the X axis.
     * @method
     * @param {float} degreeX
     * @return {Float32Array}
     */
    static RotationX(degreeX) {
        const angleInRadian = degreeX * (Math.PI / 180);
        const sinAngle = Math.sin(angleInRadian);
        const cosAngle = Math.cos(angleInRadian);
        return new Float32Array([
            1, 0, 0, 0,
            0, cosAngle, sinAngle, 0,
            0, -sinAngle, cosAngle, 0,
            0, 0, 0, 1
        ]);
    }

    /**
     * Generates a transform matrix that rotates the projection plane about the Y axis.
     * @method
     * @param {float} degreeY
     * @return {Float32Array}
     */
    static RotationY(degreeY) {
        const angleInRadian = degreeY * (Math.PI / 180);
        const sinAngle = Math.sin(angleInRadian);
        const cosAngle = Math.cos(angleInRadian);
        return new Float32Array([
            cosAngle, 0, -sinAngle, 0,
            0, 1, 0, 0,
            sinAngle, 0, cosAngle, 0,
            0, 0, 0, 1
        ]);
    }

    /**
     * Generates a transform matrix that rotates the projection plane about the Z axis.
     * @method
     * @param {float} degreeZ
     * @return {Float32Array}
     */
    static RotationZ(degreeZ) {
        const angleInRadian = degreeZ * (Math.PI / 180);
        const sinAngle = Math.sin(angleInRadian);
        const cosAngle = Math.cos(angleInRadian);
        return new Float32Array([
            cosAngle, sinAngle, 0, 0,
            -sinAngle, cosAngle, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        ]);
    }

    /**
     * Generates a transform matrix that skews the projection plane in the X direction.
     * @method
     * @param {float} degreeX
     * @return {Float32Array}
     */
    static SkewX(degreeX) {
        const angleInRadian = degreeX * (Math.PI / 180);
        const tanAngle = Math.tan(angleInRadian);
        return new Float32Array([
            1, 0, 0, 0,
            tanAngle, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        ]);
    }

    /**
     * Generates a transform matrix that skews the projection plane in the Y direction.
     * @method
     * @param {float} degreeY
     * @return {Float32Array}
     */
    static SkewY(degreeY) {
        const angleInRadian = degreeY * (Math.PI / 180);
        const tanAngle = Math.tan(angleInRadian);
        return new Float32Array([
            1, tanAngle, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        ]);
    }

    /**
     * A perspective transformation with a depth value.
     * @method
     * @param {float} depth
     * @return {Float32Array}
     */
    static PerspectiveProjection(depth)
    {
        let proj = 0;

        if (depth > 0)
        {
            proj = -1/depth;
        }

        return new Float32Array([
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, proj,
            0, 0, 0, 1
        ]);
    }

    /**
     * Rotates the projection plane about the axis you specify.
     * @method
     * @param {float} x
     * @param {float} y
     * @param {float} z
     * @param {float} degree
     * @return {Float32Array}
     */
    static RotationArbitraryAxis(x, y, z, degree)
    {
        // Normalize the vector represented by x, y, and z

        const magnitude = Math.sqrt(x*x + y*y + z*z);
        x /= magnitude;
        y /= magnitude;
        z /= magnitude;

        const angleInRadian = degree * (Math.PI / 180);
        const sinAngle = Math.sin(angleInRadian);
        const cosAngle = Math.cos(angleInRadian);

        const oneMinusCosAngle = 1 - cosAngle;

        return new Float32Array([
            1             + oneMinusCosAngle * (x * x - 1),
            z  * sinAngle + oneMinusCosAngle *  x * y,
            -y * sinAngle + oneMinusCosAngle *  x * z,
            0,

            -z * sinAngle + oneMinusCosAngle *  y * x,
            1             + oneMinusCosAngle * (y * y - 1),
            x  * sinAngle + oneMinusCosAngle *  y * z,
            0,

            y  * sinAngle + oneMinusCosAngle *  z * x,
            -x * sinAngle + oneMinusCosAngle *  z * y,
            1             + oneMinusCosAngle * (z * z - 1),
            0,

            0, 0, 0, 1
        ]);
    }

    /**
     * Returns product of two or more matrices.
     * @method
     * @param {Array<Float32Array>} matrices
     * @return {Float32Array}
     */
    static Multiply(...matrices) {
        if (matrices.length < 2) {
            throw new Error('At least two matrices are required for multiplication');
        }
        for (const m of matrices) {
            if (!(m instanceof Float32Array) || m.length !== 16) {
                throw new Error('Each matrix must be a Float32Array of 16 numbers');
            }
        }

        function mul(a, b) {
            return new Float32Array([
                a[0] * b[0] + a[1] * b[4] + a[2] * b[8] + a[3] * b[12], 
                a[0] * b[1] + a[1] * b[5] + a[2] * b[9] + a[3] * b[13], 
                a[0] * b[2] + a[1] * b[6] + a[2] * b[10] + a[3] * b[14], 
                a[0] * b[3] + a[1] * b[7] + a[2] * b[11] + a[3] * b[15],

                a[4] * b[0] + a[5] * b[4] + a[6] * b[8] + a[7] * b[12], 
                a[4] * b[1] + a[5] * b[5] + a[6] * b[9] + a[7] * b[13], 
                a[4] * b[2] + a[5] * b[6] + a[6] * b[10] + a[7] * b[14], 
                a[4] * b[3] + a[5] * b[7] + a[6] * b[11] + a[7] * b[15],

                a[8] * b[0] + a[9] * b[4] + a[10] * b[8] + a[11] * b[12], 
                a[8] * b[1] + a[9] * b[5] + a[10] * b[9] + a[11] * b[13], 
                a[8] * b[2] + a[9] * b[6] + a[10] * b[10] + a[11] * b[14],
                a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11] * b[15],

                a[12] * b[0] + a[13] * b[4] + a[14] * b[8] + a[15] * b[12], 
                a[12] * b[1] + a[13] * b[5] + a[14] * b[9] + a[15] * b[13], 
                a[12] * b[2] + a[13] * b[6] + a[14] * b[10] + a[15] * b[14],
                a[12] * b[3] + a[13] * b[7] + a[14] * b[11] + a[15] * b[15]
            ]);            
            // const result = new Float32Array(16);

            // for (let i = 0; i < 4; i++) {

            //     for (let j = 0; j < 4; j++) {

            //         let sum = 0;

            //         for (let k = 0; k < 4; k++) {

            //             sum += a[i * 4 + k] * b[k * 4 + j];

            //         }

            //         result[i * 4 + j] = sum;

            //     }

            // }

            // return result;

        }

        let result = matrices[0];
        for (let i = 1; i < matrices.length; i++) {
            result = mul(result, matrices[i]);
        }
        return result;
    }

    /**
     * Returns determinant for specified matrix.
     * @method
     * @param {Float32Array} m
     * @return {float}
     */
    static Determinant(m)
    {
        const minor1 = m[12] * (m[1] * (m[6] * m[11] - m[10] * m[7]) - m[2] * (m[5] * m[11] - m[7] * m[9]) + m[3] * (m[5] * m[10] - m[6] * m[9]));
        const minor2 = m[13] * (m[0] * (m[4] * m[11] - m[8] * m[7]) - m[2] * (m[4] * m[11] - m[7] * m[8]) + m[3] * (m[4] * m[10] - m[6] * m[8]));
        const minor3 = m[14] * (m[0] * (m[5] * m[11] - m[9] * m[7]) - m[1] * (m[4] * m[11] - m[7] * m[8]) + m[3] * (m[4] * m[9] - m[5] * m[8]));
        const minor4 = m[15] * (m[0] * (m[5] * m[10] - m[9] * m[6]) - m[1] * (m[4] * m[10] - m[6] * m[8]) + m[2] * (m[4] * m[9] - m[5] * m[8]));

        return minor1 - minor2 + minor3 - minor4;
    }
}