[Astro #24] 1,600ノードの情報を幾何学的秩序で制圧 — 3D Force-Directed Graphの再構築

[Astro #24] 1,600ノードの情報を幾何学的秩序で制圧 — 3D Force-Directed Graphの再構築

はじめに

500件を超える記事、1,600以上のタグ、そしてそれらを繋ぐ9,000本のエッジ。

これまでの「思考の断片」を3D空間に解き放った瞬間、ブラウザは悲鳴を上げ、ネットワークは爆発的なカオスに飲み込まれた。数学的な正しさだけでは、このWIRED(ワイヤード)の深層を制御することはできない。

これは、秩序を失った情報の奔流を、幾何学という檻で強引に制圧し、一つの「結晶」へと昇華させるまでの記録です。

[Astro #24] 1,600ノードの情報を幾何学的秩序で制圧 — 3D Force-Directed Graphの再構築 [Astro #24] 1,600ノードの情報を幾何学的秩序で制圧 — 3D Force-Directed Graphの再構築

YouTube動画:

前回の記事:

1. データ抽出の強行突破:Vite Globによるバイパス

Astroの標準的な getCollection は、500件を超えるMDXファイルと複雑なスキーマチェックにおいてパース落ちや速度低下を招くことがありました。これを回避するため、Viteの import.meta.glob を使用し、ファイルシステムから直接フロントマターを引っこ抜く手法を採用しました。

// Astroの正規APIをバイパスし、Viteの機能で強制抽出
const rawPosts = import.meta.glob('../content/posts//*.{md,mdx}', { eager: true });
const rawFeatured = import.meta.glob('../content/featured//*.{md,mdx}', { eager: true });

// 物理ファイルを走査してタグとFeatured属性をMapに展開
for (const [filepath, module] of Object.entries(rawPosts)) {
  const fm = (module as any).frontmatter || {};
  let rawTags = fm.tags || [];
  // ...データ整形ロジック
}

2. カオスの制圧:フィボナッチ球体による初期配置

Force-Directed Graph(力学モデル)の最大の敵は、初期配置の「ランダム性」によるエネルギーの暴走です。t=0t=0 の時点でノードが重なっていると、反発力が無限大に発散し、ネットワークが爆発します。

これを解決するため、フィボナッチ球体(Fibonacci Sphere)アルゴリズムを採用し、最初から球面上に均等な秩序を持って配置しました。

const nodes = data.nodes.map((n, i) => {
  // フィボナッチ球体による均等配置
  const phi = Math.acos(-1 + (2 * i) / data.nodes.length);
  const theta = Math.sqrt(data.nodes.length * Math.PI) * phi;

  // 出現頻度(count)に応じて半径をオフセットし、多層構造を作る
  const radius = 600 - (n.count / maxCount) * 100;

  return {
    ...n,
    pos: new THREE.Vector3(
      radius * Math.cos(theta) * Math.sin(phi),
      radius * Math.sin(theta) * Math.sin(phi),
      radius * Math.cos(phi)
    ),
    vel: new THREE.Vector3(),
    acc: new THREE.Vector3()
  };
});

3. 物理演算の「検閲」と安定化

数学的に正しい物理演算だけでは、高密度なネットワークの微振動(ジッター)を抑えられません。以下の「ロジックによる検閲」を導入し、安定性を確保しました。

  • 距離の二乗(lengthSq)の使用: 計算コストの高い sqrt を回避。
  • 反発の閾値設定: 4002400^2 ピクセル以上の距離がある場合は計算をスキップ。
  • 強い摩擦係数: 速度に 0.80.8 を乗算し、エネルギーを急速に減衰させ定常状態へ導く。
// 摩擦によるエネルギー減衰
node.vel.add(node.acc);
node.vel.multiplyScalar(0.8); // 摩擦を強めて、早く静止させる
node.pos.add(node.vel);

// 繋がりによる引き合い(バネ係数の調整)
const force = (dist - 150) * 0.005 * (edge.weight * 0.5);

4. 視覚的表現:Lain-like Aesthetics

  • Featuredノード: #ff8888 (Pink/Red) で強調。
  • 通常ノード: #66ffff (Cyan) の粒子。
  • ラベル: THREE.Sprite を活用。重要度(出現頻度)が低いものは背景ノイズとして透明度を下げ、ホバー時のみ関連エッジと共に浮かび上がらせる。
  • 自動回転: OrbitControlsautoRotate を有効化し、静止画ではない「生きているデータ」を演出。

結びに代えて

難解な数学が分からなくても、「近すぎたら計算しない」「動きすぎたら止める」「最初から整列させる」というエンジニアリング的なアプローチだけで、1,600ノードのカオスは制御可能です。

この巨大な球体は、もはや単なるタグクラウドではなく、WIREDの深層に漂う私の「思考の結晶」そのものと言えます。次は、この中に入るためのWebXR化を進める予定です。