# docs/figures —— 论文插图 每张图只输出 PNG。数据由 `scripts/build_figure_data.py` 从行为日志、覆盖计划与生产求解器 算好写入 `docs/figures/.data/*.mat`,再由 MATLAB(`scripts/matlab/*.m`)排版出图; `manifest.json` 记录每张图的数据来源(路径 + sha256 前 12 位 + 行数)与生成命令。 本目录的 11 张 PNG 全部进入论文正文:`docs/paper/main.tex` 用 `\graphicspath{{../figures/}}` 直接引用,不在论文里重绘,也不手抄图内数字。 其中回放画布两张与实测计时一张取自裁判机演练留档, 论文图注均标注「演练、非正式成绩」;问题一的三点交会算例取自演练观测。 ```bash uv run python scripts/build_figure_data.py # 全部 11 张(数据 + 出图) uv run python scripts/build_figure_data.py --prepare # 只写 .mat 数据 uv run python scripts/build_figure_data.py --list # 图名 uv run python scripts/build_figure_data.py --only geo-q3-seven-point-coverage ``` MATLAB 内手动出图:`cd scripts/matlab` 后执行 `run_all_figures`(每张图单独一个会话, 本机图形子系统连续渲染多张会崩)。 依赖:`numpy`、`scipy`(写 `.mat`)与 MATLAB R2025a。 ## 约定 - **除回放画布(点运动图 `replay-webui-*`)外,全部使用 MATLAB 默认图形样式**: 默认配色(`defaultAxesColorOrder`)、默认字体与字号、默认坐标轴与图例; 不引入自定义调色板,不手绘图例色块。 - 图内不放标题、图注与解释性文字,只保留坐标轴、图例与必要的元素标注; 图的含义由文件名与下表承担,解释写在论文正文。 - 图例一律用 MATLAB `legend` 放在坐标区外(`southoutside`),不压数据; 散点标签按候选方位自动避让,保证互不重叠。 - 输出 PNG(`exportgraphics`,300 dpi、白底),导出前关闭坐标轴工具条。 ## 图清单 | 文件 | 内容 | 数据来源 | 论文位置 | | --- | --- | --- | --- | | `flow-overall-architecture.png` | 四层主线:题面抽象(L0)、在线三层(L1)、四问主线(L2)、三条闭环(L3) | `src/mcm26b/config.py`、`docs/B题建模分析与实施计划.md` | 图 1(问题分析) | | `steps-state-machine.png` | 会话状态机(IDLE / 倒计时 / OPEN / CLOSED) | `docs/协议规则表与状态机.md`、`src/mcm26b/protocol/client.py` | 图 2(计时模型) | | `timing-attachment-example-105-111-194-199.png` | 附件计时示例:逐动作耗时构成与单步合计 | `docs/协议规则表与状态机.md` | 图 3(计时模型) | | `timing-measured-q3-10-sources.png` | 实测一局:累计虚拟时间按动作类型的堆叠构成 | `referee-data/sessions/BBM9-HZG2-77N5-NKKC/log.jsonl` | 图 11(时间分解,演练局) | | `geo-q1-convex-intersection-zoom.png` | 问题一:角域交会总览 + 定位区域放大(直径圆与最小包围圆) | `output/q1/practice/U5ZW-P9KN-TCAG-R7ST_observations.json` | 图 4(a)(问题一,演练观测) | | `geo-q1-diameter-circle-counterexample.png` | 问题一:直径圆不覆盖定位区域的反例(等边三角形布站) | `examples/q1/triangle.json` | 图 4(b)(问题一解析反例) | | `geo-q2-guaranteed-reception-disk.png` | 问题二:截断角域外接圆 `B(c, a)` 与安全候选盘 `B(c, 1000 − a)`,右侧为半径分解 | `src/mcm26b/config.py`、`docs/algo-2.md` | 图 5(问题二) | | `geo-q3-seven-point-coverage.png` | 问题三:七个 1000 m 接收盘覆盖 Ω,标出最差点 | `src/mcm26b/planner/seven.py`、`docs/algo-3.md` | 图 6(问题三) | | `geo-q4-minimum-cover-31-points.png` | 问题四:31 点最小命中集与 ±90° 前向半盘语义 | `src/mcm26b/guarantee/plans/q4_mincover_v2.json` | 图 8(问题四) | | `replay-webui-q3-omnidirectional.png` | 回放画布(点运动图,全向局):世界视图 + 事件流水 + 虚拟时间轴 | `referee-data/sessions/BBM9-HZG2-77N5-NKKC/log.jsonl` | 图 7(问题三,演练局) | | `replay-webui-q4-directional.png` | 回放画布(点运动图,定向源局):世界视图(含定向源 ±90° 前向半盘)+ 事件流水 + 虚拟时间轴 | `referee-data/sessions/GHSN-M38E-74GD-YLUD/log.jsonl` | 图 9(问题四,演练局) | 后两张是唯一保留自定义画布的图:把回放器画布上的 Ω 与真值源、移动轨迹、 检测示向度射线、定向源朝向与清除标记,连同右侧事件流水与底部虚拟时间轴一起导出; 定向源局额外画出每个定向源的 **±90° 前向半盘**(半径取该源 `r_eff_m`,朝向箭头即半盘半径)。 q4 回放固定用演练局 `GHSN-M38E-74GD-YLUD`(`build_figure_data.py::Q4_REPLAY_CASE`); 本机没有该局留档时回退到自动挑选,因此 `examples/replay/` 里归档了这一局。 ## 数据来源与复现 - `timing-measured-*`、`replay-webui-*` 读评测机行为日志(JSONL,含真值)。 `referee-data/` 被 `.gitignore` 忽略,因此另归档两局到 `examples/replay/`; 没有评测机数据时脚本自动改读归档,图一致。 ```bash uv run python scripts/archive_replay_logs.py --list # 本机有哪些局 uv run python scripts/archive_replay_logs.py <案例编码> # 追加归档 ``` - `geo-q1-convex-intersection-zoom.png` 读官方演练留档 `output/q1/practice/*_observations.json`(同样被忽略)。文件缺失时改读 `examples/q1/triangle.json` 反例算例,图仍可用,只是案例不同。 - 问题一的几何量由生产求解器 `mcm26b.geometry.solve` 现算; 问题三、问题四直接读覆盖计划与证书,不手抄数字。