[Astro #24] 1,600ノードの情報を幾何学的秩序で制圧 — 3D Force-Directed Graphの再構築
はじめに
500件を超える記事、1,600以上のタグ、そしてそれらを繋ぐ9,000本のエッジ。
これまでの「思考の断片」を3D空間に解き放った瞬間、ブラウザは悲鳴を上げ、ネットワークは爆発的なカオスに飲み込まれた。数学的な正しさだけでは、このWIRED(ワイヤード)の深層を制御することはできない。
これは、秩序を失った情報の奔流を、幾何学という檻で強引に制圧し、一つの「結晶」へと昇華させるまでの記録です。
YouTube動画:
[Astro/R3F] WIRED_NET // 1,600 Nodes Immersive 3D Network [WebXR]
Mapping the WIRED.I transformed 1,600+ tags and 9,000+ edges from over 500 blog posts into a 3D Force-Directed Graph. By implementing Fibonacci sphere layout...
www.youtube.com前回の記事:
[Astro #23] Netlify Formsでサーバーレスなコンタクトフォームをゼロから構築する // PROTOCOL.LAIN
バックエンドを持たないAstro製静的サイトに、Netlifyの標準機能であるFormsを使ってコンタクトフォームを実装する手順を解説。data-astro-reloadによるエラー回避や通知設定も網羅。
lain-lab.com1. データ抽出の強行突破: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(力学モデル)の最大の敵は、初期配置の「ランダム性」によるエネルギーの暴走です。 の時点でノードが重なっていると、反発力が無限大に発散し、ネットワークが爆発します。
これを解決するため、フィボナッチ球体(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を回避。 - 反発の閾値設定: ピクセル以上の距離がある場合は計算をスキップ。
- 強い摩擦係数: 速度に を乗算し、エネルギーを急速に減衰させ定常状態へ導く。
// 摩擦によるエネルギー減衰
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を活用。重要度(出現頻度)が低いものは背景ノイズとして透明度を下げ、ホバー時のみ関連エッジと共に浮かび上がらせる。 - 自動回転:
OrbitControlsのautoRotateを有効化し、静止画ではない「生きているデータ」を演出。
結びに代えて
難解な数学が分からなくても、「近すぎたら計算しない」「動きすぎたら止める」「最初から整列させる」というエンジニアリング的なアプローチだけで、1,600ノードのカオスは制御可能です。
この巨大な球体は、もはや単なるタグクラウドではなく、WIREDの深層に漂う私の「思考の結晶」そのものと言えます。次は、この中に入るためのWebXR化を進める予定です。