自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

矩陣分解:Pixijs 中的 Matrix 和 Transform

開發(fā) 前端
矩陣的優(yōu)點(diǎn)是計算方便,比如父節(jié)點(diǎn)和子節(jié)點(diǎn)都有 matrix,那子節(jié)點(diǎn)最終在畫布的 matrix 就是它們的矩陣直接相乘。缺點(diǎn)也明顯,就是它的值是幾個多種矩陣變換得到的數(shù)字,語義糟糕,看不出圖形做了什么形變。

大家好,我是前端西瓜哥。

在二維中,對于圖形(模型),它會有一個模型矩陣 matrix 來表達(dá)圖形的形變。

比如圖形先做了縮放,然后再位移,則模型矩陣為縮放矩陣左乘位移矩陣得到的復(fù)合矩陣。

矩陣的優(yōu)點(diǎn)是計算方便,比如父節(jié)點(diǎn)和子節(jié)點(diǎn)都有 matrix,那子節(jié)點(diǎn)最終在畫布的 matrix 就是它們的矩陣直接相乘。

缺點(diǎn)也明顯,就是它的值是幾個多種矩陣變換得到的數(shù)字,語義糟糕,看不出圖形做了什么形變。

這不利于我們對圖形的表達(dá)。

那么,有沒有辦法對矩陣做分解,得到多個形變的表達(dá)呢?

我們不妨看看 pixijs 怎么做的。

pixijs 里面有兩個類:Matrix 和 Transform。

Matrix

Matrix 是平面矩陣類,提供矩陣相關(guān)的各種方法。

Matrix 使用 6 個數(shù)字表達(dá),代表一個 3x3 的矩陣,用于平面矩陣變換。值有 a、b、c、d、tx、ty。

| a | c | tx|
| b | d | ty|
| 0 | 0 | 1 |

支持縮放、旋轉(zhuǎn)、位移、左乘、右乘、逆矩陣、計算點(diǎn)應(yīng)用矩陣后的結(jié)果等方法。支持鏈?zhǔn)綄懛ā?/p>

縮放、旋轉(zhuǎn)、位移:

import { Matrix } from 'pixi.js';

const matrix = new Matrix();
// 返回一個默認(rèn)額單位矩陣
// [pixi.js:Matrix a=1 b=0 c=0 d=1 tx=0 ty=0]
// 1, 0, 0,
// 0, 1, 0,
// 0, 0, 1,

matrix.scale(3, 3);
// 放大為原來的 3 倍
// [pixi.js:Matrix a=3 b=0 c=0 d=3 tx=0 ty=0]
// 3, 0, 0,
// 0, 3, 0,
// 0, 0, 1,

// 支持鏈?zhǔn)綄懛ǎǖ葍r連續(xù)多個變換矩陣左乘)
matrix.rotate(Math.PI / 2).translate(10, 10);
// [pixi.js:Matrix a=1.8369701987210297e-16 b=3 c=-3 d=1.8369701987210297e-16 tx=10 ty=10]
// 上面這個 a 應(yīng)該為 0,但因?yàn)楦↑c(diǎn)數(shù)誤差導(dǎo)致一個非常小的小數(shù)。

左乘、右乘、逆矩陣:

const leftMatrix = new Matrix();
const rightMatrix = new Matrix();

// 右乘
const newMatrix = leftMatrix.append(rightMatrix);

// 左乘
const newMatrix2 = rightMatrix.prepend(leftMatrix);

// 逆矩陣
const inverseMatrix = leftMatrix.invert();

計算點(diǎn)應(yīng)用矩陣后的結(jié)果、應(yīng)用逆矩陣的結(jié)果:

const matrix = new Matrix();

// 點(diǎn)應(yīng)用矩陣后的結(jié)果
const point = matrix.apply({ x: 100, y: 100 });

// 應(yīng)用逆矩陣的結(jié)果
const inversePoint = matrix.applyInverse({ x: 100, y: 100 });

Transform

Transform 是 Matrix 的等價表達(dá),但是對用戶友好。

The Transform class facilitates the manipulation of a 2D transformation matrix through user-friendly properties: position, scale, rotation, skew, and pivot.

Transform 是一些屬性的組合,可以表達(dá)一個圖形的任意形變效果。

屬性有:

  1. postion:位置,類型為 point { x: number, y: number }。
  2. scale:縮放,類型為 point。
  3. pivot:基準(zhǔn)位置,類型為 point。它作為旋轉(zhuǎn)、縮放的中心點(diǎn),默認(rèn)為原點(diǎn)。
  4. skew:斜切,類型為 point。弧度值,表示基向量方向和另一方向形成的角。
  5. rotation:旋轉(zhuǎn)角,弧度單位。

用 typescript 類型表達(dá)為:

interface TransformableObject {
  position: PointData;
  scale: PointData;
  pivot: PointData;
  skew: PointData;
  rotation: number;
}

interface PointData {
  x: number;
  y: number;
}

pixijs 的圖形使用了 Transform 的這一套表達(dá),讓用戶能夠很簡單直觀地表達(dá)一些簡單的形變。

Transform 下有一個 _matrix 屬性,維護(hù)等價的 matrix 對象,當(dāng) transform 的屬性更新時,matrix 會標(biāo)記為 dirty,之后讀取的時候會重新生成。

transform 這個名字其實(shí)有點(diǎn)迷惑,因?yàn)橛袝r候我們也會把用在形變的矩陣 matrix,也叫做 transform。只是 pixijs 這里的命名比較特別,里面也有點(diǎn)亂。

下面看看 Matrix 和 Transform 之間的轉(zhuǎn)換算法。

Transform 轉(zhuǎn) Matrix

pixijs 中 Transform 轉(zhuǎn) Matrix 的實(shí)現(xiàn)如下。

class Transform {
    /**
     * This matrix is computed by combining this Transforms position, scale, rotation, skew, and pivot
     * properties into a single matrix.
     * @readonly
     */
    get matrix(): Matrix
    {
        const lt = this._matrix;

        if (!this.dirty) return lt;

        lt.a = this._cx * this.scale.x;
        lt.b = this._sx * this.scale.x;
        lt.c = this._cy * this.scale.y;
        lt.d = this._sy * this.scale.y;

        lt.tx = this.position.x - ((this.pivot.x * lt.a) + (this.pivot.y * lt.c));
        lt.ty = this.position.y - ((this.pivot.x * lt.b) + (this.pivot.y * lt.d));

        this.dirty = false;

        return lt;
    }
  
    /** Called when the skew or the rotation changes. */
    protected updateSkew(): void
    {
        this._cx = Math.cos(this._rotation + this.skew.y);
        this._sx = Math.sin(this._rotation + this.skew.y);
        this._cy = -Math.sin(this._rotation - this.skew.x); // cos, added PI/2
        this._sy = Math.cos(this._rotation - this.skew.x); // sin, added PI/2

        this.dirty = true;
    }
}

_cx、_sx、_cy、sy 會在更新 skew 或 rotataion 時進(jìn)行更新,是緩存數(shù)據(jù)。

我們抽出算法。

上面為了提高計算效率,沒有用矩陣類的方法,這里給矩陣相乘表達(dá)。

import { Matrix } from 'pixi.js';

const transformToMatrix = (tf: TransformableObject) => {
  const cosX = Math.cos(tf.rotation + tf.skew.y);
  const sinX = Math.sin(tf.rotation + tf.skew.y);
  const cosY = -Math.sin(tf.rotation - tf.skew.x);
  const sinY = Math.cos(tf.rotation - tf.skew.x);

  const skewMatrix = new Matrix(cosX, sinX, cosY, sinY, 0, 0);

  return new Matrix()
    .translate(-tf.pivot.x, -tf.pivot.y)
    .prepend(skewMatrix)
    .scale(tf.scale.x, tf.scale.y)
    .translate(tf.position.x, tf.position.y);
};

斜切和旋轉(zhuǎn)二者需要合并為一個斜切矩陣。因?yàn)樾D(zhuǎn)本質(zhì)是一種斜切,只是剛好兩個斜切角的和為 360 度的倍數(shù)。

所以這里要把 skew 和 rotation 加起來,計算一個斜切矩陣。

結(jié)果矩陣為下面幾個矩陣連續(xù)左乘:

  1. pivot 負(fù)方向的位移矩陣。表示圖形上的某個點(diǎn),移動到坐標(biāo)原點(diǎn)。pivot 可以理解為前置版位移。
  2. skew 和 rotation 得到的斜切矩陣。
  3. scale 對應(yīng)的縮放矩陣。
  4. position 對應(yīng)的位移矩陣。

Matrix 轉(zhuǎn) Transform

pixi.js 的實(shí)現(xiàn)為:

class Matrix {
      /**
     * Decomposes the matrix (x, y, scaleX, scaleY, and rotation) and sets the properties on to a transform.
     * @param transform - The transform to apply the properties to.
     * @returns The transform with the newly applied properties
     */
    public decompose(transform: TransformableObject): TransformableObject
    {
        // sort out rotation / skew..
        const a = this.a;
        const b = this.b;
        const c = this.c;
        const d = this.d;
        const pivot = transform.pivot;

        const skewX = -Math.atan2(-c, d);
        const skewY = Math.atan2(b, a);

        const delta = Math.abs(skewX + skewY);

        if (delta < 0.00001 || Math.abs(PI_2 - delta) < 0.00001)
        {
            transform.rotation = skewY;
            transform.skew.x = transform.skew.y = 0;
        }
        else
        {
            transform.rotation = 0;
            transform.skew.x = skewX;
            transform.skew.y = skewY;
        }

        // next set scale
        transform.scale.x = Math.sqrt((a * a) + (b * b));
        transform.scale.y = Math.sqrt((c * c) + (d * d));

        // next set position
        transform.position.x = this.tx + ((pivot.x * a) + (pivot.y * c));
        transform.position.y = this.ty + ((pivot.x * b) + (pivot.y * d));

        return transform;
    }
}

上面這個是 matrix 對象的方法,接收一個 transform 對象,修改它的值,并返回它自身。

pivot 這個就直接取傳入的 transform 的 pivot。

計算斜切值 skew。即求圖形兩條相鄰邊各自的余弦值對應(yīng)的角。

如果剛好兩個斜切角之和為 0 或 360 度,說明是特殊的斜切——旋轉(zhuǎn),那就給 rotation 設(shè)置為 skewY。skew 設(shè)置為 0。

如果不是,rotation 設(shè)置為 0,skew 設(shè)置為斜切角。

scale 分別為 a 和 b、c 和 d 的平方和開方。

最后是 position,理論上直接取 tx 和 ty 即可,不過有個 pivot。pivot 是圖形斜切縮放前的前置位移,所以給它應(yīng)用去掉 tx 和 ty 的矩陣做一個運(yùn)算,然后再加上 tx 和 ty 即可。

結(jié)尾

矩陣 matrix 體現(xiàn)了數(shù)學(xué)的簡潔之美,只用幾個數(shù)字,就能表達(dá)圖形的各種變換的組合。

但問題是可讀性差,無法直接看出圖形的特性,比如旋轉(zhuǎn)了多少,縮放了多少。

為了提高易用性,pixijs 引入了一套和 matrix 等價的 transform,讓開發(fā)者使用圖形時,能夠快速上手,很好地解決了 Matrix 的弊端。

責(zé)任編輯:姜華 來源: 前端西瓜哥
相關(guān)推薦

2023-02-15 09:00:00

算法推薦系統(tǒng)矩陣分解算法

2017-02-08 09:25:16

Spark分解推薦

2014-07-04 10:05:57

機(jī)器學(xué)習(xí)

2023-01-08 23:06:14

css3d變換

2014-07-15 09:36:55

機(jī)器學(xué)習(xí)

2017-07-06 08:36:10

特征向量矩陣PCA

2022-10-24 15:56:55

PythonPyTorchGPU 計算

2025-01-24 08:34:29

pixijs圖形設(shè)置光標(biāo)cursor

2021-06-24 08:30:00

人工智能數(shù)據(jù)計算

2023-02-08 17:04:14

Python計算庫數(shù)學(xué)函數(shù)

2025-01-14 14:04:45

2023-10-09 07:49:33

PixiJSWebGL

2021-07-14 06:40:02

矩陣路徑字符串

2023-10-13 07:29:23

PixiJSRunner

2023-02-28 07:28:50

Spritepixijs

2023-06-08 08:16:33

TickerPixiJS

2023-06-07 08:13:46

PixiJSCanvas 庫

2023-12-11 07:52:19

圖像處理矩陣計算計算機(jī)視覺

2023-02-22 09:27:31

CanvasWebGL

2010-09-02 14:53:19

DHCP Relay
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號