行列演算

記号の意味
ベクトル- 矢印付きの文字
長さ- ベクトルの大きさ
単位ベクトル- 長さ1のベクトル
内積- 2つのベクトルから数値を計算
シータ- 角度を表すギリシャ文字
余弦- 角度から比率を求める関数

概要

行列とベクトルの積を計算することで、ベクトルに回転・拡大縮小などの変換を適用する方法を学びます。

行列とベクトルの積

行列でベクトルをどう変換するのか?

前章で行列の基本を学びましたが、実際にどう計算するのでしょうか?

  • 「行列でベクトルをどう変換するのか」
  • 「複数の変換を組み合わせるには」
  • 「単位行列とは何か」

これらを「行列演算」で実現します!

身近な例で考えてみよう

  • 画像編集: 「回転→拡大」を1つの操作にまとめる
  • アニメーション: キーフレーム間の変換を滑らかに補間
  • 3Dグラフィックス: カメラの視点変換

行列とベクトルの積

行列とベクトルの積は、「ベクトルを変換する」計算です。

[abcd][xy]=[ax+bycx+dy]\begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} ax+by \\ cx+dy \end{bmatrix}

計算手順:

  1. 1行目 × ベクトル: ax+byax + by → 新しいx座標
  2. 2行目 × ベクトル: cx+dycx + dy → 新しいy座標

具体例: 2倍拡大

[2002][34]=[2×3+0×40×3+2×4]=[68]\begin{bmatrix} 2 & 0 \\ 0 & 2 \end{bmatrix} \begin{bmatrix} 3 \\ 4 \end{bmatrix} = \begin{bmatrix} 2 \times 3 + 0 \times 4 \\ 0 \times 3 + 2 \times 4 \end{bmatrix} = \begin{bmatrix} 6 \\ 8 \end{bmatrix}

(3,4)(3, 4)(6,8)(6, 8) に変換されました!

単位行列

何も変換しない行列を「単位行列」といいます。

I=[1001]I = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}

単位行列でベクトルを変換しても、元のままです:

[1001][xy]=[1×x+0×y0×x+1×y]=[xy]\begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} 1 \times x + 0 \times y \\ 0 \times x + 1 \times y \end{bmatrix} = \begin{bmatrix} x \\ y \end{bmatrix}

単位行列の役割

単位行列は「何もしない変換」を表し、数学の「1」のような役割を持ちます。

JavaScript
// 変換をリセットしたい時に便利
transform = identityMatrix;  // 初期状態に戻す

行列の積で A × I = A となり、計算の基準として重要です。

実用例

行列とベクトルの積を使って、点や方向を変換できます。

点の回転

JavaScript
// 45度回転
const angle = Math.PI / 4;
const cos = Math.cos(angle);
const sin = Math.sin(angle);

const rotationMatrix = [
  [cos, -sin],
  [sin,  cos]
];

const point = { x: 1, y: 0 };
const rotated = {
  x: cos * point.x + (-sin) * point.y,
  y: sin * point.x + cos * point.y
};
// rotated ≈ (0.707, 0.707)

拡大縮小

JavaScript
// X方向に2倍、Y方向に0.5倍
const scaleMatrix = [
  [2, 0],
  [0, 0.5]
];

const point = { x: 3, y: 4 };
const scaled = {
  x: 2 * point.x + 0 * point.y,  // 6
  y: 0 * point.x + 0.5 * point.y  // 2
};

複数の頂点を一度に変換

JavaScript
// 三角形の頂点
const vertices = [
  { x: 0, y: 1 },
  { x: 1, y: 0 },
  { x: -1, y: 0 }
];

// 回転行列で全頂点を変換
const rotated = vertices.map(v => ({
  x: cos * v.x + (-sin) * v.y,
  y: sin * v.x + cos * v.y
}));

デモ: 行列演算の視覚化

下のデモで、行列がベクトルをどう変換するか確認しましょう!

行列の要素
入力ベクトル
行列とベクトルの積
a
1.00
b
0.00
c
0.00
d
1.00
×
x
2.00
y
1.00
=
x'
2.00
y'
1.00
x' = 1.00 × 2.0 + 0.00 × 1.0 = 2.00
y' = 0.00 × 2.0 + 1.00 × 1.0 = 1.00

JavaScript実装

行列ベクトル積の計算関数

JavaScript
/**
 * 行列とベクトルの積
 */
function multiplyMatrixVector(matrix, vector) {
  const [[a, b], [c, d]] = matrix;
  return {
    x: a * vector.x + b * vector.y,
    y: c * vector.x + d * vector.y
  };
}

// 使用例: 回転
const rotMatrix = [
  [Math.cos(Math.PI/4), -Math.sin(Math.PI/4)],
  [Math.sin(Math.PI/4),  Math.cos(Math.PI/4)]
];
const point = { x: 1, y: 0 };
const rotated = multiplyMatrixVector(rotMatrix, point);
console.log(rotated);  // { x: 0.707, y: 0.707 }

行列クラスの拡張

JavaScript
class Matrix2x2 {
  constructor(a, b, c, d) {
    this.values = [[a, b], [c, d]];
  }
  
  /**
   * ベクトルを変換
   */
  transform(vector) {
    const [[a, b], [c, d]] = this.values;
    return {
      x: a * vector.x + b * vector.y,
      y: c * vector.x + d * vector.y
    };
  }
  
  /**
   * 行列同士の積
   */
  multiply(other) {
    const [[a1, b1], [c1, d1]] = this.values;
    const [[a2, b2], [c2, d2]] = other.values;
    
    return new Matrix2x2(
      a1*a2 + b1*c2, a1*b2 + b1*d2,
      c1*a2 + d1*c2, c1*b2 + d1*d2
    );
  }
  
  /**
   * 単位行列
   */
  static identity() {
    return new Matrix2x2(1, 0, 0, 1);
  }
  
  /**
   * 回転行列
   */
  static rotation(angle) {
    const cos = Math.cos(angle);
    const sin = Math.sin(angle);
    return new Matrix2x2(cos, -sin, sin, cos);
  }
  
  /**
   * 拡大縮小行列
   */
  static scale(sx, sy) {
    return new Matrix2x2(sx, 0, 0, sy);
  }
}

// 使用例: 回転してから拡大
const rotate = Matrix2x2.rotation(Math.PI / 4);
const scale = Matrix2x2.scale(2, 2);
const combined = scale.multiply(rotate);  // 複合変換

const point = { x: 1, y: 0 };
const result = combined.transform(point);
console.log(result);  // 回転→拡大の結果

ゲームでの実装例

JavaScript
// スプライトの変換管理
class Sprite {
  constructor() {
    this.position = { x: 0, y: 0 };
    this.transform = Matrix2x2.identity();
  }
  
  /**
   * 回転
   */
  rotate(angle) {
    const rotation = Matrix2x2.rotation(angle);
    this.transform = this.transform.multiply(rotation);
  }
  
  /**
   * 拡大縮小
   */
  scale(sx, sy) {
    const scale = Matrix2x2.scale(sx, sy);
    this.transform = this.transform.multiply(scale);
  }
  
  /**
   * ローカル座標をワールド座標に変換
   */
  localToWorld(localPoint) {
    const transformed = this.transform.transform(localPoint);
    return {
      x: transformed.x + this.position.x,
      y: transformed.y + this.position.y
    };
  }
  
  /**
   * 描画
   */
  draw(ctx, localVertices) {
    ctx.beginPath();
    localVertices.forEach((vertex, i) => {
      const world = this.localToWorld(vertex);
      if (i === 0) {
        ctx.moveTo(world.x, world.y);
      } else {
        ctx.lineTo(world.x, world.y);
      }
    });
    ctx.closePath();
    ctx.stroke();
  }
}

// 使用例
const sprite = new Sprite();
sprite.position = { x: 100, y: 100 };
sprite.rotate(Math.PI / 4);  // 45度回転
sprite.scale(2, 2);          // 2倍拡大

// 三角形の頂点(ローカル座標)
const triangle = [
  { x: 0, y: 10 },
  { x: 10, y: -10 },
  { x: -10, y: -10 }
];

sprite.draw(ctx, triangle);  // 変換して描画

行列演算の重要性

行列演算により以下が可能になります:

  • 効率的な変換: 複数点を一度に処理
  • 変換の合成: 複数の変換を1つの行列にまとめる
  • 逆変換: 行列の逆行列で元に戻す
  • 階層構造: 親子関係のある変換を管理

次章では、具体的な変換(回転、拡大縮小、せん断)を学びます。