Plotly.jsで3dグラフを回転させる

3Dグラフがその場で回転するのってかっこいいですよね。

ということで回るようにしました。
グラフにマウスを合わせて、右上にあるベルマークをクリックしてみてください。

<p class="codepen" data-height="656" data-theme-id="dark" data-default-tab="html,result" data-user="ririli" data-slug-hash="oNxeMZd" style="height: 656px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="oNxeMZd">
  <span>See the Pen <a href="https://codepen.io/ririli/pen/oNxeMZd">
  oNxeMZd</a> by ririli (<a href="https://codepen.io/ririli">@ririli</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>



かっこいいね。

では軽く解説しましょう。

グラフ描画ライブラリであるPlotly.jsをcdnから取得します。

```html
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
```


描画するデータ群を定義。
3Dグラフにするのでtypeはscatter3dで。

```js

        var trace1 = {
        x: [1, 2, 3, 4],
        y: [10, 15, 13, 17],
        z: [10, 8, 6, 4],
        mode: "markers",
        type: 'scatter3d',
            marker: {
                size: markerSize,
            },
        };

        var trace2 = {
        x: [1, 2, 3, 4],
        y: [16, 5, 11, 9],
        z: [10, 8, 6, 4],
        mode:"markers",
        type: 'scatter3d',
            marker: {
                size: markerSize,
            }
        };
```


本題です。

```js

       var config = {
            modeBarButtonsToAdd: [
                {
                    name: 'rotate',
                    icon: icon1,
                    click: function(gd) {
                        setInterval( () => {

                            const newData = gd._fullLayout.scene._scene.getCamera()

                            const currentX = newData.eye.x
                            const currentY = newData.eye.y
                            const currentZ = newData.eye.z

                            const newX = (currentX * Math.cos(1*(Math.PI/180))) - (currentY * Math.sin(1*(Math.PI/180)))
                            const newY = (currentX * Math.sin(1*(Math.PI/180))) + (currentY * Math.cos(1*(Math.PI/180)))

                            const update = {
                                x: newX,
                                y: newY,
                                z: currentZ
                            }

                            Plotly.relayout(gd, 'scene.camera.eye', update)
                        }, 10)
                    }
                },
            ]
        }
```

modeBarButtonsToAddはライブラリで定義されているもので、ここに追加したものがグラフにマウスを当てた時に出てくるメニューに追加されます。
なので、回転を開始するボタンを追加した形です。

具体的に回転される方法についてですが、

gd._fullLayout.scene._scene.getCamera()

が現在のcameraの位置を取得しています。
cameraはxyzの座標をもっていて、この座標をsetIntervalで少しずつずらすしていくことでグラフが回転しているように見せます。
実際に回転しているのはグラフではなく観測者側ってことですね。

で、横回転させるのでZ軸方向への変化は必要ありません。
XとYだけ更新します。

値の計算には三角関数を利用。
グラフを原点において円を描くようにxとyを更新します。

具体的な式がこれ。

```
X = xcosθ − ysinθ
Y = xsinθ + ycosθ
```

現在のxとyから角度θ分移動した時のXとYを求めます。
求めたXとYが次の時点のxとyになり、これを繰り返すことで円を描きます。

プログラム的には以下が該当します。

```js
const newX = (currentX * Math.cos(1*(Math.PI/180))) - (currentY * Math.sin(1*(Math.PI/180)))
const newY = (currentX * Math.sin(1*(Math.PI/180))) + (currentY * Math.cos(1*(Math.PI/180)))
```

1と入れている部分がθなので、1度ずつ更新している形です。

setIntervalと組み合わせてみると、「10msecごとに1度回転」する仕組みの出来上がりです。