import { ArgumentError } from '../errors/ArgumentError';
import { Point } from './Point';
import { Vector3D } from './Vector3D';
/**
* The Matrix export class represents a transformation matrix that determines how to
* map points from one coordinate space to another. You can perform various
* graphical transformations on a display object by setting the properties of
* a Matrix object, applying that Matrix object to the matrix
* property of a Transform object, and then applying that Transform object as
* the transform property of the display object. These
* transformation functions include translation(x and y
* repositioning), rotation, scaling, and skewing.
*
*
Together these types of transformations are known as affine * transformations. Affine transformations preserve the straightness of * lines while transforming, so that parallel lines stay parallel.
* *To apply a transformation matrix to a display object, you create a
* Transform object, set its matrix property to the
* transformation matrix, and then set the transform property of
* the display object to the Transform object. Matrix objects are also used as
* parameters of some methods, such as the following:
draw() method of a BitmapData objectbeginBitmapFill() method,
* beginGradientFill() method, or
* lineGradientStyle() method of a Graphics objectA transformation matrix object is a 3 x 3 matrix with the following * contents:
* *In traditional transformation matrixes, the u,
* v, and w properties provide extra capabilities.
* The Matrix export class can only operate in two-dimensional space, so it always
* assumes that the property values u and v are 0.0,
* and that the property value w is 1.0. The effective values of
* the matrix are as follows:
You can get and set the values of all six of the other properties in a
* Matrix object: a, b, c,
* d, tx, and ty.
The Matrix export class supports the four major types of transformations: * translation, scaling, rotation, and skewing. You can set three of these * transformations by using specialized methods, as described in the following * table:
* *Each transformation function alters the current matrix properties so
* that you can effectively combine multiple transformations. To do this, you
* call more than one transformation function before applying the matrix to
* its display object target(by using the transform property of
* that display object).
Use the new Matrix() constructor to create a Matrix object
* before you can call the methods of the Matrix object.
If you do not provide any parameters to the new Matrix()
* constructor, it creates an identity matrix with the following
* values:
In matrix notation, the identity matrix looks like this:
* * @param a The value that affects the positioning of pixels along the * x axis when scaling or rotating an image. * @param b The value that affects the positioning of pixels along the * y axis when rotating or skewing an image. * @param c The value that affects the positioning of pixels along the * x axis when rotating or skewing an image. * @param d The value that affects the positioning of pixels along the * y axis when scaling or rotating an image.. * @param tx The distance by which to translate each point along the x * axis. * @param ty The distance by which to translate each point along the y * axis. */ constructor(rawData?: Float32Array); constructor(a?: number, b?: number, c?: number, d?: number, tx?: number, ty?: number); constructor(a: number | Float32Array = 1, b: number = 0, c: number = 0, d: number = 1, tx: number = 0, ty: number = 0) { if (a instanceof Float32Array) { this.copyRawDataFrom(a); } else { const raw: Float32Array = this.rawData; raw[0] = Number(a); raw[1] = b; raw[2] = c; raw[3] = d; raw[4] = tx; raw[5] = ty; } } public copyRawDataFrom(vector: Float32Array, offset: number = 0): void { const raw: Float32Array = this.rawData; raw[0] = vector[offset + 0]; raw[1] = vector[offset + 1]; raw[2] = vector[offset + 2]; raw[3] = vector[offset + 3]; raw[4] = vector[offset + 4]; raw[5] = vector[offset + 5]; } /** * Returns a new Matrix object that is a clone of this matrix, with an exact * copy of the contained object. * * @return A Matrix object. */ public clone(): Matrix { const raw: Float32Array = this.rawData; return new Matrix(raw[0], raw[1], raw[2], raw[3], raw[4], raw[5]); } /** * Concatenates a matrix with the current matrix, effectively combining the * geometric effects of the two. In mathematical terms, concatenating two * matrixes is the same as combining them using matrix multiplication. * *For example, if matrix m1 scales an object by a factor of
* four, and matrix m2 rotates an object by 1.5707963267949
* radians(Math.PI/2), then m1.concat(m2)
* transforms m1 into a matrix that scales an object by a factor
* of four and rotates the object by Math.PI/2 radians.
This method replaces the source matrix with the concatenated matrix. If
* you want to concatenate two matrixes without altering either of the two
* source matrixes, first copy the source matrix by using the
* clone() method, as shown in the Class Examples section.
Using the createBox() method lets you obtain the same
* matrix as you would if you applied the identity(),
* rotate(), scale(), and translate()
* methods in succession. For example, mat1.createBox(2,2,Math.PI/4,
* 100, 100) has the same effect as the following:
beginGradientFill() and lineGradientStyle()
* methods of the Graphics class. Width and height are scaled to a
* scaleX/scaleY pair and the
* tx/ty values are offset by half the width and
* height.
*
* For example, consider a gradient with the following * characteristics:
* *GradientType.LINEAR[0,
* 255]SpreadMethod.PADInterpolationMethod.LINEAR_RGBThe following illustrations show gradients in which the matrix was
* defined using the createGradientBox() method with different
* parameter settings:
width parameter.
* @param ty The distance, in pixels, to translate down along the
* y axis. This value is offset by half of the
* height parameter.
*/
public createGradientBox(width: number,
height: number,
rotation: number = 0,
tx: number = 0, ty:
number = 0): void {
this.createBox(width / 1638.4, height / 1638.4, rotation, tx + width / 2, ty + height / 2);
}
/**
* Given a point in the pretransform coordinate space, returns the
* coordinates of that point after the transformation occurs. Unlike the
* standard transformation applied using the transformPoint()
* method, the deltaTransformPoint() method's transformation
* does not consider the translation parameters tx and
* ty.
*
* @param point The point for which you want to get the result of the matrix
* transformation.
* @return The point resulting from applying the matrix transformation.
*/
public deltaTransformPoint(point: Point): Point {
const raw: Float32Array = this.rawData;
return new Point(point.x * raw[0] + point.y * raw[2], point.x * raw[1] + point.y * raw[3]);
}
/**
* Sets each matrix property to a value that causes a null transformation. An
* object transformed by applying an identity matrix will be identical to the
* original.
*
* After calling the identity() method, the resulting matrix
* has the following properties: a=1, b=0,
* c=0, d=1, tx=0,
* ty=0.
In matrix notation, the identity matrix looks like this:
* */ public identity(): void { const raw: Float32Array = this.rawData; raw[0] = 1; raw[1] = 0; raw[2] = 0; raw[3] = 1; raw[4] = 0; raw[5] = 0; } /** * Performs the opposite transformation of the original matrix. You can apply * an inverted matrix to an object to undo the transformation performed when * applying the original matrix. */ public invert(): void { const raw = this.rawData; let b = raw[1]; let c = raw[2]; const tx = raw[4]; const ty = raw[5]; if (b === 0 && c === 0) { const a = raw[0] = 1 / raw[0]; const d = raw[3] = 1 / raw[3]; raw[1] = raw[2] = 0; raw[4] = -a * tx; raw[5] = -d * ty; return; } const a = raw[0]; let d = raw[3]; let determinant = a * d - b * c; if (determinant === 0) { this.identity(); return; } /** * Multiplying by reciprocal of the |determinant| is only accurate if the reciprocal is * representable without loss of precision. This is usually only the case for powers of * two: 1/2, 1/4 ... */ determinant = 1 / determinant; let k = 0; k = raw[0] = d * determinant; b = raw[1] = -b * determinant; c = raw[2] = -c * determinant; d = raw[3] = a * determinant; raw[4] = -(k * tx + c * ty); raw[5] = -(b * tx + d * ty); } /** * Returns a new Matrix object that is a clone of this matrix, with an exact * copy of the contained object. * * @param matrix The matrix for which you want to get the result of the matrix * transformation. * @return A Matrix object. */ public multiply(matrix: Matrix): Matrix { const result = new Matrix(); result.a = this.a * matrix.a + this.b * matrix.c; result.b = this.a * matrix.b + this.b * matrix.d; result.c = this.c * matrix.a + this.d * matrix.c; result.d = this.c * matrix.b + this.d * matrix.d; result.tx = this.tx * matrix.a + this.ty * matrix.c + matrix.tx; result.ty = this.tx * matrix.b + this.ty * matrix.d + matrix.ty; return result; } /** * Applies a rotation transformation to the Matrix object. * *The rotate() method alters the a,
* b, c, and d properties of the
* Matrix object. In matrix notation, this is the same as concatenating the
* current matrix with the following:
sx, and the y axis it is multiplied by
* sy.
*
* The scale() method alters the a and
* d properties of the Matrix object. In matrix notation, this
* is the same as concatenating the current matrix with the following
* matrix:
a, b, c,
* d, tx, and ty.
*/
public toString(): string {
return '[Matrix] (a=' + this.a
+ ', b=' + this.b
+ ', c=' + this.c
+ ', d=' + this.d
+ ', tx=' + this.tx
+ ', ty=' + this.ty + ')';
}
/**
* Returns the result of applying the geometric transformation represented by
* the Matrix object to the specified point.
*
* @param point The point for which you want to get the result of the Matrix
* transformation.
* @return The point resulting from applying the Matrix transformation.
*/
public transformPoint(point: Point): Point {
const raw: Float32Array = this.rawData;
return new Point(point.x * raw[0] + point.y * raw[2] + raw[4], point.x * raw[1] + point.y * raw[3] + raw[5]);
}
/**
* Translates the matrix along the x and y axes, as specified
* by the dx and dy parameters.
*
* @param dx The amount of movement along the x axis to the right, in
* pixels.
* @param dy The amount of movement down along the y axis, in pixels.
*/
public translate(dx: number, dy: number): void {
this.rawData[4] += dx;
this.rawData[5] += dy;
}
}