Rで線形代数:アフィン変換

Rで 線形代数:アフィン変換 を試みます。

1. アフィン変換(Affine Transformation)とは

定義

アフィン変換とは、幾何学的な変換の一種で、「線形変換」「平行移動」を組み合わせたものです。 あるベクトル(または点)v を、アフィン変換によって新しい点 v' に移す操作は、以下の数式で表されます。

v' = Mv + t

  • M: 線形変換を表す行列。回転、拡大・縮小、せん断(歪ませる)などを担当します。
  • t: 平行移動を表すベクトル。図形全体を特定の位置にずらす操作を担当します。
  • v: 変換前の点の位置ベクトル。
  • v': 変換後の点の位置ベクトル。

線形変換との違い

行列を掛けるだけの線形変換 (v' = Mv) は、必ず原点 (0,0) を不動点とします。つまり、原点は動きません。

一方、アフィン変換は平行移動 + t を含むため、原点を動かすことができます

これにより、図形を座標空間内の任意の位置に移動させることが可能になり、より自由度の高い変換が実現できます。

アフィン変換の性質

アフィン変換には、変換後も維持されるいくつかの性質があります。

  1. 直線性: 直線は、変換後も直線のままです。(曲線になったりしない)
  2. 点の共線性: 一つの直線の上にある3点は、変換後も一つの直線の上にあります。
  3. 平行性: 平行な直線は、変換後も平行なままです。
  4. 比の維持: 一つの線分を特定の比で内分する点は、変換後も同じ比で線分を内分します。

簡単に言えば、「図形を引き伸ばしたり、回転させたり、ずらしたりはするが、ぐにゃぐにゃに曲げたりはしない変換」とイメージすると分かりやすいです。


2. Rによるシミュレーション

ここでは、一つの三角形に対して、回転、拡大、平行移動を組み合わせた変換を適用してみます。

Rコード

# --------------------------------------------------
# アフィン変換のシミュレーション
# --------------------------------------------------

# 描画用の関数を定義
plot_affine <- function(original_shape, M, t) {
  # Step 1: 線形変換 v_tmp = Mv
  transformed_shape <- t(M %*% t(original_shape))

  # Step 2: 平行移動 v' = v_tmp + t
  final_shape <- sweep(transformed_shape, 2, t, "+")

  # 描画範囲を自動設定
  all_points <- rbind(original_shape, final_shape)
  plot_range <- range(all_points) * 1.2 + c(-1, 1) # 少し余裕を持たせる
  plot(NULL,
    xlim = plot_range, ylim = plot_range,
    xlab = "x", ylab = "y", asp = 1,
    main = "Affine Transformation (Original -> Final)"
  )

  # 軸線
  abline(h = 0, v = 0, lty = 2, col = "grey")

  # 元の図形を描画
  polygon(original_shape, col = "#AAAAFF80", border = "blue")

  # 変換後の図形を描画
  polygon(final_shape, col = "#FFAAAA80", border = "red")

  legend("topleft",
    legend = c("Original Shape", "Final Shape (Transformed & Translated)"),
    fill = c("#AAAAFF80", "#FFAAAA80"),
    border = c("blue", "red"),
    bty = "n"
  )
}

# --- シミュレーションの準備 ---

# 1. 変換対象の図形を定義(三角形の頂点座標)
#    閉じた図形にするため、始点を末尾に再度追加
triangle <- rbind(c(0, 0), c(2, 0), c(1, 2), c(0, 0))

# --- シナリオ1: 回転 + 平行移動 ---

cat("--- シナリオ1: 回転 + 平行移動 Figure 1 ---\n")

# (a) 線形変換行列 M: 45度回転
theta <- pi / 4 # 45度をラジアンに変換
M1 <- matrix(c(cos(theta), sin(theta), -sin(theta), cos(theta)), nrow = 2)
cat("回転行列 M1:\n")
print(M1)

# (b) 平行移動ベクトル t
t1 <- c(3, 1)
cat("\n平行移動ベクトル t1:", t1, "\n\n")

# シミュレーション実行
plot_affine(triangle, M1, t1)


# --- シナリオ2: 拡大・縮小 + せん断 + 平行移動 ---

cat("--- シナリオ2: 複雑な変換 Figure 2 ---\n")

# (a) 線形変換行列 M: x方向に1.5倍、y方向に0.8倍し、さらに歪ませる
M2 <- matrix(c(1.5, 0.5, 0, 0.8), nrow = 2)
cat("変換行列 M2:\n")
print(M2)

# (b) 平行移動ベクトル t
t2 <- c(-4, 2)
cat("\n平行移動ベクトル t2:", t2, "\n\n")

# シミュレーション実行
plot_affine(triangle, M2, t2)
--- シナリオ1: 回転 + 平行移動 Figure 1 ---
回転行列 M1:
          [,1]       [,2]
[1,] 0.7071068 -0.7071068
[2,] 0.7071068  0.7071068

平行移動ベクトル t1: 3 1 

--- シナリオ2: 複雑な変換 Figure 2 ---
変換行列 M2:
     [,1] [,2]
[1,]  1.5  0.0
[2,]  0.5  0.8

平行移動ベクトル t2: -4 2 
Figure 1
Figure 2

実行結果と解説

シナリオ1: 回転 + 平行移動 Figure 1

  • まず、原点にある青い三角形が、行列 M1 によって原点を中心に45度回転されます。
  • 次に、その回転された三角形が、平行移動ベクトル t1 = (3, 1) によって、全体が右に3、上に1だけ平行移動し、最終的な赤い三角形の位置に移ります。
  • 直線性や平行性(この図形には平行な辺はありませんが)が保たれていることがわかります。

シナリオ2: 複雑な変換 Figure 2

  • 青い三角形が行列 M2 によって線形変換されます。M2 は、x方向のスケール変更、y方向のスケール変更、さらにせん断(歪み)を組み合わせた複雑な変換です。
  • その歪んだ図形が、平行移動ベクトル t2 = (-4, 2) によって、左に4、上に2だけ平行移動し、最終的な赤い図形の位置に移ります。
  • このように複雑な変形と移動を組み合わせても、直線が直線のままであるなど、アフィン変換の基本的な性質は維持されています。

以上です。