そして今日、私のGitHub Visualizer プロジェクト ( GitHubプロジェクト )を裁判所に持ち込みたいと思います。
これは予備的な知人のスクリーンキャストです。
そして、大きなGIFではありません

使用されるもの
- SVG 、 Canvas-インタラクティブなグラフィックス用。
- D3.js -javascriptライブラリにより、データを操作して視覚化することが非常に便利になります。
Mike Bostockライブラリの作成者からの膨大 な例のコレクション 。 - Github API
スケジュールとその実装の説明
このプロジェクトには、リポジトリに関する情報、その履歴、および定量的な指標を示す3つの主要な視覚化があります。
リポジトリリストの視覚化
リポジトリグラフ

- 円(頂点)はリポジトリです
- 頂点のサイズは、リポジトリの年齢に依存し、古いほど小さくなります。
- 不透明度は、最後の変更の日付に依存します。
- 頂点の色とグループ化は、リポジトリのメイン言語に依存します。
- 言語のヒストグラム
- 各言語の要約情報を表示します
- 言語の色を表示します
- ホバー時に頂点をフィルタリングできます
グラフを作成するために、 この例で提案したD3.Layout.Forceとクラスタリング手法が使用されました 。
例からのコード
var force = d3.layout.force() .nodes(nodes) .size([width, height]) .gravity(.02) .charge(0) .on("tick", tick) .start(); function tick(e) { circle .each(cluster(10 * e.alpha * e.alpha)) .each(collide(.5)) .attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }); } // Move d to be adjacent to the cluster node. function cluster(alpha) { var max = {}; // Find the largest node for each cluster. nodes.forEach(function(d) { if (!(d.color in max) || (d.radius > max[d.color].radius)) { max[d.color] = d; } }); return function(d) { var node = max[d.color], l, r, x, y, i = -1; if (node == d) return; x = dx - node.x; y = dy - node.y; l = Math.sqrt(x * x + y * y); r = d.radius + node.radius; if (l != r) { l = (l - r) / l * alpha; dx -= x *= l; dy -= y *= l; node.x += x; node.y += y; } }; } // Resolves collisions between d and all other circles. function collide(alpha) { var quadtree = d3.geom.quadtree(nodes); return function(d) { var r = d.radius + radius.domain()[1] + padding, nx1 = dx - r, nx2 = dx + r, ny1 = dy - r, ny2 = dy + r; quadtree.visit(function(quad, x1, y1, x2, y2) { if (quad.point && (quad.point !== d)) { var x = dx - quad.point.x, y = dy - quad.point.y, l = Math.sqrt(x * x + y * y), r = d.radius + quad.point.radius + (d.color !== quad.point.color) * padding; if (l < r) { l = (l - r) / l * alpha; dx -= x *= l; dy -= y *= l; quad.point.x += x; quad.point.y += y; } } return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; }); }; }
実際、これは私を訪ねたミューズでした。
関数は、いくつかの例外と追加を除いて実質的に変更されていません。
リポジトリのリストを視覚化するための関数の実装は、 repo.jsとlangHg.jsの 2つのファイルにあります
リポジトリ履歴の可視化
ユーザーリポジトリのリストに関する情報をダウンロードした後、2番目のステージパネルの列またはリポジトリのリストのいずれかで、目的のリポジトリを選択できます(分析用に最近のリビジョンの数をここで設定することもできます)。

次に、「分析」ボタンを押して分析します。 分析中に、リポジトリ履歴のグラフが作成されます。 指定した最近のコミットの数に関する情報を表示します(デフォルトでは100コミット。リポジトリにある数より少ないかもしれません)。
履歴グラフ

- X軸は修正日を示します。
- 赤い点はそれぞれ固定を表します。
- 上下のアークは、コミットで追加および削除された行の数です。
- 背景の領域には、変更するファイルの数が表示されます。
- アップロードされたファイル
- 変更されたファイル
- 削除されたファイル
- 参加者チャート -さまざまなパラメーターに従って参加者のアクティビティを表示します。
ダイアグラムを描画するために、私はd3.jsライブラリのいくつかのツールとそれらの組み合わせを使用しました。
面積の計算は、 d3.svg.area()コンポーネントによって実行されます( 積み上げ 面積の例)。 私は自分でスタックを検討していますが、他のすべてはd3jsにとって些細なことです。
スタックが考慮されるコード
var layers = [ { color: colors.deletedFile, values: sorted.map(function (d) { return {t : 1, x: d.date, y0 : 0, y: (d.stats ? -d.stats.fd : 0)} }) }, { color: colors.modifiedFile, values: sorted.map(function (d) { return {x: d.date, y0 : 0, y: (d.stats ? d.stats.fm : 0)} }) }, { color: colors.addedFile, values: sorted.map(function (d) { return {x: d.date, y0: (d.stats ? d.stats.fm : 0), y : (d.stats ? d.stats.fa : 0)} }) } ] ; function interpolateSankey(points) { var x0 = points[0][0], y0 = points[0][1], x1, y1, x2, path = [x0, ",", y0], i = 0, n = points.length; while (++i < n) { x1 = points[i][0]; y1 = points[i][1]; x2 = (x0 + x1) / 2; path.push("C", x2, ",", y0, " ", x2, ",", y1, " ", x1, ",", y1); x0 = x1; y0 = y1; } return path.join(""); } var y1 = d3.scale.linear() .range([h6 * 4.5, h6 * 3, h6 * 1.5]) .domain([-data.stats.files, 0, data.stats.files]), area = d3.svg.area() .interpolate(interpolateSankey /*"linear" "basis"*/) .x(function(d) { return x(dx); }) .y0(function(d) { return y1(d.y0); }) .y1(function(d) { return y1(d.y0 + dy); }) ;
アークを構築するには、 d3.svg.arc()を使用します(このコンポーネントが使用される例は多数あります: Arc Tween 、 Pie Multiples )。
2つのコンポーネントd3.time.scale()およびd3.svg.axisを使用して、Xスケールの生成を行います。 実装は、このカスタム時間形式の例から取られています。
参加者の図は、 d3.layout.pack() ( 円パッキングの例)によって計算されます。 円の並べ替えとサイズ変更を行うために、 並べ替えと値のプロパティを変更します。
この視覚化のコードは、 stat.jsとusercommit.jsの 2つのファイルにあります
動的な視覚化
このため、すべてがベンチャーでした。 code_swarmを使用してレンダリングするとどうなるかが気に入っていますが、毎回リポジトリをコンピューターに複製してからレンダリングするのは便利ではありません。
この視覚化では、code_swarmに適用されるすべてのアイデアを実装し、その場で設定を変更しようとしました。
Song-of-githubの視覚化、 Launcherリンク 、 HabrahabrのSong-of-githubに関する記事

- 各パーティクルはファイルです。 開発者から開発者に移動します。
- 粒子サイズはその変化の程度に依存し、頻繁に変化するほど大きくなります。
- 粒子の色は、その膨張に依存します。
- 時間が経つにつれて、粒子は消え、すべての粒子が消えるとすぐに、ユーザーも溶けます。 (これは、パネル3ステージ、 ユーザーライフとファイルライフ 、値0-不滅の対応する設定で調整できます)。
- 各参加者は、自分が操作したファイルを自分の周りに収集します。
- ファイルがユーザーの軌道を離れ、誰にも飛んでいない場合、ファイルは削除されます。
- 1秒ごとに1日(ステップを変更する機能を追加する計画)
- ヒストグラムは、コミットに参加しているファイルの数を拡張子で割ったものを示します
- 凡例には、各拡張子の現在の既存ファイルの数が表示されます。
物理計算は悪名高いD3.Layout.Forceによって実行されますが、わずかな省略を加えて2つあります。 1つはユーザーの位置を計算し、もう1つはユーザーの位置に応じてファイルの位置を考慮します。 これはどのように行われますか? 各ファイルにはauthor
プロパティがあり、このファイルが現在のコミットにある場合、現在のユーザー(コミット時間)が書き込みます。 上記のクラスタリング方法はそれを取得し、空間内の指定されたファイルの位置を考慮します。
クラスタリング機能
function tick() { if (_force.nodes()) { _force.nodes() .forEach(cluster(0.025)); _forceAuthor.nodes( _forceAuthor.nodes() .filter(function(d) { blink(d, !d.links && setting.userLife > 0); if (d.visible && d.links === 0 && setting.userLife > 0) { d.flash = 0; d.alive = d.alive / 10; } return d.visible; }) ); } _forceAuthor.resume(); _force.resume(); } // Move d to be adjacent to the cluster node. function cluster(alpha) { authorHash.forEach(function(k, d) { d.links = 0; }); return function(d) { blink(d, setting.fileLife > 0); if (!d.author || !d.visible) return; var node = d.author, l, r, x, y; if (node == d) return; node.links++; x = dx - node.x; y = dy - node.y; l = Math.sqrt(x * x + y * y); r = radius(nr(d)) / 2 + (nr(node) + setting.padding); if (l != r) { l = (l - r) / (l || 1) * (alpha || 1); x *= l; y *= l; dx -= x; dy -= y; } }; }
そして、強制レイアウトの初期化の場所
_force = (_force || d3.layout.force() .stop() .size([w, h]) .friction(.75) .gravity(0) .charge(function(d) {return -1 * radius(nr(d)); } ) .on("tick", tick)) .nodes([]) ; ..... _forceAuthor = (_forceAuthor || d3.layout.force() .stop() .size([w, h]) .gravity(setting.padding * .001) .charge(function(d) { return -(setting.padding + d.size) * 8; })) .nodes([]) ;
2つのスレッドが動作します(そう言えば)、1つはsetInterval
もう1つはrequestAnimationFrame
です。 最初は時間の移動を担当し、2番目はレンダリングを担当します。 しかし、実際には、forceにも独自のタイマーがあり、asyncForEach(適切なシステム応答があり、1つのコミットからのファイルが一度に飛び出さないようにする必要がありますが、少し遅れて)もsetTimeoutsを起動します。
コードはshow.jsファイルで確認できます。
データ検索
api.github.comからデータを取得します。
データはJSONPメソッドを使用して受信されます。
GitHub APIによると、
Client_id
と
Client_Secret
が必要ですが、リクエストの制限は1時間あたり1 ipに対して60です。 そのため、GitHubのプロファイル設定でアプリケーションを作成し、必要な承認情報をリクエストに追加します。
それが私がすべてだということです...そして、この認証方法の制限が1時間あたり5000リクエストであるという事実に、 mcのようないくつかのリポジトリには豊富な歴史があります。 そして、あなたがそれをうまく歩けば、システムがあなたに言うように、制限はすぐに使い果たされます。 これが発生した場合、 システム設定メニューで、アプリケーションの
client_id
および
client_secret
指定できます(まだ存在しない場合は以前に作成済み)。
GitHubには非常に優れたAPIがあります。リクエストを1つだけ完了するだけで十分です。たとえば、ユーザー情報のリクエスト
api.github.com/users{user}
api.github.com/users{user}
他のすべてのリンクが応答します。 さらに、これが複数ページのリクエストである場合(たとえば、リポジトリのリストを取得する場合、応答には10個のリポジトリに関する情報のみが含まれます)、
meta
パラメーターの応答オブジェクトには、認可パラメーターの完全なセットを含む次のページへのリンクがあります。
一般に、私はAPIの開発者とAPIのドキュメントを書いた人々に感謝を表明します。彼らと一緒に仕事をすることは喜びです。
また、 D3js開発者には、 豊富な例のコレクション (これなしではおそらくこれに触発されなかっただろう)と、すべての説明を含む非常に完全なドキュメントに感謝します。
おわりに
私がプロジェクトを始めた当初、それは自分にとっておもちゃでしたが、実際はそうでした。 リポジトリをフォークしてエラーの束を見つけたり、何か新しいものを台無しにしたりする場合は、プルリクエストを残すか、 Issuesに書き込んでください。
開発中、アプリケーションはGoogle Chrome dev-mでのみテストされました(もちろん、他のブラウザーで修正された妨害を明示的に修正しました)。お気に入りのブラウザーで正しく動作させる方法を知っているなら、私は無限に感謝します。
健康的な批判を待っています。
ご清聴ありがとうございました!
PS
いくつかの興味深いリポジトリ:
- 興味深いプロジェクトリポジトリはありません
- D3js ( レンダリング開始 )
- jQuery ( レンダリングを開始 )
- MidnightCommander ( 視覚化の起動 )