[Processing]円状隣接グラフを描いてみた

カテゴリ: processing / author: uechoco / 2008年02月07日 12:22:00
この記事を読む時間:79くらい

さきほどFlickrの

※クリックしてLightboxで拡大
flickr: everyone knows everyone from flickr

http://flickr.com/photos/eskimoblood/2111672366/

の画像をみて、「描きたいっ」と思って、Processingで描いてみました。「Circular Adjacency Graph」。「円状隣接グラフ」って勝手に呼んでいます。プログラム的にそういうことをしているので。全く同じものを作ったわけではないですが、自分の中では同じようなものなので満足しています。

※カーブ率 = 1のグラフ(クリックで別ウィンドウに原寸表示)

processing_CircularAdjacencyGraph.jpg

※カーブ率 = 0.3のグラフ(クリックで別ウィンドウに原寸表示)

processing_CircularAdjacencyGraph2.jpg

Processing:
  1. size(600, 600);
  2. colorMode(HSB, 100);
  3.  
  4. // 定数定義
  5. int max = 30; // 最大数
  6. int radius = 220; // 半径
  7. int cx = width / 2; // 中心x座標
  8. int cy = height / 2; // 中心y座標
  9. float adjRate = 0.3; // 隣接する確率
  10. float curveRate = 0.3; // カーブ率
  11.  
  12. // 変数定義
  13. Point[] points = new Point[max]; // 円周上の点の集合の定義
  14. int[] hues = new int[max]; // 円周上の点のカラー(色相)
  15. int[] adj = new int[max * max]; // 隣接行列の定義
  16.  
  17. // 円周上に点を配置
  18. for (int i = 0; i <max; i++) {
  19.   float angle = random(360);
  20.   float x = cx + cos(radians(angle)) * radius;
  21.   float y = cy - sin(radians(angle)) * radius;
  22.   points[i] = new Point();
  23.   points[i].setLocation(x, y);
  24.   // 色をランダムに定義
  25.   hues[i] = (int)random(100);
  26. }
  27.  
  28. // 隣接行列を作成
  29. for (int i = 0; i <max; i++) {
  30.   for (int j = 0; j <max; j++) {
  31.     if (i>= j) {
  32.       adj[i * max + j] = 0;
  33.     } else {
  34.       adj[i * max + j] = (random(1) <adjRate ? 1 : 0);
  35.     }
  36.   }
  37. }
  38.  
  39. // 円
  40. stroke(70);
  41. ellipseMode(CENTER_RADIUS);
  42. ellipse(cx, cy, radius, radius);
  43. // 点の配置
  44. for (int i = 0; i <max; i++) {
  45.   stroke(hues[i], 60, 100, 60);
  46.   point(points[i].x, points[i].y);
  47.   // 隣接点を結ぶ
  48.   for (int j = 0; j <max; j++) {
  49.     // 隣接していなければ次へ
  50.     if (adj[i * max + j] == 0) continue;
  51.     // 点を再定義(利便性)
  52.     int x1 = points[i].x;
  53.     int y1 = points[i].y;
  54.     int x2 = points[j].x;
  55.     int y2 = points[j].y;
  56.     // 2点間の距離を求める(2倍にして補間点を増やす)
  57.     int x21 = x2 - x1;
  58.     int y21 = y2 - y1;
  59.     float r = sqrt(x21 * x21 + y21 * y21) * 2;
  60.     for (int k = 0; k <r; k++) {
  61.       // 進行度
  62.       float rate = k / r;
  63.       // 点の色を調整
  64.       stroke(hues[i] + (hues[j] - hues[i]) * rate, 60, 100, 60);
  65.       // まずは2点を直線で結んだときの現在点を求める
  66.       float x = x1 + (x21 * rate);
  67.       float y = y1 + (y21 * rate);
  68.       // 重心の設定をするための比率(値域は0?curveRateまで)
  69.       float cRate = sin(PI * rate) * curveRate;
  70.       // 円の中心と現在点に比率をかけて、描画点を求める
  71.       x = (x + cx * cRate) / (cRate + 1);
  72.       y = (y + cy * cRate) / (cRate + 1);
  73.       point(x, y);
  74.     }
  75.   }
  76. }
  77.  
  78. // 署名
  79. PFont font = loadFont("Impact-18.vlw");
  80. fill(0);
  81. textFont(font, 18);
  82. textAlign(RIGHT);
  83. text("Circular Adjacency Graph\n2008 uechoco", width - 20, height - 40);

円周上の点同士をline()命令を使って直線で結ぶのは簡単なのですが、上のFlickrの画像を見ると、中心に向かってカーブしています。これをどうやって再現するのかが悩みました。最初は円の中心と2点を結ぶ直線を漸近線とみたてて、双曲線を描こうとしたのですが、数式を調べたりそれを実装したりするのが面倒だと思って、即却下。代替案として、直線の個々の点と円の中心との重心をなぞる方法にしました。2点間の中心に近づくほど、円の中心に近づくようにサインカーブを調整しています。またカーブ率を低くすることで、重心を円周側に寄せられるので、なめらかなカーブになります。

いやぁーこんなのまで描けるんですね。楽しさ無限大!


コメントはまだありません »

コメントはまだありません。

この投稿へのコメントの RSS フィード。 TrackBack URI

コメントする

Copyright © 2014 うえちょこ@ぼろぐ. WP Theme created by Web Top.