行列演算
記号の意味
- ベクトル- 矢印付きの文字
- 長さ- ベクトルの大きさ
- 単位ベクトル- 長さ1のベクトル
- 内積- 2つのベクトルから数値を計算
- シータ- 角度を表すギリシャ文字
- 余弦- 角度から比率を求める関数
概要
行列とベクトルの積を計算することで、ベクトルに回転・拡大縮小などの変換を適用する方法を学びます。
行列とベクトルの積
行列でベクトルをどう変換するのか?
前章で行列の基本を学びましたが、実際にどう計算するのでしょうか?
- 「行列でベクトルをどう変換するのか」
- 「複数の変換を組み合わせるには」
- 「単位行列とは何か」
これらを「行列演算」で実現します!
身近な例で考えてみよう
- 画像編集: 「回転→拡大」を1つの操作にまとめる
- アニメーション: キーフレーム間の変換を滑らかに補間
- 3Dグラフィックス: カメラの視点変換
行列とベクトルの積
行列とベクトルの積は、「ベクトルを変換する」計算です。
計算手順:
- 1行目 × ベクトル: → 新しいx座標
- 2行目 × ベクトル: → 新しいy座標
具体例: 2倍拡大
が に変換されました!
単位行列
何も変換しない行列を「単位行列」といいます。
単位行列でベクトルを変換しても、元のままです:
単位行列の役割
単位行列は「何もしない変換」を表し、数学の「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.00b
0.00c
0.00d
1.00x
2.00y
1.00x'
2.00y'
1.00x' = 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つの行列にまとめる
- 逆変換: 行列の逆行列で元に戻す
- 階層構造: 親子関係のある変換を管理
次章では、具体的な変換(回転、拡大縮小、せん断)を学びます。