JS奮闘記【JavaScriptとcanvasと線形変換 ~回転編~】

JS奮闘記 canvasと線形変換

こんにちは。エンジニアの辻です。

普段はフロントエンドの構築を担当している私ですが、1つ悩みがあります。
それはcanvasが苦手な事です。

canvasの何が難しいって、デバックしづらい事もさることながら、数学や物理学の知識がほぼ必須なところです。
学生時代が終わってはや数年。数学や物理学なんてもう完全に忘却の彼方です。

そこでcanvasへの苦手意識を矯正することも兼ねて、今回から不定期でcanvasを中心とした連載をはじめていきたいと思います。
記念すべき初回は、canvas上で線形変換を使ってボックスを回転させてみます。

線形変換とは?

線形変換とは『平面上の(x, y)が変換fによって、(x’, y’)に移動する。この時の関係式を以下のように定数項を含まない一次式で表せる場合、この移動を線形変換(1次変換)という』だそうです。
数式で表すと以下のようになります。

線形変換の基本式

上記の式を計算してみると以下のようになります。
結果としてベクトルを得ることができます。

線形変換(計算後)

高校数学の行列の分野で出てきたヤツですね! なんとも懐かしいです!
今回は線形変換の中でも、サイン、コサインを使って反時計回りに移動させるものを使って、canvas上でボックスを動かしてみたいと思います。
反時計回りに移動させる線形変換の式はコチラです。

線形変換(回転)

上記の式をJavaScriptで表すと下記のようになります。

線形変換についてもっと知りたいという方はwikipediaをご覧ください。(丸投げ)
線型写像 線形変換行列の例 – wikipedia

canvas上で線形変換を使ってボックスを回転させてみる

今回の成果物

まず先に今回の成果物のスクリーンショットをどうぞ。

 

canvas上で線形変換した画面

 

上図では画面中央を原点として画面いっぱいにcanvasを配置しています。

赤ボックスが初期配置のオブジェクトです。
原点からx軸に75、y軸に75の位置に配置しました。
赤ボックスはxとyの値が同じなので、原点とのなす角度は45°ですね。

この赤ボックスを線形変換を使って、反時計周りに45°移動させたのが青ボックス。
また、赤ボックスを反時計周りに135°移動させたのが緑ボックスです。
赤ボックスは原点から45°の位置にいるので、青ボックスの角度は90°。緑ボックスは180°の位置にいます。
図解すると以下のようになります。

 

canvas上で線形変換した画面とその解説

 

ソースコードの解説

ソースコードは以下の通りです。構成自体はシンプルです。
index.htmlにcanvasの処理を書いたmain.jsを読み込んでいます。それだけです。

肝となるmain.jsの全文は以下の通りです。

main.jsの処理の流れは、おおまかにこんな感じです。

  1. ページが読み込まれた後にinitを実行して、canvasを取得する
  2. canvasの原点を画面中央にし、y軸のプラス・マイナスを反転する
  3. x軸とy軸を描画する
  4. 線形変換前の点オブジェクトを用意して、赤ボックスを描画する
  5. 赤ボックスを角度45°で線形変換した青ボックスを描画する
  6. 赤ボックスを角度135°で線形変換した緑ボックスを描画する

では、main.jsの内容をピックアップして解説していきますね。

canvasの原点を画面中央に移動する

canvasを初期化した後に、translate()とscale()を使って、canvasの原点を画面中央へ移動しています。

canvasは特に何も設定しなければ、画面左上が原点となります。
そして、x軸は画面右方向へ行けばプラス。y軸は画面下方向へ行けばプラスになります。
この状態を数学でよく見る2次元平面図に変換するために、translate()とscale()を使っています。

まずtranslate()を使って、canvasを画面横幅の半分だけ水平方向右向きへ移動し、画面縦幅の半分だけ垂直方向下向きへ移動させます。
これで画面中央を原点に設定する事ができます。

次に、scale()を使ってy軸のプラス・マイナスを逆転させます。
scale()は要素を拡大・縮小する時に使いますが、引数にマイナスを入れることで要素を反転した上で拡大・縮小を行うことができます。
今回は第2引数に-1を入れているので、x軸に対して上下が反転して1倍の大きさとなります。
つまり、y軸のプラス・マイナスが反転した状態になります。

ちなみに、context.scale(-1, 1)とすると、x軸のプラス・マイナスが反転します。

原点と軸を描画

初期化した後は、原点と軸を描画しています。
画面中央に移動したので、原点はx = 0、y = 0 で描画できます。
x軸とy軸はそれぞれ画面の端から端まで線を描画しているだけですね。

度(°)をラジアンに変換する

線形変換の処理を見ていく前に、度(°)をラジアン(rad)に変換する処理を見ていきます。

ラジアンとは角度を表す単位であり、円の半径と同じ長さの弧を切り取った時の円の中央の角が1ラジアンです。
半径が1の円を考えてみましょう。円周は直径 * πなので、2πになります。
したがって、円一周分(360°)のラジアンは2π[rad]となり、半円(180°)のラジアンはπ[rad]となります。

上記の関数は度を引数として受け取り、この180 : π[rad]の比率を使ってラジアンに変換して、結果を返しています。

線形変換で出てくるサインとコサインは、JavaScriptではMath.cos()、Math.sin()で取得できるのですが、引数にラジアンが必要になります。
なので、度からラジアンへの変換処理が必要になるわけですね。

Math.cos()や、Math.sin()、ラジアンを詳しく知りたい方は以下をご覧ください。

線形変換(回転)を行う

さて、ようやく本命の登場です。
上記は線形変換(回転)を行う処理です。

まず引数として、変換対象のx、yを持ったオブジェクト(target)と、回転させる角度(degree)を受け取っています。
(※引数で受け取っているdegreeはラジアンではなく度です。)

次に受け取った度をラジアンに変換します。
radian = convertToRadian(degree)のところですね。
そして、ラジアンを変換行列のMath.cos()や、Math.sin()にいれて線形変換を行います。

2×2の行列と2×1の行列の積なので、最終的に2×1の行列(ベクトル)が算出されます。
この計算結果のxとyが、角度分だけ移動した座標となります。
あとは、計算結果を元にボックスを描画するだけですね。

まとめ

今回はシンプルな線形変換を使って、canvas上のボックスを反時計回りに回転させて移動させてみました。
こうしてcanvasを構築してみると、JavaScriptの練習にもなりますし、数学の勉強もできて一石二鳥ですね。
次回もcanvasを触っていきます!

よろしければ、次の記事「JavaScriptとcanvasと線形変換 その2 ~任意の点を中心にした回転編~」もご覧ください。



❏❏ TOPIC ❏❏ ------------------------------------------------------------

カスタム自由!フリーECサイトパッケージ
チャットボット導入サービス
WEBシステム開発・スマホアプリ開発はSRIAへ