LATEST ENTRIES
CATEGORIES
ARCHIVES
SPONSORED LINK
MOBILE
qrcode
LINKS
PROFILE
OTHERS

11
--
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
--
>>
<<
--

Agata's Blog

MATLABやPythonとともにD3.jsを使ってデータ解析とデータビジュアライゼーションに挑戦する某エンジニアのBlog
D3.js v4によるデータバインド (2)
0

    前回はSVG要素とデータをバインドしたうえで、Join, Update, Enter, Exit, Removeという一連の流れによってグラフが動的に変化する様子を確認しました。今回は、D3.js v4で新たに導入されたmergeメソッドを使って、前回のプログラムを書き直してみます。

     

    サンプルコードを下に示します。生成されるグラフは前回と変わりません。ポイントは、前回は別々に実施していたEnterとUpdateの処理を、36~59行目でいっぺんに行っている部分です。キーとなるのは49行目に出てくる merge メソッドです。

     

    少し詳しく見てゆきましょう。まず36行目で、新たなデータを enter メソッドによって追加しています。追加されたデータは37行目で rect 要素とバインドされたうえで、48行目にかけてさまざまな属性を設定されています。そのうえで、次に出てくる49行目の merge メソッドによって、enterで追加されたデータ要素と、既存のSVGエレメントとバインド済みにデータ要素 (Update分の要素) がマージされます。そして、Enter分の要素とUpdate分の要素の両方に対して、50行目以降で x, y, height 属性を設定しています。

     

    mergeメソッドによってEnterとUpdateの処理をいちどにできるため、v3に比べるとプログラムを若干短縮することができます (たとえば merge メソッドを使わなかった前回のプログラムが78行だったのに対して、今回のプログラムは72行)。

     

    個人的には、merge を使わないでUpdateとEnterをそれぞれ個別に指定するやり方のほうが、プログラムを読んだときに処理内容を理解しやすいので好きなのですが、このへんは好みの問題かもしれませんね。

     

     

    JUGEMテーマ:JavaScript

    このエントリーをはてなブックマークに追加
    | アニメーション | 14:07 | comments(0) | - | - |
    D3.js v4によるデータバインド (1)
    0

      D3.js の大きな特徴は、データをSVGエレメントにバインドできるという点です。といっても、最初はなかなか実感がわかないと思うので、まずは下記のサンプルコードを例に見ていくことにしましょう。

       

      下記のサンプルコードでは、まず20個の要素を持つ配列をサンプルデータとして作成しています(15行目)。そのうえで、23~26行目で、データの個々の要素をSVGのrectエレメントとバインドしています。ここで、バインドの仕方として、データの個々の数値をキーにrect要素とバインドしていることに注意して下さい(24~26行目)。

       

      次に、29~36行目では、データに含まれる要素のうち、既に表示しているSVGのrect要素とバインドされているものについて、rect要素の属性をアップデートしています。ここでは、30行目でtransitionメソッドを加えることで、アップデートの様子をアニメーション表示しています。

       

      次の39~58行目では、データに含まれる要素のうち、既に表示しているSVGのrect要素とバインドされていないものについて、新たにrect要素を追加しています。ここでも、ただrect要素が追加されるだけでは面白くないので、52行目にtransitionメソッドを加えて、rect要素が追加される様子をアニメーションで表示しています。

       

      最後の61~65行目では、既に表示しているSVGのrect要素のうち、データに含まれない要素とバインドされているものをSVGから削除しています。ここでもまた、transitionメソッドを使って削除される様子をアニメーションで表示しています。

       

      69行目は、初期状態として20個の要素を持つ配列dataSetをデータとして、rect要素とバインドしたうえでSVGとして表示しています(この時点では、41行目によってすべてのrect要素のclassは "enter" になっています)。

       

      72~77行目では、2秒(=2000ミリ秒)ごとにデータ配列dataSetをシャッフルのうえランダムな個数だけ選択して、これを新たなデータとしてSVGを更新しています。

       

       

      上記のプログラムでおこなったJoin, Update, Enter, Exit, Removeという一連の流れはD3.js v3と同じです。しかしv4からは、新たにmergeというメソッドが追加されました。次回は、このmergeメソッドを使うと、上記のプログラムのEnterとUpdateの部分がいっぺんに書けるということを紹介します。

       

      JUGEMテーマ:JavaScript

      このエントリーをはてなブックマークに追加
      | アニメーション | 21:13 | comments(0) | - | - |
      D3でマウスイベントを使う
      0

        D3の .on メソッドを使うと、ユーザのマウス操作によって表示中のSVG要素を変化させることができます。今回は、.リック、▲泪Ε好ン、マウスアウトの3つイベントそれぞれによって表示中の図形のサイズや色が変化する簡単なサンプルを紹介します。

         

        27行目までは、SVGの描画領域を設定したうえで、サンプルデータの配列とSVG要素をバインドして、配列の個数分だけ図形を作成しています。これまで何度もやってきた操作なので、説明は省略します。

         

        29行目以降の部分が今回のメインです。マウスイベントをキャッチするには、.on メソッドを使います。メソッドの第1引数 ("click", "mouseover" などの部分) は、キャッチするマウス操作の種類を指定しています。そのうえで、指定したマウス操作をキャッチしたときに何をするかを、第2引数に関数として与えています。

         

        ここで、いくつもある図形のうち、マウス操作をした図形だけを変化させたいので、select(this) としてその図形だけをセレクトしています。

         

         

         

        JUGEMテーマ:JavaScript

        このエントリーをはてなブックマークに追加
        | アニメーション | 19:55 | comments(0) | - | - |
        プルダウンメニューを使ってインタラクティブに散布図を更新
        0

          前回・前々回の2回にわたって、D3.jsを使ってCSVデータから散布図を作成する方法と、x,y軸のスケールをデータにあわせてインタラクティブに変更する方法を、それぞれ紹介しました。今回はこの2つの方法を合体させて、ユーザがプルダウンから選択した変数によって散布図が自動的に変化するページを作ってみます。

           

          読み込むデータとしては、再びIrisデータセットのCSVファイルを使います。このIrisデータセットには4個の変数が含まれています。そこで、この4個の変数から2つをユーザがプルダウンメニューから選択すると、それに従って画面上の散布図が変化するようにしてみました。

           

          Javascriptファイルの78行目までは、前々回にお話したCSVデータから散布図を作成するプログラムとほぼ同じです。違っている部分は、18,19行目でそれぞれプルダウンメニューのDOM要素を選択している部分です。81~88行目は、これら選択されたDOM要素 (プルダウン) が変更されたときに散布図を更新するためのコールバック関数を呼び出している部分です。

           

          91行目以降は、このコールバック関数の処理内容です。100~127行目までは x,y軸を更新する部分で、基本的には前回ご紹介した内容と同じです。130行目以降では、プルダウンで選択された変数によって散布図上のデータ点の位置を変更する処理です。

           

           

          JUGEMテーマ:JavaScript

          このエントリーをはてなブックマークに追加
          | アニメーション | 22:50 | comments(0) | - | - |
          折れ線グラフをアニメーションで表示する
          0
            JUGEMテーマ:JavaScript

            前回作成したグラフですが、せっかくなので表示に動きをつけてみます。散布図をアニメーションで表示する方法については以前このブログで取り上げました。今回は、折れ線グラフをアニメーションで表示する方法を紹介します。

            D3.js を使った折れ線グラフの作成では、まず d3.svg.line() メソッドを使って path を生成します (詳しくは以前の記事を参照)。この path をアニメーションで表示させるには、stroke-dasharray と stroke-dashoffset という、2種類のプロパティを操作します。前者の stroke-dasharray プロパティを使うと、パスを破線で描画するときの線と間隙の長さを指定することができます。また後者の stroke-dashoffset プロパティは、破線が表示される開始位置を指定することができます。このあたり、言葉で説明してもなかなか分かりにくいので、下記の図を参考にして下さい。



            サンプルページとプログラムは下記の通りです。まず、40行目では getTotalLength() メソッドを使って、27〜37行目で作成した path の長さを取得しています。そのうえで、43〜48行目の部分で、上記2種類のプロパティを操作して、4秒かけてこのパスを表示しています。

            サンプルページ

            var dataSet = [];
            
            // X,Y軸を表示できるようにグラフの周囲にマージンを確保する
            var margin = {top: 40, right: 40, bottom: 40, left: 100};
            var width = 800 - margin.left - margin.right;
            var height = 500 - margin.top - margin.bottom;
            
            d3.csv("./js/WorldPopulation.csv", function(error, dataSet){
            
              // SVGの表示領域を生成
              var svg = d3.select("#MyGraph")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
                
              // スケール関数を作成。
              var xScale = d3.scale.linear()
                .domain([1000,2200])
                .range([0,width]);
            
              var yScale = d3.scale.log()
                .domain([0.1,10])
                .range([height,0]);
            
              // 折れ線グラフを生成
              var line = d3.svg.line()
                .x(function(d,i){
                  return xScale(d.Year);
                })
                .y(function(d,i){
                  return yScale(d.Population);      
                });
            
              var path = svg.append("path")
                .attr("class", "line")
                .attr("d", line(dataSet));
              
              // パスの長さを取得
              var pathLength = path.node().getTotalLength();
              
              // "stroke-dasharray" と "stroke-dashoffset" を使ってパスをアニメーションで描画
              path.attr("stroke-dasharray", pathLength + " " + pathLength)
                .attr("stroke-dashoffset", pathLength)
                .transition()
                .duration(4000)
                .ease("linear")
                .attr("stroke-dashoffset", 0);
                  
              // 散布図を描画
              svg.selectAll("circle")
                .data(dataSet)
                .enter()
                .append("circle")
                .attr("r", 0)
                .attr("cx", function(d){
                  return xScale(d.Year);
                })
                .attr("cy", function(d){
                  return yScale(d.Population);
                })
                .attr("fill","orange")
                .transition()
                .delay(function(d,i){
                  return 2000+(i*100);
                })
                .attr("r", 5);
            
              // Y軸を描画
              svg.append("g")
                .attr("class", "axis")
                .call(d3.svg
                  .axis()
                  .scale(yScale)
                  .orient("left")
                  .tickValues([0.1,0.2,0.4,0.6,0.8,1,2,4,6,8,10])
                  .tickPadding(10) // 軸と数値の間のスペースを指定
                  .tickFormat(d3.format(".1f"))
              );
              
              // Y軸のグリッドを描画
              svg.append("g")
                .attr("class", "axis")
                .call(d3.svg
                  .axis()
                  .scale(yScale)
                  .orient("left")
                  .innerTickSize(-width)
                  .tickFormat("")
              );
            
              // X軸を描画
              svg.append("g")
                .attr("class", "axis")
                .attr("transform", "translate(0," + height + ")")
                .call(d3.svg
                  .axis()
                  .scale(xScale)
                  .orient("bottom")
                  .innerTickSize(-height) // X軸のグリッドを描画
                  .tickPadding(10) // 軸と数値の間のスペースを指定
                  .tickFormat(function(d){return d;})
              );
              
              // 凡例
              svg.append("g")
                .attr("class", "axis")
                .append("text")
                .attr("x", -40)
                .attr("y", -20)
                .text("[単位: 10億人]");
            
              svg.append("g")
                .attr("class", "axis")
                .append("text")
                .attr("x", width/2)
                .attr("y", margin.top + height)
                .text("Year");
            
            })
            
            

             
            このエントリーをはてなブックマークに追加
            | アニメーション | 22:00 | comments(0) | - | - |
            【D3.js】 バラ曲線 (Rose curve) を描画する
            0
              JUGEMテーマ:JavaScript

              前回は D3.js を使って、プルダウンメニューで選択した数値によって変化するリサージュ曲線 (Lissajous curve) を描画しました。リサージュ曲線と同じように sin, cos を使った曲線として、バラ曲線 (Rose curve) があります。そこで今回は、このバラ曲線 (別名「正葉曲線」とも呼ばれます) を描画するプログラムを作成してみました。

              バラ曲線は、局座標表示で下式によって現される曲線です。

              r = sin((a/b)θ)

              とてもシンプルな方程式ですが、変数 a, b を変化させることで、驚くほど多様な変化を見せてくれます。

              前回リサージュ曲線を描画したときの html ファイルはそのまま流用して、JavaScript プログラムだけを下記のように変更します。変数 a, b を変化させて、ぜひバラ曲線が変化する様子を楽しんでみて下さい。

              サンプルページ



              // グラフの周囲にマージンを確保する
              var margin = {top: 40, right: 40, bottom: 80, left: 80};
              var width = 600 - margin.left - margin.right;
              var height = 500 - margin.top - margin.bottom;
              
              // SVGの表示領域を生成
              var svg = d3.select("#MyGraph")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
              
              var n = 2001;
              
              var dt = 20.0*Math.PI/n;
              var t = d3.range(0, 20.0*Math.PI, dt);
              var x = d3.range(n);
              var y = d3.range(n);
              
              // X軸のスケール関数を生成
              var x_scale = d3.scale.linear()
                .domain([-1, 1])
                .range([(width/2) - 200, (width/2) + 200]);
                  
              // Y軸のスケール関数を生成
              var y_scale = d3.scale.linear()
                .domain([-1,1])
                .range([(height/2) - 200, (height/2) + 200]);  
                
              // (x,y) の軌跡を描画するための関数
              var line = d3.svg.line()
                .x(function(d, i){ return x_scale(x[i]);})
                .y(function(d, i) { return y_scale(y[i]);})
                .interpolate("linear");
              
              var polyline = svg.append('path')
                .attr("class", "line")
                .attr('stroke', 'black')
                .attr('stroke-width', '1')
                .attr('fill', 'transparent');
              
              // 初期値として a = 1, b = 1のグラフを描画
              update(1,1);
              updateLine();
              
              // 対象となる html 内のプルダウン要素を選択 
              var select_a = document.getElementById( 'listbox_a' );
              var select_b = document.getElementById( 'listbox_b' );
              
              var a = 1;
              var b = 1;
              
              // プルダウンメニューが更新されるとグラフを更新する
              select_a.onchange = function(){
                // プルダウンで選択されているoption要素を取得する
                var selectedItem = this.options[ this.selectedIndex ];
                a = selectedItem.value;
                update(a, b);
                updateLine();
              }
              
              select_b.onchange = function(){
                // プルダウンで選択されているoption要素を取得する
                var selectedItem = this.options[ this.selectedIndex ];
                b = selectedItem.value;
                update(a, b);
                updateLine();
              }
              
              // バラ曲線 (Rose curve) を計算する
              function update(a, b){
                for (var i = 0; i < n; i++){
                  x[i] = Math.sin((a/b)*t[i])*Math.cos(t[i]);
                  y[i] = Math.sin((a/b)*t[i])*Math.sin(t[i]);
                }
              }
              
              // バラ曲線を更新する
              function updateLine(){
                polyline
                  .transition()
                  .duration(1500)
                  .ease("bounce")
                  .attr('d', line(y));
              }
              
              // X軸を描画
              svg.append("g")
                .attr("class", "axis")
                .attr("transform", "translate(0," + height/2 + ")")
                .call(d3.svg
                  .axis()
                  .scale(x_scale)
                  .orient("bottom")
                );
                
              // Y軸を描画
              svg.append("g")
                .attr("class", "axis")
                .attr("transform", "translate(" + width/2 + ",0)")
                .call(d3.svg
                  .axis()
                  .scale(y_scale)
                  .orient("left")
                );
                
              

               
              このエントリーをはてなブックマークに追加
              | アニメーション | 21:03 | comments(0) | - | - |
              【D3.js】 リサージュ曲線 (Lissajous curve) を描画する
              0
                JUGEMテーマ:JavaScript

                前回は D3.js を使って、プルダウンメニューで選択した数値に従って変化するグラフを作成しました。今回はその応用として、リサージュ曲線 (Lissajous curve) を描画してみます。

                リサージュ曲線とは、x = cos(at), y = sin(bt)t を変化させたときに x-y 平面上に描かれる (x, y) の軌跡です。今回は 2 つのプルダウンメニューを用意して、a, b それぞれの数値をユーザが選択すると、画面上のリサージュ曲線がそれに従って変化するページを作ってみます。

                まず準備として、html ファイルに以下を追加します。ここでは D3.js で描画するグラフの線として、幅2ピクセルのオレンジ色の線を指定しています。また、プルダウンメニューとして a, b それぞれ 1 〜 10 まで選択可能としました。

                サンプルページ


                 

                <style> 
                  .line{ 
                    fill: none; 
                    stroke: orange; 
                    stroke-width: 2px; 
                  } 
                </style> 

                (中略)

                <p>
                a = <select id="listbox_a">
                  <option value="1">1</option>
                  <option value="2">2</option>
                  <option value="3">3</option>
                  <option value="4">4</option>
                  <option value="5">5</option>
                  <option value="6">6</option>
                  <option value="7">7</option>
                  <option value="8">8</option>
                  <option value="9">9</option>
                  <option value="10">10</option>
                </select>
                , b = <select id="listbox_b">
                  <option value="1">1</option>
                  <option value="2">2</option>
                  <option value="3">3</option>
                  <option value="4">4</option>
                  <option value="5">5</option>
                  <option value="6">6</option>
                  <option value="7">7</option>
                  <option value="8">8</option>
                  <option value="9">9</option>
                  <option value="10">10</option>
                </select>
                </p>
                <svg id="MyGraph"></svg>


                ここで、対象となるプルダウンメニューを特定できるよう、id = "listbox_a", "listbox_b" として、それぞれのプルダウンに ID を設定しています。
                次に、プルダウンメニューで選択された a, b の値を取得して、リサージュ曲線を描画する D3.js プログラムを以下に示します。

                // X,Y軸を表示できるようにグラフの周囲にマージンを確保する
                var margin = {top: 40, right: 40, bottom: 80, left: 80};
                var width = 600 - margin.left - margin.right;
                var height = 500 - margin.top - margin.bottom;
                
                // SVGの表示領域を生成
                var svg = d3.select("#MyGraph")
                  .attr("width", width + margin.left + margin.right)
                  .attr("height", height + margin.top + margin.bottom)
                  .append("g")
                  .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
                
                var n = 800;
                
                var dt = 2.0*Math.PI/n;
                var t = d3.range(0, 2.0*Math.PI, dt);
                var x = d3.range(n);
                var y = d3.range(n);
                
                // X軸のスケール関数を生成
                var x_scale = d3.scale.linear()
                  .domain([-1, 1])
                  .range([(width/2) - 200, (width/2) + 200]);
                    
                // Y軸のスケール関数を生成
                var y_scale = d3.scale.linear()
                  .domain([-1,1])
                  .range([(height/2) - 200, (height/2) + 200]);  
                  
                // (x,y) の軌跡を描画するための関数
                var line = d3.svg.line()
                  .x(function(d, i){ return x_scale(x[i]);})
                  .y(function(d, i) { return y_scale(y[i]);})
                  .interpolate("linear");
                
                var polyline = svg.append('path')
                  .attr("class", "line")
                  .attr('stroke', 'black')
                  .attr('stroke-width', '1')
                  .attr('fill', 'transparent');
                
                // 初期値として a = 1, b = 1のグラフを描画
                update(1,1);
                updateLine();
                
                // 対象となる html 内のプルダウン要素を選択 
                var select_a = document.getElementById( 'listbox_a' );
                var select_b = document.getElementById( 'listbox_b' );
                
                var a = 1;
                var b = 1;
                
                // プルダウンメニューが更新されるとグラフを更新する
                select_a.onchange = function(){
                  // プルダウンで選択されているoption要素を取得する
                  var selectedItem = this.options[ this.selectedIndex ];
                  a = selectedItem.value;
                  update(a, b);
                  updateLine();
                }
                
                select_b.onchange = function(){
                  // プルダウンで選択されているoption要素を取得する
                  var selectedItem = this.options[ this.selectedIndex ];
                  b = selectedItem.value;
                  update(a, b);
                  updateLine();
                }
                
                
                // リサージュ曲線 (Lissajous curve) を計算する
                function update(a, b){
                  for (var i = 0; i < n; i++){
                    x[i] = Math.cos(a*t[i]);
                    y[i] = Math.sin(b*t[i]);
                  }
                }
                
                // グラフを更新する
                function updateLine(){
                  polyline
                    .transition()
                    .duration(300)
                    .attr('d', line(y));
                }
                
                // X軸を描画
                svg.append("g")
                  .attr("class", "axis")
                  .attr("transform", "translate(0," + height/2 + ")")
                  .call(d3.svg
                    .axis()
                    .scale(x_scale)
                    .orient("bottom")
                  );
                  
                // Y軸を描画
                svg.append("g")
                  .attr("class", "axis")
                  .attr("transform", "translate(" + width/2 + ",0)")
                  .call(d3.svg
                    .axis()
                    .scale(y_scale)
                    .orient("left")
                  );
                
                このエントリーをはてなブックマークに追加
                | アニメーション | 19:20 | comments(0) | - | - |
                D3.js でギブス現象を見る
                0
                  JUGEMテーマ:JavaScript

                  今回は、プルダウンメニューで選択した数値によって変化するグラフを作成します。とはいっても、ただの練習のためだけのプログラムを書いても面白くないので、フーリエ変換 (Fourier transform) のギブス現象 (Gibbs phenomenon) が発生する様子を D3.js を使って可視化してみます。

                  ギブス現象というのは、周期関数のフーリエ級数において、元の周期関数の不連続点付近でフーリエ級数の m 次部分和が大きく振動する現象のことを指します。

                  言葉で書いてもなかなか分かりにくい現象なので、実際の例で見てみましょう。簡単のために元信号 f(x) を次のような x = π で不連続点を持つ矩形信号とします。


                  この関数をフーリエ級数展開すると、次式のようになります。

                  ではいよいよ、D3.js を使ってこのフーリエ級数展開の m 次部分和を描画してみます。加えて今回は、m の数値をプルダウンメニューから選択できるようにしてみます。こうすることで、m の値を増やすと次第に不連続点 (x = π) 周辺で振動が発生するギブス現象を可視化することができます。

                  サンプルページ



                  まず準備として、html ファイルに以下を追加します。ここでは D3.js で描画するグラフの線として、幅2ピクセルのオレンジ色の線を指定しています。また、プルダウンメニューとして m = 1 〜 100 まで選択可能としました。
                   

                  <style>
                    .line{
                      fill: none;
                      stroke: orange;
                      stroke-width: 2px;
                    }
                  </style>

                  (中略)

                  <p>
                  m = <select id="listbox">
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                    <option value="4">4</option>
                    <option value="5">5</option>
                    <option value="10">10</option>
                    <option value="20">20</option>
                    <option value="50">50</option>
                    <option value="100">100</option>
                  </select>
                  </p>
                  <svg id="MyGraph"></svg>


                  ここで、対象となるプルダウンメニューを特定できるよう、id = "listbox" として ID を設定しています。

                  次に、プルダウンメニューで選択された m の値を取得して、フーリエ級数展開の m 次部分和を描画する D3.js プログラムを以下に示します。44 行目でまず html 内のプルダウン要素を選択しています。そのうえで、47 行目でこのプルダウンメニュー選択の更新を検知して、グラフを更新しています。具体的には、プルダウンにより選択された m の値を 49, 59 行目で取得しています。そのうえで 56 行目以降で定義する update() 関数でフーリエ級数展開の m 次部分和を計算し、70 行目以降で定義する updateLine() 関数でグラフを更新しています。

                  // X,Y軸を表示できるようにグラフの周囲にマージンを確保する
                  var margin = {top: 40, right: 40, bottom: 80, left: 80};
                  var width = 800 - margin.left - margin.right;
                  var height = 500 - margin.top - margin.bottom;
                  
                  // SVGの表示領域を生成
                  var svg = d3.select("#MyGraph")
                    .attr("width", width + margin.left + margin.right)
                    .attr("height", height + margin.top + margin.bottom)
                    .append("g")
                    .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");
                  
                  var dx = 2.0*Math.PI/800.0;
                  var x = d3.range(0,2.0*Math.PI,dx);
                  var y = d3.range(0,x.length);
                  
                  // 時間軸のスケール関数を生成
                  var x_scale = d3.scale.linear()
                    .domain([0, 2.0*Math.PI])
                    .range([0, width]);
                      
                  // 縦軸のスケール関数を生成
                  var y_scale = d3.scale.linear()
                    .domain([-2,2])
                    .range([height, 0]);  
                    
                  // データごとに折れ線グラフを描画するための関数
                  var line = d3.svg.line()
                    .x(function(d, i){ return x_scale(x[i]);})
                    .y(function(d, i) { return y_scale(y[i]);})
                    .interpolate("linear");
                  
                  var polyline = svg.append('path')
                    .attr("class", "line")
                    .attr('stroke', 'black')
                    .attr('stroke-width', '1')
                    .attr('fill', 'transparent');
                  
                  // 初期値として m = 1 のグラフを描画
                  update(1);
                  updateLine();
                  
                  // 対象となる html 内のプルダウン要素を選択 
                  var select = document.getElementById( 'listbox' );
                  
                  // プルダウンメニューが更新されるとグラフを更新する
                  select.onchange = function(){
                    // プルダウンで選択されているoption要素を取得する
                    var selectedItem = this.options[ this.selectedIndex ];
                    var value = selectedItem.value;
                    update(value);
                    updateLine();
                  }
                  
                  // フーリエ級数展開の m 次部分和を計算する
                  function update(k){
                    for (var i = 0; i < k; i++){
                      for (var j = 0; j < x.length; j++){
                        if (i == 0){
                          y[j] = (4/Math.PI)*(1/(2*(i+1)-1))*Math.sin((2*(i+1)-1)*x[j]);
                        }
                        else{
                          y[j] = y[j] + (4/Math.PI)*(1/(2*(i+1)-1))*Math.sin((2*(i+1)-1)*x[j]);
                        }
                      }
                    }
                  }
                  
                  // グラフを更新する
                  function updateLine(){
                    polyline
                      .transition()
                      .duration(300)
                      .attr('d', line(y));
                  }
                  
                  // X軸を描画
                  svg.append("g")
                    .attr("class", "axis")
                    .attr("transform", "translate(0," + height/2 + ")")
                    .call(d3.svg
                      .axis()
                      .scale(x_scale)
                      .orient("bottom")
                    );
                    
                  // Y軸を描画
                  svg.append("g")
                    .attr("class", "axis")
                    .call(d3.svg
                      .axis()
                      .scale(y_scale)
                      .orient("left")
                    );
                    
                  

                   
                  このエントリーをはてなブックマークに追加
                  | アニメーション | 07:51 | comments(0) | - | - |
                  【D3.js】 フォースレイアウトの作成例
                  0
                    JUGEMテーマ:JavaScript

                    D3.js では力学モデルに基づく描画をおこなうフォースレイアウト (force layout) が準備されています。フォースレイアウトを使うと、ノードとリンクによって表現されたグラフ (Graph) 形式のデータをアニメーションとして可視化することができます。

                    フォースレイアウトでは、d3.layout.force() を使って、ノード間の距離やノード間に働く力、リンクの伸び縮みのしやすさなどを設定して、動きのあるデータ可視化を実現することができます。フォースレイアウトで設定できる主なパラメータは、以下のとおりです。
                     
                    パラメータ 意味
                    .node ノードを指定
                    .links ノード間を結ぶリンクを指定
                    .size 表示領域のサイズを指定
                    .linkDistance リンクの長さを指定
                    .linkStrength リンクの伸び縮みのしやすさを指定
                    .gravity 表示領域の中心に引き寄せる重力の強さを指定
                    .charge ノード間の引力/斥力を指定

                    サンプルページ


                    なお、サンプルページのプログラムでは、リンクの線幅や色を指定するために css (あるいは html ファイルの <style type="text/css"> 〜 </style>) に次の記述を追加しています。
                     
                      .line{
                        fill: none;
                        stroke: orange;
                        stroke-width: 2px;
                      }

                    サンプルプログラムは、以下のとおりです。
                     
                    var width = 800;
                    var height = 500;

                    var color = d3.scale.category20(); //D3.jsが用意する標準10色

                    // フォースレイアウトの元となるデータを作成
                    var dataSet = {
                      nodes : [],
                      links : []
                    }

                    var N1 = 8;

                    for(var k=0; k<N1; k++){
                      var st = "Node"+k;
                      dataSet.nodes.push({name : st});
                    }

                    for(var k=1; k<N1; k++){
                      dataSet.links.push({source : k-1, target : k});
                    }

                    // SVGの表示領域を生成
                    var svg = d3.select("#MyGraph")
                      .attr("width", width)
                      .attr("height", height)

                    // フォースレイアウトの設定
                    var force = d3.layout.force()
                      .nodes(dataSet.nodes)
                      .links(dataSet.links)
                      .size([width, height])
                      .linkDistance(80)
                      .linkStrength(1)
                      .gravity(0.01)
                      .charge(-20)
                      .start()
                      
                    // ノード間のリンクを描画
                    var link = svg.selectAll("line")
                      .data(dataSet.links)
                      .enter()
                      .append("line")
                      .attr("class", "line")

                    // ノードを描画  
                    var node = svg.selectAll("circle")
                      .data(dataSet.nodes)
                      .enter()
                      .append("circle")
                      .attr("r", 20)
                      .call(force.drag)

                    // ノードの名前をテキストで表示
                    var nodeText = svg.selectAll("text")
                      .data(dataSet.nodes)
                      .enter()
                      .append("text")

                    // フォースレイアウトを描画  
                    force.on("tick", function(){
                      link
                        .attr("x1", function(d){return d.source.x;})
                        .attr("y1", function(d){return d.source.y;})
                        .attr("x2", function(d){return d.target.x;})
                        .attr("y2", function(d){return d.target.y;})
                      node
                        .attr("cx", function(d){return d.x;})
                        .attr("cy", function(d){return d.y;})
                        .style("fill", function(d,i){
                          return color(i%20);
                        })
                        .style("fill-opacity", 0.5)
                      nodeText
                        .text(function(d){return d.name;})
                        .attr("x", function(d){return d.x+25;})
                        .attr("y", function(d){return d.y-5;})
                    })
                    このエントリーをはてなブックマークに追加
                    | アニメーション | 22:48 | comments(0) | - | - |
                    D3.js を使った時計・その2
                    0
                      JUGEMテーマ:JavaScript

                      以前このブログで、D3.js のパイチャートとアニメーション機能を使った時計の作例を紹介しました。今回は、前回の記事で取り上げた SVG のドロップシャドウを使って、背景から時刻が浮かび上がってくるデジタル時計を作ってみます。

                      ドロップシャドウでは、feGaussianBlur の stdDeviation をゼロとして、なおかつ feOffset で 2 つの SVG 要素をずらす量もゼロとすると、影のない SVG 要素だけが表示されます。circle 要素や rect 要素ではその通りうまくいくのですが、この時計を作っているなかで、なぜか text 要素だけはどうしてもうっすら輪郭が残ってしまうことが分かりました。そこで今回のプログラムでは苦肉の策として、白い rect 要素を定期的に上から覆いかぶせて、うっすら残るテキスト要素の輪郭を画面表示から隠しています (42〜48行目と84〜87行目) 。

                      サンプルページ


                      ■ JavaScript プログラム
                      var width = 500;
                      var height = 300;
                       
                      svg = d3.select("#MyGraph")
                        .attr("width", width)
                        .attr("height", height);
                      
                      // #drop-shadow という ID のフィルタを定義
                      var filter = svg.append("defs")
                        .append("filter")
                        .attr("id", "drop-shadow")
                        .attr("height", "130%");
                      
                      // 元となるSVG要素をぼかして影を作る
                      var feGaussianBlur = filter.append("feGaussianBlur")
                          .attr("in", "SourceAlpha")
                          .attr("stdDeviation", 3)
                          .attr("result", "blur");
                      
                      // 作った影の表示位置を少しずらす
                      var feOffset = filter.append("feOffset")
                          .attr("in", "blur")
                          .attr("dx", 5)
                          .attr("dy", 5)
                          .attr("result", "offsetBlur");
                          
                      // 2つの入力画像 (元のSVG要素と影) を重ねて表示       
                      filter.append("feBlend")
                        .attr("in", "SourceGraphic")
                        .attr("in2", "blurOut")
                        .attr("mode", "normal");
                       
                      var item = svg.append("text")
                        .text("Text")
                        .attr("x", width/2)
                        .attr("y", height/2)
                        .attr("text-anchor", "middle")
                        .attr("font-size", "100px")
                        .style("fill", "white")
                        .style("filter", "url(#drop-shadow)");
                      
                      var mask = svg.append("rect")
                        .attr("x", 0)
                        .attr("y", 0)
                        .attr("height", height)
                        .attr("width", width)
                        .style("fill", "white")
                        .style("fill-opacity", 1);
                        
                      // 影の位置とぼかしの強さを変化させて、SVG要素が画面から浮かび上がるようにアニメーション
                      var dest_min = 0, dest_max = 5, dest = dest_min;
                      var std_min = 0, std_max = 3, std = std_min;
                      var opa_min = 1, opa_max = 0, opa = opa_min;
                      
                      // 現在時刻を取得して変数 now に保存
                      var now = new Date();
                      
                      var updateTime = function(){
                        now = new Date();
                      };
                      
                      var floater = function() {
                        if (dest == dest_min) {
                          dest = dest_max;
                          std = std_max;
                          opa = opa_max;
                          updateTime();
                        }
                        else {
                          dest = dest_min;
                          std = std_min;
                          opa = opa_min;
                        }
                        item.transition()
                          .duration(1000)
                          .text(now.toLocaleTimeString());
                        feOffset.transition()
                          .duration(1000)
                          .attr("dx", dest)
                          .attr("dy", dest)
                        feGaussianBlur.transition()
                          .duration(1000)
                          .attr("stdDeviation", std)
                        mask.transition()
                          .duration(1000)
                          .style("fill-opacity", opa)
                          .each("end", floater);
                      };
                      
                      floater();
                      

                       
                      このエントリーをはてなブックマークに追加
                      | アニメーション | 00:52 | comments(0) | - | - |