TL;DR:
Tensor would be added, which provides a standardized API for interacting with vectors, colors, matrices, quaternions, etc.
Rational
After merging #13699, the various vectors implement Vector. Vector, along with the color classes implement VectorLike. VectorLike defines a standard signature for different methods (e.g. copyFrom, clone, add, subtractInPlaceFromFloats, etc.). Vector extends VectorLike and adds normalization methods and length / lengthSquared. This proposal’s goals are based on the ideas behind #13699, with the core idea being to create a standard interface from which various math constructs are defined.
Semantics
Note:
CurrentClassis a placeholder for a class that implementsTensor. In fields, it is the current class.Tensorand its related types will be defined insrc/Maths/tensor.tsunless otherwise noted
MultidimensionalArray
Defined in src/types.ts
type MultidimensionalArray<T, D extends number> = D extends 1
? T[]
: MultidimensionalArray<T, D - 1>[];
Represents a multidimensional array of T with depth D.
TensorValue
type TensorValue = number | MultidimensionalArray<TensorValue, number>
Tensor
declare class Tensor<V extends TensorValue>
This proposal replaces VectorLike with a new type, Tensor, for tensor-like classes to implement. Vector would be changed to extend Tensor,
Tensor includes all of the methods in VectorLike. This includes
- Math operations (
add,subtract,multiply,divide,scale, …) - Array conversion (
fromArray,toArray,asArray, …)- Since the type parameter is no longer assignable to
number[], the return type for array-related methods will be changed tonumber[] - See
Tensor.value
- Since the type parameter is no longer assignable to
- Transfering (
clone,copyFrom,copyFromFloats, …) - The above methods’
InPlace,ToRef,FromFloats, etc.
It adds or changes the following methods:
Tensor.From
public static From(source: Tensor, fillValue: number = 0): CurrentClass;
From creates a new instance of CurrentClass from source.
source: The tensor to copy data from.
fillValue: The value to use for filling empty parts of the resulting CurrentClass.
Example:
const vec3 = new Vector3(1, 2, 3);
const vec4 = Vector4.From(vec3, 4); // { 1, 2, 3, 4 }
const matrix = Matrix.From(vec3, 0); // this is possible!
Additionally, From could be changed to be From(...args: [...Tensor[], number]): CurrentClass. This is better understood by this invalid Typescript signature (since rest parameters must be last):
public static From(...source: Tensor[], fillValue: number = 0): CurrentClass;
Tensor.as
public as<T extends typeof Tensor>(type: T, fillValue: number = 0): InstanceType<T>;
as creates an instance of type from an instance of CurrentClass.
type: The class to create an instance of.
fillValue: The value to use for filling empty parts of the resulting instance.
Example:
const vec3 = new Vector3(1, 2, 3);
const vec4 = vec3.as(Vector4, 4); // { 1, 2, 3, 4 }
const matrix = vec3.as(Matrix, 0);
Tensor.sum
public sum(): number;
sum return the sum of the components of the Tensor.
Example:
const scalar = new Scalar(1),
vec3 = new Vector3(1, 2, 3),
vec4 = new Vector4(4, 5, 6, 7),
matrix = new Matrix();
scalar.sum() // 1
scalar.sum() // 6
scalar.sum() // 22
scalar.sum() // 0
The return value of the
lengthSquaredorlengthmethod of vectors is not the same as the return value ofsum.
Tensor.rank
public abstract rank: number;
The rank of a tensor is the number of indices required to uniquely select each element of the tensor.
🛈 The
rankof aTensoris the same as thelengthof itsdimension.
Example:
const scalar = new Scalar(),
vec3 = new Vector3(),
matrix = new Matrix();
scalar.rank // 0, since indicies are not needed
vec3.rank // 1
matrix.rank // 2
Tensor.dimension
public abstract dimension: number[];
dimension is the mathematical dimension [2] of the tensor. In dynamic tensor types, it can be defined as a getter.
Example:
const scalar = new Scalar(),
vec3 = new Vector3(),
vec4 = new Vector4(),
matrix = new Matrix();
scalar.dimension // []
vec3.dimension // [3]
vec4.dimension // [4]
matrix.dimension // [4, 4]
Tensor.value
public get value(): V;
public set value(value: V): void;
value is the values of the tensor in a multidimensional array with the type V (the type parameter of the class). Unlike dimension, this must be implemented as a getter and setter.
Example:
const vec3 = new Vector3(),
matrix = new Matrix();
vec3.value
// [0, 0, 0]
matrix.value
// [ [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ] ]
vec3.value = [ 1, 2, 3 ];
vec3.x // 1
isTensor
function isTensor(value: unknown): value is Tensor;
Checks if a value is a Tensor.
Example:
const vec3 = new Vector3();
isTensor(vec3) // true
isTensor(vec3.value) // false
isTensor([1, 2, 3]) // false
isTensor(0) // false
isTensor(null) // false
isTensorValue
function isTensorValue(value: unknown): value is TensorValue;
Checks if a value is a TensorValue.
Example:
const vec3 = new Vector3();
isTensorValue(vec3) // false
isTensorValue(vec3.value) // true
isTensorValue([1, 2, 3]) // true
isTensorValue(0) // true
isTensorValue(null) // false
getDimension
function getDimension(value: Tensor | TensorValue): number[];
getDimension returns the mathimatical dimension [2] of value, similar to Tensor.dimension. If value is not a Tensor or TensorValue, it will throw a TypeError
🛈
Tensorbased classes can usegetDimension(this.value)for theirdimensionimplementations.
Considerations
- Static methods not pertaining to data transfer and formatting (e.g.
Add,Normalize,Lerp) and non-static normalization methods are outside the scope of this proposal. WhileTensormay include them in the future, this proposal does not - This proposal does not provide for a dimensionally dynamic
Tensor.
Performance
Since Tensor is defined using the declare class and classes that follow Tensor do so using the implements keyword, there are no runtime changes to Tensor-based classes.
Bundle size
The JS bundle size will increase only by the size of the added functions (isTensor, getDimension, etc.). The TS declaration size will also increase due to Tensor.
Questions
-
Should
Scalarbe included in this proposal? As a rank 0 tensor, the benefits of standardizingScalarmay not be worth it, especially considering it is not currently meant to be instantiated. -
How should incompatibility be managed? Should incompatible classes be dropped or should they be modified to follow
Tensor? -
What other classes (e.g.
Size) would be included/standardized?
FAQs
None yet
Revisions
If revisions to this proposal are made, edits will be made to the above proposal with revision numbers attached to revised semantics, questions, considerations, and FAQs. Revisions were made on the below dates:
#0: 23 August 2023