sin関数を動的に描写する
続いては、もう少し複雑なデータを動的に変更する方法を紹介します。
具体的には、角度が動的に変わり図3の線が左へ移動し続けるようなsin関数のグラフを動的に描画してみます。これは、先ほどの回転している様子でX軸を時間(角度)に、Y軸をsinの値にしたグラフになります。
つまり、等速回転運動を表した波形とも言えます。例えば、先ほどのスイングの例で言えば回転が等速にはならないため、それが波長として見えるようになるはずです。
また、前回までと今回が異なるのは、X軸(度数)が1度ずつ増加するため、軸も同時に移動する点です。
これを実装したのが、リスト3のコードです。
export default class DynamicController{
drawGrid(ele){
const rect = ele.getBoundingClientRect();
const width = rect.width;
const height = rect.height;
const marginTop = 30;
const marginRight = 30;
const marginBottom = 30;
const marginLeft = 30;
const xDomain = [-180, 360];
const yDomain = [-2, 2];
// (1) X軸を作成(時間と共に動く前提)
this.scaleX = d3.scaleLinear()
.domain(xDomain)
.range([marginLeft, width - marginRight]);
// (2) Y軸を作成(固定)
const scaleY = d3.scaleLinear()
.domain(yDomain)
.range([height - marginBottom, marginTop]);
const contents = d3.select(ele);
const svg = contents.append("svg")
.attr("width", width)
.attr("height", height);
const x_zero = this.scaleX(0);
const y_zero = scaleY(0);
// (3) X軸を配置する
this.axisX = svg.append("g")
.attr("transform", `translate(0,${y_zero})`)
.call(d3.axisBottom(this.scaleX));
// (4) Y軸を配置する
svg.append("g")
.attr("transform", `translate(${x_zero},0)`)
.call(d3.axisLeft(scaleY));
// (5) lineを描写するようの関数を作成する
this.line = d3.line().x((d) => {
return this.scaleX(d.x);
}).y((d) => {
return scaleY(d.y);
});
// (6) sin関数の値を描写する為のpath要素を作成
this.path = svg.append("path");
}
// (7) データを動的に更新する
updateDataset(){
let deg = 0;
setInterval(() => {
// (8)データを作成
const dataset = [];
for(let i = -180 + deg ; i <= 360 + deg; i++) {
const r = Math.PI / 180 * (i + deg);
dataset.push({
x : i,
y : Math.sin(r),
});
}
// (9) X軸の範囲を更新
this.updateXDomain([-180 + deg , 360 + deg]);
// (10)sin関数のグラフを描写(更新)
this.sinLine(dataset);
deg++;
},10);
}
sinLine(dataset){
this.path
.datum(dataset)
.attr('fill','none')
.attr('stroke',"red")
.attr('d',this.line);
}
updateXDomain(xDomain){
this.scaleX.domain(xDomain);
this.axisX
.transition()
.duration(50)
.call(d3.axisBottom(this.scaleX));
}
}
(1)でX軸を描画します。このX軸は後で更新するため、クラス内のメンバ変数として保持します。(2)のY軸は固定したままであるため、メンバ変数にはしていません。
そして、(3)(4)でX軸とY軸をSVG上に描画しています。次に、(5)でsin関数のグラフを描画するための関数を作成し、その描画対象となるノードを(6)で作成します。
(5)と(6)も再描画の際に利用するため、メンバ変数にしています。
そして、(7)のsetIntervalで繰り返し処理を行い、その中で変化した角度ごとにデータを更新します(8)。また、その変化したデータに伴いX軸の範囲を変更し(9)、データに合わせて線を描画し直します(10)。
(9)と(10)の処理自体はこれまで行ってきた内容と変わりませんが、データセット内の角度やsin値の変化に応じて、自動的に再描画されます。
このようにSVG上の同じノードの内容を変更するだけの場合には、それぞれ描画する際に作成した関数をそのまま用いて、変化した内容で更新すれば動的な描画を行えます。
円運動と波運動との連動
これまでのコードをもとに、円運動とsin波形の動きを同期させて表示させたものが、図7です(詳細はサンプルコード(sync.html)を参照してください)。
また、サンプルコードを実行すると円運動の中心点(Y軸)と、円運動の速度を変更できるようにしました。できればこれまでのコードなどを参考にパラメータが変わった場合なども試してみてください。
同じ現象も異なる方法で見ると、計測したデータ結果も変わります。特に三角関数が生かせる領域は幅広く、さまざまな視点でデータを見ることが重要になります。
例えば、円運動の条件のわずかな変化が大きな結果の違いとして出てしまうケースがあります。実際のデータ分析でも、データだけを見ていると、より複雑な結果に注意を向けがちです。
しかし、円運動側を見ると思ったよりも複雑な動きをしていないように、データをどのような視点から見ているのかにも注意が必要となります。
最後に
今回は、基本的な三角関数について、視覚的な理解を通じて、三角関数が単なる数学の公式ではなく、現実世界に適用した際にも同じようにイメージできる手助けになれば幸いです。
次回はsin、cosの波としての特徴について、より深く掘り下げます。高校時代にsinやcosの合成や分解について問われる問題を解いたことを思い出す方もいるかもしれません。そのような波の合成と分解の理解の手助けになる内容について紹介します。
