vuejsのグラフサンプルであるレーダーチャートの座標計算を読み解く


[vue.js](http://jp.vuejs.org/)の公式サンプルであるレーダーチャートの仕組みを見ていきます。

sample
[Edit fiddle - JSFiddle](http://jsfiddle.net/yyx990803/bbt0f3nz/light/)

これは`polygon`というsvgの多角形を描画する要素に、各頂点の座標を渡すことで描画しています。正多角形ではなく、各ステータス値によって頂点を変動させます。重要なのはこの頂点の座標の取得の仕方です。ここをこの記事では解説します。

### 前提知識

関数`valueToPoint (value, index, total)`を理解するのに次の2つの知識を把握しておきましょう。この関数がA~Fの各ステータス値の座標を取得する関数です。

#### Array#mapとは？

先にmap関数に把握しておきましょう。これはArrayオブジェクトのメソッドで、引数ににはコールバック関数を渡します。このコールバック関数は配列の要素を1つ受け取ります。そしてこのコールバックの戻り値が新しい配列の要素となります。コールバック関数は要素の数だけ呼び出されます。[MDNの例](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map)わかりやすいので貼っておきます。

```js
// 配列内の単語 (文字列) を複数形にする
function fuzzyPlural(single) {
  var result = single.replace(/o/g, 'e');  
  if( single === 'kangaroo'){
    result += 'se';
  }
  return result; 
}

var words = ["foot", "goose", "moose", "kangaroo"];
console.log(words.map(fuzzyPlural));

// 変換前 : ["foot", "goose", "moose", "kangaroo"]
// 変換後 : ["feet", "geese", "meese", "kangareese"]
```

```js
// 数値の配列を平方根の配列にマッピングする
var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// roots   の内容は [1, 2, 3] となる
// numbers の内容は [1, 4, 9] のまま
```

#### 三角関数

これがグラフを色々な入力値に対応させるための鍵です。これは次のブログがわかりやすいです。

[テーマ「三角関数講座」のブログ記事一覧 機織り職人募集中。 /ウェブリブログ](http://78578203.at.webry.info/theme/7addd6aad5.html)

これを読んだ後にQiitaのまとめを見るといいかもしれません。今回はアークタンジェントは必要ありません。[回転移動の１次変換](http://www.geisya.or.jp/%7Emwm48961/kou2/linear_image3.html)を使います。

[Swift - ゲーム作るために三角関数を勉強 - Qiita](http://qiita.com/kimi_dropc/items/edce3911b74d8bd324da)

### svg要素と円の中心座標はずれている

変数`x, y`は円の中心からの座標になります。`x = 0, y = 0`なら円の中心です。このまま6点のx, yの座標を結べばステータスを表した多角形が完成するでしょう。しかし、これを描画するのはsvgという要素であり、svgは座標の計算が違います。`x = 0, y = 0`だと一番左上になります。この2つは座標の始まりが違うため、円を中心とした座標から左上を中心とした座標に変換する必要があります。

ちなみに変数`y`の求め方ですが、これは0.8というのは`value = 1`を座標で表すとどれくらいかということです。valueが1増えるごとにy座標が0.8増加することになります。これは半径が80の円で、valueが100の場合にy座標を100とそのまま決めてしまうと半径を超えてしまいますよね。なので80（半径）/ 100(valueの最大値)で1目盛りあたりのvalue値を算出しています。ちなみに、value値をマイナスにしているのは、y座標の増加方向をsvg要素を同じにするためです。

### 座標の回転移動
![vuejs_radar_chart_01_150927.jpg](https://qiita-image-store.s3.amazonaws.com/0/62881/d57c01da-790c-a321-df5f-eac7db4f7bb3.jpeg "vuejs_radar_chart_01_150927.jpg")


ここから座標の変換に入ります。上記の画像をみてください。SVG要素を上下左右逆さまにしたものです。紫の点は円の中心を(0, 0)とした場合の座標です。今回はx値は常に0で変動しません。変動するのはyだけです。このyはステータス値、valueの量を表しています。まずAの地点でvalueの量を表し、それを特定の項目まで回転させます。`x * cos - y * sin`と`x * sin + y * cos`は回転後の座標を求める公式です。詳しくは下記の説明1をみてください。

[回転移動の１次変換](http://www.geisya.or.jp/%7Emwm48961/kou2/linear_image3.html)

そして回転後の座標というのは円の中心を(0, 0)とした場合です。これをsvgの右下を(0, 0)とした基準に合わせるために100を足しています。この100はsvg要素が200 * 200の正方形の中心の座標(100, 100)を表しています。既にx, yの増加方向は2つの基準とも合わせているので、後は中心座標のずれ(100, 100)を足し合わせる事によって座標の変換を行っているわけです。

出力値を詳しく見たい方は、`valueToPoint()`を次のとおりに書き換えて試してみてください。

```js
function valueToPoint (value, index, total) {
  var x     = 0
  var y     = -value * 0.8
  var angle = Math.PI * 2 / total * index
  var cos   = Math.cos(angle)
  var sin   = Math.sin(angle)
  var tx    = x * cos - y * sin + 100
  var ty    = x * sin + y * cos + 100
  console.log("value="+value+", index="+index+", total="+total);
  console.log("x="+x+", y="+y);
  console.log("angle="+angle+", cos="+cos+", sin="+sin);
  console.log("x * cos = "+(x * cos)+ " y * sin ="+(y*sin));
  console.log("tx="+tx+", ty="+ty);
  console.log("---");
  return {
    x: tx,
    y: ty
  }
}
```