needhelp
← ブログに戻る

@xingwangzhe/tags-cloud: 純粋数学で作られたマルチモーダル3Dタグクラウドエンジン

著者 needhelp
TagsCloud
3D
TypeScript
Math
Visualization
Open Source

@xingwangzhe/tags-cloud: 純粋数学で作られたマルチモーダル3Dタグクラウドエンジン

GitHub: xingwangzhe/tags-cloud · Demo: tagscloud.needhelp.icu · npm: @xingwangzhe/tags-cloud · License: MIT · Version: v0.9.0


1. はじめに: モダンWebのための3Dタグクラウドの再発明

タグクラウドは20年以上にわたりWeb視覚表現の定番であり続けてきた。シンプルな重み付き単語リストから、ユーザーを引き込み、ありふれたテキストの集合をインタラクティブな立体的彫刻へと変える没入型の3D体験へと進化してきた。この進化に最も影響を与えた貢献のひとつが cong-min/TagCloud だ。この軽量JavaScriptライブラリは、最小限のオーバーヘッドで外部依存ゼロのまま、回転するテキストラベルの球体を実現できることを示した。GitHubで 390スター93フォーク を獲得したオリジナルのTagCloudは、ポートフォリオサイト、スキルショーケース、データ可視化ダッシュボードなど、数え切れないほどの開発者に採用されるデザインパターンを確立した。その核となる革新は、3Dタグクラウドが重量級のWebGLフレームワークや外部レンダリングエンジンに依存する必要はなく、シンプルな三角関数の計算と注意深いDOM配置で驚くほど魅力的な視覚結果を生み出せることを証明した点にある。

そこに登場したのが @xingwangzhe/tags-cloud だ。先駆者の数学的なエレガンスを継承しつつ、機能を劇的に拡張し、アーキテクチャをモダナイズし、タグクラウドがレンダリングできる表現の限界を押し広げる、3Dタグクラウド概念のゼロからの再発明である。これは単なるフォークや機能追加ではない。テキスト専用・DOM主体のレンダラーから、テキスト、画像、SVGグラフィクス、任意のHTML、動画要素、さらにはWeb Componentsまでもが数学的に正確な3D空間で一緒に回転するマルチモーダルエンジンへの、根本的なアーキテクチャシフトを意味する。このライブラリはこの汎用性を達成しながら、gzip圧縮で約3KBという驚異的なコンパクトサイズを維持し、ランタイム依存ゼロを実現し、Bun(JavaScriptランタイム)、Vite(バンドラ)、TypeScript(コードベース全体の完全な型安全性)という最新のツールチェーンで構築されている。

プロジェクトのタグライン — “multi-modal 3D tag cloud · pure math engine” — はその二面性を凝縮している。一方では、開発者がどんなWebプロジェクトにもドロップインして視覚的に印象的なタグ球体を作成できる実用的なUIコンポーネントライブラリである。他方では、3Dタグクラウドレンダリングという複雑な問題を、Fibonacci球面分布(初期位置決め)、回転行列乗算(空間変換)、透視投影(深度を考慮した2Dレンダリング)という3つの純粋で決定論的で美しく分離可能な幾何演算に分解する数学エンジンである。各演算は src/core/ ディレクトリ内の専用モジュールに実装されており、コードベースは高いメンテナンス性を持つだけでなく、完全なレンダリングパイプラインの複雑さに隠されることなく3Dコンピュータグラフィクスの背後にある数学を理解したい人にとって優れた教育リソースにもなっている。

以降のセクションでは、黄金比由来の球面分布アルゴリズムからクォータニオンに着想を得たアークボール操作モデル、デュアルCanvas/DOMレンダリングアーキテクチャからデスクトップとモバイルでシームレスに動作するレスポンシブデザインまで、@xingwangzhe/tags-cloud のあらゆる側面を包括的に掘り下げる。このライブラリを次のプロジェクトで評価しようとしている開発者にも、軽量3Dテクニックに興味のあるグラフィクスプログラマにも、Fibonacci格子の実世界応用に好奇心を持つ数学者にも、この記事は詳細な説明、数学的導出、アーキテクチャ図、注釈付きコード例を通じて、この注目すべきソフトウェア工学のあらゆる側面を照らし出す。


2. 数学的基盤: 3Dレンダリングの3つの柱

@xingwangzhe/tags-cloud の中核には、コンテンツ項目の抽象的なリストを視覚的に一貫性のある回転する球体へと変換する、3つの数学的操作からなる三連構造がある。分布→回転→投影というこれらの操作は src/core/ 下の3つの専用モジュールに実装されており、各々が単一の幾何変換を担当する。この関心の分離は単なるアーキテクチャ上の好みではない。頂点生成、モデル変換、カメラ投影が個別に最適化・テスト・理解可能な独立したステージであるという、3Dコンピュータグラフィクスパイプラインの基本構造を反映している。

⚙️ Pure Math Engine(src/core/)📥 Input Layer👤 Interaction Layer🎨 Render Layer"Vec3[]""Rotated Vec3[]""ProjectedTag[](x, y, z, scale, alpha)""rotation angles"Canvas 2D API(text + images)🔵 distribution.tsFibonacci SphereN points → 3D coordinatesDOM Overlay(SVG + HTML + video +elements)Inertia Decay(0.96/frame)Mouse / Touch DragTagCloudOptions(radius, spin, colors,callbacks)🔴 projection.tsPerspective Projectionrotated 3D → 2D + depth🟢 rotation.tsRotation Matrices3D points → rotated 3DpointsTagItem[](text | image | SVG |HTML | video | element)

2.1 Fibonacci球面分布: 点配置の黄金幾何

3Dタグクラウド作成における最初の、そしておそらく視覚的に最も重要なステップは、各タグを球面上のどこに配置するかを決めることだ。単純なアプローチでは緯度・経度を一定間隔で配置するが、これでは経線が収束する極付近でのクラスタリングと赤道付近での疎な領域が避けられない。Fibonacci球面アルゴリズムはこの問題をエレガントに解決し、点の数に関係なく球面全体にわたって顕著に均一な分布を生成する。視認できるクラスタリングやギャップがない。

このアルゴリズムの名前の由来は黄金比 φ=1+521.6180339887\varphi = \frac{1 + \sqrt{5}}{2} \approx 1.6180339887 である。これはひまわりの種の配列からオウムガイの螺旋まで、自然界のいたるところに現れる数学定数だ。Fibonacci数列との関連は、数列が進むにつれて連続するFibonacci数の比が黄金比に収束することにある。球面分布の文脈では、黄金比の無理数性によって連続する点が決して繰り返されない角度に配置され、球面を均等に埋める非周期螺旋パターンが生まれる。

src/core/distribution.ts の具体的な実装では、球面マッピング用に最適化されたFibonacci格子のバリエーションを使用している。半径 RR の球面に NN 個の点を分布させる場合、アルゴリズムは各点 i{0,1,2,,N1}i \in \{0, 1, 2, \ldots, N-1\} について球面座標を計算し、それをデカルト座標 (x,y,z)(x, y, z) に変換する。緯度角 ϕ\phi(正のz軸、すなわち北極からの角度)は逆余弦関数を用いて計算され、z軸に沿った一様分布を保証する:

ϕi=arccos(1+2i+1N)\phi_i = \arccos\left(-1 + \frac{2i + 1}{N}\right)

この式は最初の点を北極のすぐ下、最後の点を南極のすぐ上に配置し、+0.5+0.5 のオフセット(2i+12i + 1 分子に埋め込まれている)によってどの点も極に正確に着地しないようにしている。この微調整は極に直接タグが置かれて回転がほとんど知覚できなくなるという視覚的アーチファクトを防ぐために極めて重要だ。arccos\arccos 関数は一様分布する線形パラメータを余弦重み付きの角度分布に変換し、極付近では表面積が小さく赤道付近では大きいという球の性質を補償する。

経度角 θ\theta(xy平面で正のx軸からの角度)は ϕ\phiNπ\sqrt{N\pi} を乗じて計算され、特徴的な螺旋パターンを生み出す:

θi=Nπϕi\theta_i = \sqrt{N\pi} \cdot \phi_i

Nπ\sqrt{N\pi} の因子は黄金角と球の総表面積の関係から導出される。NN が増加するにつれて、このスケーリングは近隣の点間の均一な間隔を維持するために螺旋が球の周りを適切な回数巻き付くことを保証する。結果として、各連続する点はz軸方向から見ると先行点に対して約黄金角 137.5°\approx 137.5° だけ回転したパターンとなり、自然界の葉序螺旋を反映する。

最後に、これらの球面座標は標準変換を用いてデカルト座標に変換される:

xi=Rcos(θi)sin(ϕi)x_i = R \cdot \cos(\theta_i) \cdot \sin(\phi_i)

yi=Rsin(θi)sin(ϕi)y_i = R \cdot \sin(\theta_i) \cdot \sin(\phi_i)

zi=Rcos(ϕi)z_i = R \cdot \cos(\phi_i)

得られる点は球面方程式 xi2+yi2+zi2=R2x_i^2 + y_i^2 + z_i^2 = R^2 を満たし、計算コストの高い最適化ベースの手法に匹敵する均一性で分布する。以下の表示は完全なFibonacci球面アルゴリズムを要約している:

FibonacciSphere(N,R):i[0,N1]:{ϕi=arccos(1+2i+1N)θi=NπϕiPi=(Rsinϕicosθi,  Rsinϕisinθi,  Rcosϕi)\boxed{\text{FibonacciSphere}(N, R): \quad \forall i \in [0, N-1]: \begin{cases} \phi_i = \arccos\left(-1 + \frac{2i+1}{N}\right) \ \theta_i = \sqrt{N\pi} \cdot \phi_i \ P_i = (R\sin\phi_i\cos\theta_i, ; R\sin\phi_i\sin\theta_i, ; R\cos\phi_i) \end{cases}}

src/core/distribution.ts のTypeScript実装は驚くほど簡潔で、アルゴリズムのエレガントな単純さを反映している:

export interface Vec3 { x: number; y: number; z: number; }
export function fibonacciSphere(n: number, R: number): Vec3[] {
const points: Vec3[] = [];
for (let i = 0; i < n; i++) {
const phi = Math.acos(-1 + (2 * i + 1) / n);
const theta = Math.sqrt(n * Math.PI) * phi;
points.push({
x: R * Math.cos(theta) * Math.sin(phi),
y: R * Math.sin(theta) * Math.sin(phi),
z: R * Math.cos(phi),
});
}
return points;
}

この関数はクラウド内のすべてのタグの初期位置となる Vec3 オブジェクトの配列を返す。アルゴリズムは O(N)\mathcal{O}(N) 時間で実行されるため、数百要素のタグクラウドにも適している。実際には、ほとんどのタグクラウドは20〜100個のタグを含み、この計算は事実上瞬時に行われる。

2.2 3D回転: 空間領域での行列乗算

タグが球面上に配置されたら、次のステップは回転を可能にすることだ — 連続的な自動スピンとユーザー操作によるドラッグの両方だ。このライブラリは複合軸合わせ回転行列、具体的にはY軸回転→X軸回転の順次回転を実装している。このアプローチは計算効率が高く、理解しやすく、回転が常に原点中心の球体に対するものであるタグクラウドのユースケースには完全に十分である。

これらの回転の数学的基盤は線形代数にある。3D回転行列は、点の座標を変換しながら原点からの距離を保持する、行列式が1の 3×33 \times 3 直交行列である。角度 α\alpha によるY軸回りの回転行列 Ry(α)R_y(\alpha) は:

Ry(α)=(cosα0sinα010sinα0cosα)R_y(\alpha) = \begin{pmatrix} \cos\alpha & 0 & \sin\alpha \ 0 & 1 & 0 \ -\sin\alpha & 0 & \cos\alpha \end{pmatrix}

角度 β\beta によるX軸回りの回転行列 Rx(β)R_x(\beta) は:

Rx(β)=(1000cosβsinβ0sinβcosβ)R_x(\beta) = \begin{pmatrix} 1 & 0 & 0 \ 0 & \cos\beta & -\sin\beta \ 0 & \sin\beta & \cos\beta \end{pmatrix}

ライブラリはこれらの回転を順次適用する: 最初にY軸回転、次にX軸回転。点 P=(x,y,z)P = (x, y, z) について、Y軸回転は中間点 PP' を生成する:

(xyz)=Ry(α)(xyz)=(xcosα+zsinαyxsinα+zcosα)\begin{pmatrix} x' \ y' \ z' \end{pmatrix} = R_y(\alpha) \begin{pmatrix} x \ y \ z \end{pmatrix} = \begin{pmatrix} x\cos\alpha + z\sin\alpha \ y \ -x\sin\alpha + z\cos\alpha \end{pmatrix}

次にX軸回転が PP' に適用され、最終的な回転点 PP'' が生成される:

(xyz)=Rx(β)(xyz)=(xycosβzsinβysinβ+zcosβ)\begin{pmatrix} x'' \ y'' \ z'' \end{pmatrix} = R_x(\beta) \begin{pmatrix} x' \ y' \ z' \end{pmatrix} = \begin{pmatrix} x' \ y'\cos\beta - z'\sin\beta \ y'\sin\beta + z'\cos\beta \end{pmatrix}

src/core/rotation.ts の実装はこれらの行列乗算を直接算術演算に展開し、行列オブジェクトのアロケーションと乗算のオーバーヘッドを回避している。1点に対する回転関数は次のように計算する:

y1=ycosα+z(sinα)z1=ysinα+zcosαx2=xcosβ+z1sinβz2=z1cosβxsinβP=(x2,  y1,  z2)\boxed{\begin{aligned} y_1 &= y \cdot \cos\alpha + z \cdot (-\sin\alpha) \ z_1 &= y \cdot \sin\alpha + z \cdot \cos\alpha \ x_2 &= x \cdot \cos\beta + z_1 \cdot \sin\beta \ z_2 &= z_1 \cdot \cos\beta - x \cdot \sin\beta \ P'' &= (x_2, ; y_1, ; z_2) \end{aligned}}

rotatePoints 関数はこの変換をすべてのタグに対して一括実行する:

export function rotatePoints(points: Vec3[], a: number, b: number): Vec3[] {
const sinA = Math.sin(a), cosA = Math.cos(a);
const sinB = Math.sin(b), cosB = Math.cos(b);
return points.map((p) => {
const y1 = p.y * cosA + p.z * -sinA;
const z1 = p.y * sinA + p.z * cosA;
const x2 = p.x * cosB + z1 * sinB;
return { x: x2, y: y1, z: z1 * cosB - p.x * sinB };
});
}

2.3 透視投影: 3D空間から2Dスクリーンへ

最後の数学的操作は、回転した3D座標を2Dスクリーン位置に変換する。ライブラリはシンプルかつ非常に効果的な投影モデルを使用し、深度に基づいてスケール係数と不透明度係数を計算する。透視深度パラメータは球半径の2倍に等しい:

per=2depth2depth+z=4R4R+z\text{per} = \frac{2 \cdot \text{depth}}{2 \cdot \text{depth} + z} = \frac{4R}{4R + z}

不透明度(alpha)係数はバイアス調整を伴う二次関係を使用する:

alpha=clamp(per20.25,  0,  1)\text{alpha} = \text{clamp}\left(\text{per}^2 - 0.25, ; 0, ; 1\right)

完全な投影パイプラインは次のように要約される:

peri=2depth2depth+ziscalei=perialphai=min(1,  max(0,  peri20.25))Pi2D=(xiscalei,  yiscalei,  scalei,  alphai)\boxed{\begin{aligned} \text{per}_i &= \frac{2 \cdot \text{depth}}{2 \cdot \text{depth} + z_i} \ \text{scale}_i &= \text{per}_i \ \text{alpha}_i &= \min\left(1, ; \max\left(0, ; \text{per}_i^2 - 0.25\right)\right) \ P_i^{\text{2D}} &= \left(x_i \cdot \text{scale}_i, ; y_i \cdot \text{scale}_i, ; \text{scale}_i, ; \text{alpha}_i\right) \end{aligned}}

TypeScriptの実装はこの数学を正確に反映している:

export function project(
points: { x: number; y: number; z: number }[],
depth: number,
): ProjectedTag[] {
const d2 = 2 * depth;
return points.map((p) => {
const per = d2 / (d2 + p.z);
const alpha = Math.min(1, Math.max(0, per * per - 0.25));
return { x: p.x, y: p.y, z: p.z, scale: per, alpha };
});
}

3. アーキテクチャとモジュール設計

コードベースは、数学的計算、レンダリングオーケストレーション、ユーザー操作処理の意図的な分離を通じて、クリーンなソフトウェアアーキテクチャを体現している。

3.1 純粋数学エンジン: src/core/

モジュール 関数 入力 出力 主要な式
distribution.ts fibonacciSphere(n, R) N, R Vec3[] ϕi=arccos(1+2i+1N)\phi_i = \arccos(-1 + \frac{2i+1}{N})
rotation.ts rotatePoints(points, a, b) Vec3[], a, b Vec3[] Ry(a)R_y(a) · Rx(b)R_x(b) · PP
projection.ts project(points, depth) Vec3[], d ProjectedTag[] per = 2d/(2d+z), α\alpha = per2^2 - 0.25

3.2 デュアルレンダリングシステム: Canvas + DOM

最も重要な革新のひとつがデュアルレンダリングシステムであり、Canvas 2D APIをテキストと画像に、DOMオーバーレイをSVG、HTML、動画、Web Componentsに使用する。

Renderer SelectionTag Types & RenderersCanvas 2D API✅ Text✅ ImagesDOM Overlay✅ SVG✅ HTML✅ Video✅ Web Components🔧 Element Tag🌐 HTML Tag🖼️ Image Tag🎨 SVG Tag📄 Text Tag🎬 Video Tag

4. パフォーマンス特性

4.1 計算量

段階 計算量 主要な演算
回転 O(N) 点あたり8乗算 + 4加算
投影 O(N) 点あたり1除算 + 2乗算 + 1加算
Zソート O(N log N) 比較ベースのソート
Canvasレンダリング O(N) fillText / drawImage呼び出し
DOM更新 O(N) transform + opacity設定

4.2 メモリ効率

N=100N = 100 タグの場合、総数値状態は約 100×(3+5)×8=6,400100 \times (3 + 5) \times 8 = 6,400 バイト — 7KB未満の浮動小数点数である。

4.3 バンドルサイズ

フォーマット サイズ
ESM (dist/index.js) ~12 KB
Gzip ~3 KB
Brotli ~2.5 KB

5. マルチモーダルコンテンツ: 6つのタグタイプ

タグタイプ 入力形式 レンダラー インタラクション ユースケース
Text string Canvas 2D onTagClickコールバック スキル、キーワード
Image { type:“image”, src, w, h } Canvas 2D タグごとのonClick ロゴ、アバター
SVG { type:“svg”, content, w, h } DOM タグごとのonClick ベクターアイコン
HTML { type:“html”, html } DOM タグごとのonClick リッチフォーマットテキスト
Video { type:“video”, src, w, h } DOM クリックで全画面 デモクリップ
Element { type:“element”, element } DOM ネイティブDOMイベント Web Components

6. cong-min/TagCloud との比較

項目 cong-min/TagCloud @xingwangzhe/tags-cloud 改善点
言語 JavaScript (ES5) TypeScript 完全な型安全性
バンドルサイズ ~6KB minified ~3KB gzipped ~50% 小型化
依存関係 0 0 ともに依存ゼロ
タグタイプ テキストのみ テキスト、画像、SVG、HTML、動画、Element 6倍のコンテンツモダリティ
レンダラー DOMのみ Canvas 2D + DOM ハイブリッド より優れたパフォーマンス
ビルドツール Rollup + Babel Vite + Bun + Oxlint モダンなツールチェーン
ドラッグ操作 基本的なマウスドラッグ アークボール風 + 慣性 より自然な操作感
タッチ対応 明示的でない 完全タッチ + モバイルCSS モバイル対応

7. 実践的な使い方

7.1 テキストのみの基本クラウド

import { TagCloud } from "@xingwangzhe/tags-cloud";
const container = document.getElementById("cloud")!;
new TagCloud(container, {
tags: ["TypeScript", "React", "Vue", "Svelte", "Node.js", "Bun", "Rust"],
radius: 250,
spinY: 0.12,
color: "#e0e0e0",
fontSize: 16,
});

7.2 マルチモーダルクラウド

new TagCloud(container, {
tags: [
"TypeScript",
{ type: "image", src: "/avatar.webp", width: 40, height: 40 },
{ type: "svg", content: `<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/></svg>`, width: 48, height: 48 },
{ type: "video", src: "/demo.mp4", width: 120, height: 68 },
],
radius: 300,
spinY: 0.15,
onTagClick(item) { console.log("Clicked:", item); },
});

7.3 ライフサイクル管理

const cloud = new TagCloud(container, { tags, spinY: 0.2 });
cloud.setTags(["New", "Set", "Of", "Tags"]);
cloud.pause();
cloud.resume();
cloud.destroy();

8. 結論: レンダリングエンジンとしての数学

@xingwangzhe/tags-cloud は、ソフトウェアにおける最も強力なレンダリングエンジンがGPUでもシェーダコンパイラでもフレームワークでもなく、数学であるという説得力のある実証である。3Dタグクラウド可視化の問題を3つの純粋な数学的操作 — Fibonacci球面分布、回転行列乗算、透視投影 — に分解することで、このライブラリはWebGLベースの代替手法と視覚的に区別がつかない結果を、それらの複雑さ、ハードウェア依存性、バンドルサイズを一切必要とせずに達成している。

このプロジェクトのオープンソースエコシステムへの貢献は3つある。アーキテクチャ的には、ハイブリッドCanvas/DOMレンダラーが3KBのパッケージでパフォーマンスと汎用性の両方を提供できることを証明した。数学的には、基本的な3Dグラフィクスアルゴリズムのクリーンで十分にドキュメント化された実装を提供している。実用的には、リッチメディア対応、TypeScriptの安全性、モバイル対応のインタラクションを備えた3Dタグクラウド作成用のドロップインコンポーネントを、単一の依存関係も追加せずに開発者に提供している。


End of Article — Based on source code analysis of @xingwangzhe/tags-cloud v0.9.0

このページをシェア