# Axler Visual

Interactive companion to Sheldon Axler's *Linear Algebra Done Right* (4th ed).
Each section becomes a single self-contained HTML page: Q&A + symbol glossary +
2D/3D interactive canvases + 8 self-test exercises.

## 目录结构

```
axler_visual/
├── shared/
│   ├── style.css          ← 全部样式（暗色主题 + Q&A + 练习 + 符号表）
│   ├── utils.js           ← fmt, cfmt, 安全 DOM 构造, 向量/矩阵小工具
│   ├── eigen.js           ← eig2, eig3General, eig3Symmetric
│   └── canvas.js          ← Canvas2D 类（2D 可视化）+ Scene3D 类（Three.js 脚手架）
├── templates/
│   └── section-template.html  ← 新章节的空模板
├── index.html             ← 所有已做页面的导航
├── 5A_invariant_subspaces.html   ← v1 独立版（内嵌所有 CSS/JS）
├── 5D_diagonalization.html       ← v1 独立版
├── 5D_v2_fresh.html              ← v2 独立版
├── 7_spectral_theorem.html       ← v2 独立版
└── 3A_linear_maps_demo.html      ← 基于 shared/ 的参考实现
```

## 做一节新页的流程

1. `cp templates/section-template.html NEW_SECTION.html`
2. 替换标题栏、TOC 标签、`<h1>` 里的 `XXX Title` / `pp.XXX-YYY`
3. 在符号表 `<table class="glos">` 里填本节会用到的符号
4. 写 Q1-Q15 + 8 道练习（保留 Q4 的 2D 画布和 Q8 的 3D 画布占位，如不需要 3D 可整段删）
5. 在底部 `<script>` 里把 `renderSpec2d` / `renderSpec3d` 扩充成本节需要的读数
6. 本地看一眼：`python -m http.server 8766` 然后打开
   `http://127.0.0.1:8766/NEW_SECTION.html`

## shared/ 暴露的 API

### `utils.js`（全局函数）

- `fmt(x, d=3)` — 数字格式化，trim 末尾 0
- `cfmt(re, im, d=3)` — 复数格式化
- `clearOut(el)` — 清空 DOM 元素
- `addText(el, t)` / `addLine(el, t)` — 追加文本节点
- `addBold(el, t)` / `addBoldLine(el, t)` — 追加粗体
- `addSpan(el, t, cls)` — 追加带 class 的 span
- `apply2x2(a,b,c,d, x,y)` / `apply3x3(M, v)` — 应用矩阵
- `norm2`, `norm3`, `normalize2`, `normalize3`
- `symmetrize3(M)` — 对称化 3×3 矩阵
- `isSymmetric2x2`, `isNormal2x2`

### `eigen.js`

- `eig2(a, b, c, d)` → `{real, vals, vecs}`，2×2 解析解
- `eig3General(M)` → `[{val, vec}, ...]`，仅返回实特征值和向量（Cardano + 零空间）
- `eig3Symmetric(A)` → `{vals, vecs}`（降序，正交规范），Jacobi 迭代

### `canvas.js`

**Canvas2D 类** — 一次 `new` 拿到一个可编程 2D 画布：

```js
const cv = new Canvas2D('c2d', { range: 3.5 });
cv.setMatrix([a, b, c, d]);     // 更新矩阵并重绘
cv.redraw = (ctx, c) => {       // 自定义绘制钩子（可选）
  c.clear();
  c.drawGrid();
  c.drawEigenlines();
  c.drawUnitCircle();
  c.drawImageEllipse();
  c.drawArrows();
  // 加你自己的图层...
};
cv.onClick((x, y) => {...});     // 画布点击
```

内置图层：`clear`, `drawGrid`, `drawDeformedGrid`, `drawEigenlines`,
`drawUnitCircle`, `drawImageEllipse`, `drawArrows`。坐标映射：`w2p(x,y)`、
`p2w(px,py)`。矩阵应用：`apply(x, y)`。

**Scene3D 类** — Three.js 场景脚手架：

```js
const sc = new Scene3D('three3d');
sc.setUnitCubeWire();            // 蓝色线框单位立方体
sc.onUpdate = (M) => {           // 每次 setMatrix 后执行
  sc.setTransformedCube(M);
  const eigs = eig3General(M);   // or eig3Symmetric
  sc.setEigenAxes(eigs);
};
sc.setMatrix(M);
```

自带：AxesHelper、GridHelper、环境光/平行光、OrbitControls、<kbd>Space</kbd> 暂停、
<kbd>R</kbd> 复位相机。

## 设计约定

### 颜色（CSS 变量）

- `--accent` 琥珀色 `#f5b942`：特征值、主要强调、按钮高亮
- `--cyan` 蓝色 `#5cb3ff`：普通向量、类型提示、"A" 徽章
- `--eig` 黄色 `#ffd166`：特征方向（高亮版）
- `--red` 红色 `#ff6b6b`：复特征/病态
- `--teal` 青色 `#4dd0e1`：轨迹
- `--violet` 紫色 `#c792ea`：正交规范基
- `--good` 绿色 `#66bb6a`：确认状态

### Q&A 节拍

- 每节 12-15 个 Q，不要超过 20
- Q1 引入概念、Q2 给动机、中段交互、Q13 定理清单、Q14 应用、Q15 下一步
- 交互 Q 放在 Q4-Q5 和 Q8-Q10 之间（别放开头，先建立概念再看图）

### 练习

- 8 道，难度分布：★ 2-3 题 · ★★ 3-4 题 · ★★★ 1-2 题
- 每题都有「提示」和「答案」两个 `<details>` 可展开
- 答案里一定给完整推理，不是只给结论

## 运行

```bash
cd axler_visual
python3 -m http.server 8766
# 浏览器打开 http://127.0.0.1:8766/index.html
```

或者直接双击 HTML 文件（MathJax/Three.js 通过 HTTPS CDN 加载，不受 file:// 限制）。

## 生成新页的参考成本

以每节 ~60KB HTML（用 shared/ 后大概 45KB）估算：

| 模型 | 单节成本 | 全书 34 节估 |
|---|---|---|
| Opus 4.7 一次到位 | ~$3 | ~$100 |
| Sonnet 4.6 一次到位 | ~$1 | ~$35 |
| 迭代/精修（每节 8-10 轮）| ~$10-15 | ~$350-500 |
