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:
CurrentClass
is a placeholder for a class that implementsTensor
. In fields, it is the current class.Tensor
and its related types will be defined insrc/Maths/tensor.ts
unless 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
lengthSquared
orlength
method 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
rank
of aTensor
is the same as thelength
of 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
🛈
Tensor
based classes can usegetDimension(this.value)
for theirdimension
implementations.
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. WhileTensor
may 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
Scalar
be included in this proposal? As a rank 0 tensor, the benefits of standardizingScalar
may 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