@lightningtv/renderer
Version:
Lightning 3 Renderer
245 lines (229 loc) • 6.24 kB
text/typescript
/*
* If not stated otherwise in this file or this component's LICENSE file the
* following copyright and licenses apply:
*
* Copyright 2023 Comcast Cable Communications Management, LLC.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A 3D matrix representing a 2D graphics transformation
*
* @remarks
* For convenience, entries in the first two rows can be accessed by the following
* properties:
* | ta tb tx |
* | tc td ty |
* | 0 0 1 |
*
* This matrix is optimized for 2D transformations and hence the last row will
* always be considered [0, 0 ,1].
*
* To access a column major array for WebGL, use the {@link getFloatArr} method.
*/
export class Matrix3d {
public ta: number;
public tb: number;
public tx: number;
public tc: number;
public td: number;
public ty: number;
private _floatArr: Float32Array | null = null;
/**
* Potential Mutation Flag
*
* @remarks
* This flag is set to true whenever the matrix is potentially modified.
* We don't waste CPU trying to identify if each operation actually modifies
* the matrix. Instead, we set this flag to true whenever we think the matrix
* is modified. This signals that the `floatArr` should to be updated.
*/
private mutation: boolean;
/**
* Creates a new 3x3 matrix.
*
* @param entries Row-major 3x3 matrix
*/
public constructor() {
this.ta = 0;
this.tb = 0;
this.tx = 0;
this.tc = 0;
this.td = 0;
this.ty = 0;
this.mutation = true;
}
/**
* Returns a temporary matrix that can be used for calculations.
*
* @remarks
* This is useful for avoiding allocations in tight loops.
*
* The matrix is not guaranteed to be the same between calls.
*
* @returns
*/
public static get temp(): Matrix3d {
return tempMatrix;
}
public static multiply(a: Matrix3d, b: Matrix3d, out?: Matrix3d): Matrix3d {
const e0 = a.ta * b.ta + a.tb * b.tc;
const e1 = a.ta * b.tb + a.tb * b.td;
const e2 = a.ta * b.tx + a.tb * b.ty + a.tx;
const e3 = a.tc * b.ta + a.td * b.tc;
const e4 = a.tc * b.tb + a.td * b.td;
const e5 = a.tc * b.tx + a.td * b.ty + a.ty;
if (!out) {
out = new Matrix3d();
}
out.ta = e0;
out.tb = e1;
out.tx = e2;
out.tc = e3;
out.td = e4;
out.ty = e5;
out.mutation = true;
return out;
}
public static identity(out?: Matrix3d): Matrix3d {
if (!out) {
out = new Matrix3d();
}
out.ta = 1;
out.tb = 0;
out.tx = 0;
out.tc = 0;
out.td = 1;
out.ty = 0;
out.mutation = true;
return out;
}
public static translate(x: number, y: number, out?: Matrix3d): Matrix3d {
if (!out) {
out = new Matrix3d();
}
out.ta = 1;
out.tb = 0;
out.tx = x;
out.tc = 0;
out.td = 1;
out.ty = y;
out.mutation = true;
return out;
}
public static scale(sx: number, sy: number, out?: Matrix3d): Matrix3d {
if (!out) {
out = new Matrix3d();
}
out.ta = sx;
out.tb = 0;
out.tx = 0;
out.tc = 0;
out.td = sy;
out.ty = 0;
out.mutation = true;
return out;
}
public static rotate(angle: number, out?: Matrix3d): Matrix3d {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
if (!out) {
out = new Matrix3d();
}
out.ta = cos;
out.tb = -sin;
out.tx = 0;
out.tc = sin;
out.td = cos;
out.ty = 0;
out.mutation = true;
return out;
}
public static copy(src: Matrix3d, dst?: Matrix3d): Matrix3d {
if (!dst) {
dst = new Matrix3d();
}
dst.ta = src.ta;
dst.tc = src.tc;
dst.tb = src.tb;
dst.td = src.td;
dst.tx = src.tx;
dst.ty = src.ty;
dst.mutation = true;
return dst;
}
public translate(x: number, y: number): Matrix3d {
this.tx = this.ta * x + this.tb * y + this.tx;
this.ty = this.tc * x + this.td * y + this.ty;
this.mutation = true;
return this;
}
public scale(sx: number, sy: number): Matrix3d {
this.ta = this.ta * sx;
this.tb = this.tb * sy;
this.tc = this.tc * sx;
this.td = this.td * sy;
this.mutation = true;
return this;
}
public rotate(angle: number): Matrix3d {
if (angle === 0 || !((angle % Math.PI) * 2)) {
return this;
}
const cos = Math.cos(angle);
const sin = Math.sin(angle);
const e0 = this.ta * cos + this.tb * sin;
const e1 = this.tb * cos - this.ta * sin;
const e3 = this.tc * cos + this.td * sin;
const e4 = this.td * cos - this.tc * sin;
this.ta = e0;
this.tb = e1;
this.tc = e3;
this.td = e4;
this.mutation = true;
return this;
}
public multiply(other: Matrix3d): Matrix3d {
return Matrix3d.multiply(this, other, this);
}
/**
* Returns the matrix as a Float32Array in column-major order.
*
* @remarks
* This method is optimized to avoid unnecessary allocations. The same array
* is returned every time this method is called, and is updated in place.
*
* WARNING: Use the array only for passing directly to a WebGL shader uniform
* during a frame render. Do not modify or hold onto the array for longer than
* a frame.
*/
getFloatArr(): Float32Array {
if (!this._floatArr) {
this._floatArr = new Float32Array(9);
}
if (this.mutation) {
this._floatArr[0] = this.ta;
this._floatArr[1] = this.tc;
this._floatArr[2] = 0;
this._floatArr[3] = this.tb;
this._floatArr[4] = this.td;
this._floatArr[5] = 0;
this._floatArr[6] = this.tx;
this._floatArr[7] = this.ty;
this._floatArr[8] = 1;
this.mutation = false;
}
return this._floatArr;
}
}
const tempMatrix = new Matrix3d();