TikZ ライブフィギュア
ライブフィギュアとは、読者が操作できる TikZ 図面です。tikz-dl-visualizer の静的フィギュアと同じく「一つのアイデアを明確に」を目的としますが、読者は実際に操作してアイデアが応答する様子を観察できます。つまり、始点をドラッグするとモーメンタムがジグザグを打ち消す様子を見たり、温度を下げるとソフトマックスが argmax に収束する様子を見たりできます。
すべての図は次の 2 つのファイルで構成されます:
| ファイル | 担当 | コンパイル元 |
|---|---|---|
figure.tex | 決して変化しないものすべて:軸、等高線、グリッド、固定値の行列、ラベル、数式、そしてライブテキスト用のスロット | pdflatex(1回) |
figure.js | 変化するものすべて:パラメータ、ハンドル、ホットスポット、純粋な compute(state)、および draw(d, state, c) | ブラウザ(変更のたび) |
scripts/build.py は .tex を2回(通常と、ライブテキストをID色で出力してグリフを切り出すため)コンパイルし、TikZ 座標フレームと名前付きノードボックスをエクスポートし、Node 上で仕様を実行して次を書き出します:
figure.html:自己完結型の 1 ページ(ネットワーク不要)。開くもよし、iframe するもよし、公開するもよし。figure.pngおよびfigure.<snapshot>.png:デフォルト状態と各スナップショット。ブラウザなしでレンダリングされます。これらがあなたの作品を確認する手段です。figure.svg:デフォルト状態の静的 SVG。論文やフォールバック用。
同梱:
assets/dlviz.sty:パレットと TikZ スタイル(tikz-dl-visualizer と同じファイル)assets/dllive.sty:TeX 側、exportキーとlive=キー、\dlnumassets/dllive.js:ランタイム(座標マップ、描画API、スライダー、ハンドル、ホットスポット、再生)assets/examples/momentum/:パラメータ、ドラッグ可能なハンドル、再生ボタン、ライブ数値、移動ラベルassets/examples/attention/:エクスポートされたセル、ホバーホットスポット、ヒートマップ、29個のライブ数値、曲線references/api.md:完全なAPI(TeXキー、仕様フィールド、描画呼び出し)。セッションで最初の仕様を書く前に読んでください。references/gotchas.md:遭遇した問題とその修正。何かおかしいときは読んでください。
視覚言語自体(色の役割、表記法、レイアウト、注釈)については、tikz-dl-visualizer に従います:その references/visual-language.md と references/archetypes.md がそのまま適用されます。このセッションでまだ読んでいなければ読んでください。
ワークフロー
1. インタラクション文を書く
コードを書く前に、「読者はXをするとYが見え、それはZを示す」 という形式の一文を書きましょう:
- 「始点をドラッグしてβを変更すると、谷を横切るジグザグが打ち消され、それに沿った運動が蓄積される。βが大きすぎるとオーバーシュートする。」
- 「トークンをホバーすると、その行の A は重みの集合であり、出力行は V の行の加重平均であることがわかる。τを下げると重みは1つの行に集中する。」
もし Y が静的フィギュアでも同じくらい明確なら、静的なもの(tikz-dl-visualizer)を作成してください。コントロールは 1 〜 3 個にします。各コントロールは目で追える変化を生み出さなければなりません。その効果が見えないスライダーはノイズです。
2. 静的要素と動的要素を分割する
紙に何が変化するかを描きます。それ以外はすべて .tex に置きます。典型的な分割:
- 静的:軸、等高線、グリッド、セルの枠線、固定行列、トークンラベル、数式、
\beta = {}のような読み出しラベル、凡例の線。 - 動的:パス、点、ハイライト、ヒートマップの塗りつぶし、矢印、減衰、および数値(
\dlnumスロットとして、本物の TeX グリフを維持)。 - 移動する必要があるラベル(たとえばハンドルに付随するもの)も
live=ノードとし、d.label(id, {from, to})で移動します。
3. figure.tex を書く
最も近い例から始めてください。必須:
\documentclass[tikz,border=8pt]{standalone}
\usepackage[dark]{dlviz} % [light] は白いページ用
\usepackage{dllive}
\begin{document}
\begin{tikzpicture}[x=1.15cm, y=1.15cm]
\useasboundingbox (xmin,ymin) rectangle (xmax,ymax); % 必須:ライブ描画はこの内部に収まる必要があります
...静的な描画...
\node[cell=coloraqua, export] (A-1-1) at (..) {}; % export: JSがこのボックスを参照できます
\node[fit=(Q-1-1)(Q-1-3), inner sep=0, export] (Qrow-1) {}; % ホットスポット用の不可視ボックス
\node[anchor=base west, inner sep=0, text=coloraqua] (b) at (..) {\beta = {}};
\node[live=beta, anchor=base west, inner sep=0, text=coloraqua] at (b.base east) {\dlnum};
\end{tikzpicture}
\end{document}
- スケールは
x=/y=で設定してください(scale=ではありません)。これにより JS の 1 単位が TikZ の 1 単位になります。 - ライブ数値ノードにはちょうど
\dlnumが含まれます。色はtext=、サイズはfont=で設定します;ノード内の色コマンドはそれを壊します。 - 表のセルに数値を配置する場合は、セルの中心に
anchor=mid westを指定し、d.number(id, v, {at: "cell"})で中央揃えにします。\matrixやレイアウトが重要な場所では\dlnumin{2em}を使用してスロットがその幅だけを予約するようにします。 - 図が何かが選択されている場合にのみ意味を持つ場合は、その状態で開きます:
initial: {hover: "..."}とstickyHover: true。 - JS がループ処理できるように名前を付けます(
A-\i-\j、Qrow-\i)。 - tikz-dl-visualizer と同様に、先頭に
% Caption:コメントを入れます。
4. figure.js を書く
DLLive.figure({
title: "...", caption: "...操作方法を一文または二文で...",
params: { beta: { label: "β", min: 0, max: 0.99, step: 0.01, value: 0.6 } },
handles: { w0: { value: [-4, 1.5], min: [-5.2, -1.85], max: [5.2, 1.85] } },
hotspots: /^Qrow-\d$/, // state.hover を設定するエクスポートされたノード
play: { param: "t", ms: 110 }, // オプション:隠しパラメータをアニメーション化
compute: (s) => ({ path: run(s.beta, s.w0) }), // 純粋な数学、メモ化
draw(d, s, c) { // TikZ 単位、dlviz 色
d.path(c.path, { color: "aqua", width: "thick", dots: true });
d.number("beta", s.beta, { digits: 2 });
},
snapshots: [{ name: "overshoot", beta: 0.95 }], // 確認用の追加 PNG
tests: [{ name: "matches numpy", state: { beta: 0.4 }, check: (c) => Math.abs(c.path[30][0] - 1.036e-5) < 1e-8 || `got ${c.path[30]}` }],
});
- 数学は
computeに存在し、正しくなければなりません。 まず Python(numpy)でリファレンスを書き、Python で計算した数値と JS を比較するtestsエントリを追加してください。頭の中で書いた数学を決して信用しないでください。 drawは描画のみを行います:状態変更やランダム性はなし(ノイズが必要ならcompute内でシード付き RNG を使用)、すべてのフレームで実行されます。- dlviz の色ロール(
aqua、red、orange!55!bg...)と TikZ の線幅(thin、thick、very thick)を使用し、オーバーレイが元の図と調和するようにします。 - 重要な状態ごとにスナップショットを追加します:各スライダーの極端な値、ホバー、アニメーション途中。これにより隠れた重なりを発見できます。
5. ビルド
uv run --with pymupdf python <skill-dir>/scripts/build.py path/to/figure.tex
エクスポートされたノード名とライブスロット、ホットスポットが一致したノード数、各テストの PASS/FAIL、ページ外への描画や TeX テキストが端で切れている場合の WARN が出力されます。エラーは原因となったスロット、ノード、パラメータを特定します。.js のみを変更した後は --no-tex を追加してください(約 1 秒)。
6. 確認、批評、反復
書き出されたすべての PNG を開き(Read ツール)、静的フィギュアと同様に以下を確認します:
- ライブ部分は元の図と一致していますか(パスは正しい点から始まり、数値はセルに収まり、ハイライトは行を囲んでいますか)?
- すべてのスナップショットで、重なりはありませんか(パスが読み出しを横切る、曲線が数値と重なる、テキストがセルからはみ出す、端でクリップされる)?
- コントロールの極端な値でも適切に表示されますか(β = 0.99、τ = 0.2、ハンドルが隅にある)?
- デフォルト状態で 3 秒以内にアイデアを見つけられますか?デフォルト状態はサムネイルとざっと見た読者が見るものです。
- 色の役割は、元の図とオーバーレイの両方で維持されていますか?
修正して再ビルドします。通常 2 〜 3 回のラウンドが必要です。
次に、ブラウザがあれば実際のページを試します(組み込みブラウザペイン):通常 file:// を拒否するので、フォルダをサーブしてください。たとえば、.claude/launch.json エントリ {"name": "fig", "runtimeExecutable": "python3", "runtimeArgs": ["-m", "http.server", "8800", "--directory", "<folder>"], "port": 8800} と preview_start を使用します。ドラッグ、スライド、ホバー、再生を行い、コンソールで DLLive.state() で現在の状態を確認します。スナップショットには破線やホバーカーソルは表示されません。
7. 納品
ユーザーに PNG(デフォルト状態)、figure.html へのパス、および短い読み物ガイド(最初に何を試し、何に注目するか:「w₀ を急な方向にドラッグしてから β を上げる...」)を提供します。共有可能なリンクが必要な場合は、HTML を Artifact として公開します。自己完結型なので追加ファイルは不要です。自然な次のコントロールや図を 1 行で示します。
埋め込み
- 1 ページに複数の図、または独自のライト/ダークテーマを持つページ(tikz-explorer-book は
tools/fig.shを通じてこれを行います):--themes dark,light --embedでビルドします。これにより、両方のテーマで元の図がコンパイルされ、figure.live.html(<div class="dl-live">と<script>DLLive.load("name", ...)</script>を含むフラグメント)が書き出されます。ホストページはフラグメントの前にassets/dllive.jsを 1 回インクルードし、assets/template.htmlで「live figure」とマークされた CSS ルールを含めます。--show-d/--show-lで現在のテーマの元の図を表示します。ランタイムはページテーマ(<html>のdata-theme="dark|light"、またはシステム設定)に従い、オーバーレイを再色付けします。-light.pngも書き出されます。 - スタンドアロンまたは Artifact:
figure.htmlをそのまま使用します。--themes dark,lightを指定するとビューアのテーマに従います。指定しない場合は.texのテーマを維持します。 - スライド:静的スライドには
figure.png/figure.svgを使用し、HTML にリンクします。 - 論文:
figure.svgまたは.dllive/内の元の図の PDF を使用します。静的なデフォルト状態はそれ自体で完全な図です。印刷専用の図には、tikz-dl-visualizer の方が適しています。