TikZ 互動式圖形
互動式圖形是讀者可以改變的 TikZ 圖畫。其要點與 tikz-dl-visualizer 中的靜態圖形相同(一個想法,一目了然),但讀者現在親自操作並觀察想法如何回應:拖曳起點觀察動量如何消除鋸齒,降低溫度觀察 softmax 如何收斂到 argmax。
每個圖形包含兩部分:
| 檔案 | 內容 | 編譯方式 |
|---|---|---|
figure.tex | 所有永不改變的部分:座標軸、輪廓、網格、固定值的矩陣、標籤、方程式,以及用於動態文字的插槽 | pdflatex,一次 |
figure.js | 所有會變化的部分:參數、拖曳點、熱區、純函數 compute(state) 以及 draw(d, state, c) | 瀏覽器,每次變更時 |
scripts/build.py 會將 .tex 編譯兩次(一次正常編譯,一次將動態文字以 ID 色輸出以便裁切那些字形),匯出 TikZ 座標框架和已命名的節點框,在 node 下執行規格,並寫入:
figure.html:一個自包含的頁面(無需網路)。直接開啟、嵌入 iframe 或發佈。figure.png及figure.<快照>.png:預設狀態與每個快照,無需瀏覽器即可渲染。這些是你檢視自己成果的方式。figure.svg:預設狀態的靜態 SVG,用於論文和備用方案。
內附:
assets/dlviz.sty:色板與 TikZ 樣式(與 tikz-dl-visualizer 相同)。assets/dllive.sty:TeX 端,export和live=鍵值與\dlnum。assets/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」的句子:
- 「拖曳起點並改變 β:穿過山谷的鋸齒路徑被抵消,而沿山谷方向的運動則累積;β 太大會導致過衝。」
- 「懸停在一個 Token 上:其 A 的行是權重集合,其輸出行是 V 的那些行的加權平均。降低 τ,權重會收斂到一行。」
如果 Y 在靜態圖形中同樣清晰,請製作靜態圖形(tikz-dl-visualizer)。使用一到三個控制項。每個控制項必須改變眼睛可以追蹤的東西;效果看不見的滑桿只是雜訊。
2. 拆分靜態與動態
在紙上畫出會變化的部分。其他所有內容都放在 .tex 中。典型的拆分方式:
- 靜態:座標軸、輪廓、網格、儲存格邊框、固定矩陣、Token 標籤、方程式、讀出標籤(如
\(\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)。 - 在頂部加上
% Caption:註解,如同 tikz-dl-visualizer。
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) 撰寫參考,然後加入tests條目,將 JS 與你在 Python 中計算的數值進行比較。永遠不要相信腦袋裡寫出來的數學。 draw只負責繪圖:沒有狀態改變,沒有隨機性(如果需要雜訊,請在compute中使用種子隨機數產生器),且每一影格都會執行。- 使用 dlviz 顏色角色(
aqua、red、orange!55!bg...)和 TikZ 寬度(thin、thick、very thick),讓疊加層看起來像框架。 - 為每一個重要的狀態加入快照:每個滑桿的極端值、懸停、動畫中間。這是重疊隱藏的地方。
5. 建置
uv run --with pymupdf python <技能目錄>/scripts/build.py 路徑/到/figure.tex
它會輸出匯出的節點名稱和動態插槽、熱區匹配的節點數量、每個測試的 PASS/FAIL,以及繪圖超出頁面或 TeX 文字在邊緣被截斷時的 WARN。錯誤會指出是哪個插槽、節點或參數的問題。如果僅更改了 .js,加上 --no-tex(約需 1 秒)。
6. 檢視、評論、迭代
開啟它寫出的每個 PNG(使用 Read 工具)並檢查,如同靜態圖形:
- 動態部分是否與框架對齊(路徑起點在正確的點上,數字在其儲存格內,高亮貼合其行)?
- 在每個快照中,是否有任何重疊:路徑穿過讀出標籤、曲線跨過數字、文字超出儲存格、某個元素在邊緣被裁剪?
- 控制項的極端值是否仍然可讀(β = 0.99、τ = 0.2、拖曳點在角落)?
- 在預設狀態下,眼睛能否在三秒內找到想法?預設狀態是縮圖和快速瀏覽的讀者所看到的。
- 顏色在框架和疊加層中是否保持其角色?
修正並重新建置。通常需要兩到三輪。
如果你有瀏覽器,然後嘗試實際頁面(內建的 Browser 窗格):它通常拒絕 file://,因此提供該資料夾的服務,例如使用 .claude/launch.json 條目 {"name": "fig", "runtimeExecutable": "python3", "runtimeArgs": ["-m", "http.server", "8800", "--directory", "<資料夾>"], "port": 8800} 和 preview_start。拖曳、滑動、懸停和播放;在控制台中,DLLive.state() 顯示目前狀態。快照不會顯示虛線或懸停游標。
7. 交付
提供使用者 PNG(預設狀態)、figure.html 的路徑,以及一個簡短的閱讀指南:首先嘗試什麼以及注意什麼(「將 w₀ 拖入陡峭的方向,然後提高 β...」)。如果他們想要可分享的連結,請將 HTML 發佈為 Artifact;它是自包含的,因此不需要額外檔案。用一句話提及自然的下一個控制項或圖形。
嵌入
- 單一頁面上的多個圖形,或擁有自己淺色/深色主題的頁面(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,以及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 是更好的工具。