Vega でクリスマスツリー

FUJITSU アドベントカレンダー２３日目の記事です。

## はじめに

Vega はjsonでデータや描画の設定を記述することで、視覚化できるツールです。[いろんなグラフ](https://vega.github.io/vega/examples/)を表現できますが、今回はグラフでなくクリスマスツリーをVegaで表現してみたいと思います。


### 出来上がったもの

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51316/4377f846-a7ee-fd70-57c7-eb74feebb457.png)


[Online Editor](https://vega.github.io/editor/#/gist/250e714a5b3281780c85ba30d5ad7ba7/tree.json)
[Gist](https://gist.github.com/ihgs/250e714a5b3281780c85ba30d5ad7ba7#file-tree-json)


## 中身

データとしては、「幹」「葉っぱ」「星」「白いライン」「丸い飾り」で構成しています。
それぞれ、抜粋しながらポイントを書いておきます。

### 幹

rect(四角形）を描画してます。

```json
  "data": [
    {"name": "tree", "values": [{"x": 0, "y": 0}]},
  ],
...
  "marks": [
    {
      "type": "rect",
      "from": {"data": "tree"},
      "encode": {
        "update": {
          "x": {"scale": "xscale", "signal": "datum.x - 0.8"},
          "x2": {"scale": "xscale", "signal": "datum.x + 0.8"},
          "y": {"scale": "yscale", "value": 10},
          "y2": {"scale": "yscale", "value": 0},
          "fill": {"value": "#864A2b"}
        }
      }
    },
```

### 葉っぱ

高さ(y)とサイズ（size)を変えて、緑色の三角形を並べて表現しています。

```json
"data": [
{
      "name": "leaves",
      "values": [
        {"x": 0, "y": 15, "size": 18000},
        {"x": 0, "y": 20, "size": 15000},
        {"x": 0, "y": 25, "size": 13000},
        {"x": 0, "y": 30, "size": 10000},
        {"x": 0, "y": 36, "size": 6000}
      ]
 },
...
"marks": [
    {
      "type": "symbol",
      "from": {"data": "leaves"},
      "encode": {
        "update": {
          "x": {"scale": "xscale", "field": "x"},
          "y": {"scale": "yscale", "field": "y"},
          "size": {"field": "size"},
          "shape": {"value": "triangle"},
          "fill": {"value": "green"}
        }
      }
    },
]
```


### 星

中身（x=0)に高さ(y)を調整し、星形を作ります。
星形は、SVG pathで表現しています。

```json
"data": [
    {"name": "star", "values": [{"x": 0, "y": 42, "size": 1000}]
],
...
"marks":[
    {
      "type": "symbol",
      "from": {"data": "star"},
      "encode": {
        "enter": {"fill": {"value": "#939597"}, "stroke": {"value": "#652c90"}},
        "update": {
          "x": {"scale": "xscale", "field": "x"},
          "y": {"scale": "yscale", "field": "y"},
          "size": {"field": "size"},
          "shape": {
            "value": "M0,.5L.6,.8L.5,.1L1,-.3L.3,-.4L0,-1L-.3,-.4L-1,-.3L-.5,.1L-.6,.8L0,.5Z"
          },
          "fill": {"value": "yellow"}
        }
      }
    },
]
```


### 白いライン

layerによって、group化することで、一つのデータセットから複数の斜め上にいく線を表現しています。
本来は、葉のうえだけ描画するのが正しいですが、白色にすることで背景色と同一にしごまかしています。


```json
"data":[
    {
      "name": "lines",
      "values": [
        {"x": -4, "y": 10, "layer": 1},
        {"x": 4, "y": 20, "layer": 1},
        {"x": -4, "y": 20, "layer": 2},
        {"x": 4, "y": 30, "layer": 2},
        {"x": -4, "y": 30, "layer": 3},
        {"x": 4, "y": 40, "layer": 3}
      ]
    },
],
...
"marks": [
    {
      "type": "group",
      "from": {"facet": {"data": "lines", "name": "facet", "groupby": "layer"}},
      "marks": [
        {
          "type": "line",
          "from": {"data": "facet"},
          "encode": {
            "update": {
              "x": {"scale": "xscale", "field": "x"},
              "y": {"scale": "yscale", "field": "y"},
              "strokeWidth": {"value": 5},
              "stroke": {"value": "white"}
            }
          }
        }
      ]
    },
]
```

### 丸い飾り

横の位置(x)と高さ(y)をツリーの上に来るように配置し、飾りの色はxとyを掛けた値をcとして、`pastel`カラーで振り分けるように、color スケールをつくって表現します。


```json
"data":[
    {
      "name": "bolls",
      "values": [
        {"x": -2, "y": 15},
        {"x": 2, "y": 12},
        {"x": -1, "y": 25},
        {"x": 1.2, "y": 20},
        {"x": -0.8, "y": 10},
        {"x": 0.4, "y": 32},
        {"x": -0.6, "y": 38}
      ],
      "transform": [{"type": "formula", "as": "c", "expr": "datum.x * datum.y"}]
    }
],
"scales": [
    {
      "name": "color",
      "domain": {"data": "bolls", "field": "c"},
      "range": {"scheme": "pastel1"}
    }
],
...
"marks": [
    {
      "type": "symbol",
      "from": {"data": "bolls"},
      "encode": {
        "update": {
          "x": {"scale": "xscale", "field": "x"},
          "y": {"scale": "yscale", "field": "y"},
          "size": {"value": 150},
          "fill": {"scale": "color", "field": "c"}
        }
      }
    }
]
```

## おわりに

急遽思いついて試してみましたが思った以上にクリスマスツリーになりました。