可编辑的学术论文 draw.io 图形
您从 Python 构建脚本(记录所有坐标、角色和尺寸的可追溯产物,因此图形可以重新生成和重新主题化)中构建图形,作为原生 draw.io 对象。绝不追踪像素,绝不栅格化文本。每次调用和规则的详细信息:REFERENCE.md。可供参考的模式:EXAMPLES.md。
运行工具
所有代码都封装在一个文件中。下文的 T 表示 python drawio_toolkit.py:
T <工具> [参数] # 例如 T lint_drawio fig.drawio --theme theme.yaml (任何工具:--help)
T unpack ./tidyfigs # 可选:标准文件夹 scripts/ assets/ examples/;然后 python scripts/<工具>.py
在 Python 中:import drawio_toolkit; drawio_toolkit.setup() → from drawio_kit import Diagram,
from figlib import *,from drawio_doc import Doc。生成的构建脚本会自动找到库。
没有可用的代码执行环境? 仍遵循相同的方法:为用户编写构建脚本(对于小图形,也可以直接编写 draw.io XML — REFERENCE §14),让他们运行,并要求他们提供预览/lint 输出或截图,而不是您自己查看。
选择模式
| 输入 | 模式 | 输出 |
|---|---|---|
| 栅格图像待重现 | A 图片 | <img>.drawio + <img>#drawio.png (300 dpi),位于图片旁边 |
| 散文/论文章节/公式 | B 文字 | 3 个原型 → 用户选择 → <名称>.drawio |
| 现有 .drawio 文件出版就绪 | C 增强 | <名称>#ENHANCED.drawio + PDF/SVG/PNG 导出 |
A 或 B 模式之后经常接着 C 模式。仅在请求确实不明确时才询问模式。
共享规则(所有模式)
- 仅使用主题角色。 颜色、字体、大小、描边、透明度来自一个 YAML/TOML/JSON 主题(
fill="primary",size="body",sw="thick",fill_opacity="tint")。色调 = 现有角色 + 透明度角色,而非新的十六进制色值。需要新颜色?添加语义角色(highlight,chunk_b)并告知用户。Lint 报告每个偏离主题的值;最终版本中不应存在任何偏离主题的值。 - 根据意图标准化:同级元素使用相同尺寸,行/列使用一个间隙(
row,row_widths,fit_row,column,grid_boxes),共享边使用一个坐标;标签中心对齐其所标识的内容(x + w/2)。忽略 ±1 像素的噪声。 - 附加连接器:当线条接触形状时,使用
src/tgt(+exit/entry比例)。pts仅列出 draw.io 无法计算的内容(自由起点、路径点、自由终点)。仅对分隔线、坐标轴、非附着向量使用自由点。 - 曲线边(
route="curved",flex_arrow,cycle):路径点是控制点;曲线通过连续路径点之间的中点。 - Z 轴顺序 = 创建顺序(后者在上):画布 → 面板 → 填充 → 形状 → 连接器 → 文字 → 注释。将一起移动的内容分组(
with d.group("tile"))。 - 稳定的 ID:重要顶点需要(边缘依赖,lint 可读)。
- 文本标记:
**粗体***斜体*__下划线__{角色|着色}\n\*;行内 LaTeX\( … \);显示数学使用d.equation(r"…")(原生 MathJax,可编辑)。raw=True传递 HTML。 - 通过查看验证:每次构建后,使用您的图像工具查看 PNG 预览;数字(lint、分数、审计)提供指导,视觉检查决定。预览近似于 draw.io:emoji 显示为灰色方块,圆柱体/文件显示为矩形,没有小型大写字母,数学通过 mathtext 显示。
- 工作区卫生:临时脚本、探测和裁剪文件放入
<工作目录>/scratch/,绝不放在用户的文件旁边。 - 学术默认设置:白色背景,图形内无标题(改为起草图注),≤ 3 种色调 + 色调变体,颜色绝不是唯一的编码方式,一种字体系列,≤ 6–7 个尺寸角色,≤ ~25 个文本项。
模式 A — 图片 → 可编辑 draw.io
方法:测量 → 规划 → 主题化 → 构建脚本 → 渲染 → 比较 → 修复,直至预览在布局上与源图像匹配。坐标是在真实网格上测量的源图像像素(目测缩放后的视图是转换质量不佳的首要原因)。
A0 预分类(确定工作量;首先阅读 EXAMPLES.md 中最相似的示例):
| 层级 | 外观 | 主要技术 | 迭代次数 | 最终分数 |
|---|---|---|---|---|
| 1 简单 | 方框、标签、直线/肘型箭头、1–3 个面板 | rect,text,已附着的 edge,row | 2–3 | ≥ 0.88 |
| 2 网格/仪表盘 | 带有图标、列、微型流程、项目符号的图块 | panel,分组,fit_row/grid_boxes,icon,正交边缘 | 3–5 | ≥ 0.88 |
| 3 绘图/几何 | 坐标轴、向量、点、圆形、波浪箭头、旋转标签 | dot,circle,锚点,flex_arrow,曲线边,rotation | 3–6 | ≥ 0.85 |
| 4 复杂 | 3D 表面、网格、照片、密集散点、标注 | surface_mesh,polygon,image_crop,scatter_*,callout,Z 轴分层 | 5–10 | ≥ 0.78 |
A1 创建框架 + 检查:T new_build images/fig.png [--theme user.yaml] [--scale 1] 创建
images/fig_drawio_work/ (build.py,theme.yaml,NOTES.md,inspect/,iterations/,scratch/,
history.json;保留它)。读取 inspect/summary.md并查看 source.grid*.png(位置),
source.boxes.png/.json(精确边界框 + 中值颜色 — 直接使用),source.ocr.png/.json(文本 +
字体大小:使用宽度匹配的 est_px_if_regular/bold,通过查看选择),source.palette.png
(颜色 → 最近角色,ΔE)。框架会在 build.py 中预先填充检测到的框 — 完善它们。
放大任意区域:T inspect_image zoom SRC --box x0 y0 x1 y1 --factor 3 --step 10;数值上探测细/浅色特征
(T inspect_image probe SRC --box … 或 numpy,REFERENCE §6.1)。OCR 可能误读,检测器可能合并接触形状 — 务必视觉检查。
A2 在 NOTES.md 中规划:按 Z 轴顺序逐个区域清点清单。决定每个元素:
原生顶点(方框、面板、药丸形、圆形、菱形、圆柱体)· 原生文本(所有文本,始终如此)·
已附着的 edge(连接)· 自由 edge(坐标轴、分隔线)· flex_arrow(宽/波浪)· 分组中的 dot(散点 ≤ ~400)·
polygon/svg_layer/surface_mesh(斑点、光束、网格、3D)·
image_crop(仅限照片、纹理、徽标 — 在其上原生重建其标签)· 库形状或 emoji icon(图标;说明是哪个)。记录每个非原生决策以备报告。
A3 主题化:用户颜色/字体 → 角色,将每个源颜色映射到最近的角色;否则将源的主色去重为 ≤ 6–8 个角色。将 OCR 发现的大小聚类为 ≤ 6–7 个尺寸角色(相同视觉级别 ⇒ 相同角色)。最接近的网页安全字体族(Helvetica/Arial;对于 LaTeX 风格衬线字体使用 Times New Roman);对于 Roboto/Inter 使用 google: true。通过放大测量描边粗细。
A4 编写 build.py(Diagram.from_source 已预连接;完整 API 参考 §2):
pid, (cx, cy, cw, ch) = d.panel(52, 110, 620, 570, "**AUTO-ENCODING**", header_h=70, header_fill="primary",
header_color="background", title_size="heading")
for (x, y, w, h), t in zip(d.row_widths(182, 336, [97, 138, 97, 97], 62, gap=13), ["[A]", "[MASK]", "[C]", "[D]"]):
d.rect(x, y, w, h, t, fill="token", stroke="text", rounded=4, size="body", id="tok_" + t.strip("[]"))
d.rect(275, 455, 170, 62, "**Encoder**", fill="primary", color="background", rounded=4, id="enc")
d.edge([], src="enc", tgt="tok_MASK", sw="thick", exit=(.5, 0), entry=(.5, 1))
d.edge([(108, 486)], tgt="enc", entry=(0, .5)) # 自由起点,附着终点
d.text("Dimension 2", 365, 144, size="small", rotation=-90)
d.image_crop((49, 199, 143, 309), border="text") # 从源图像复制的照片
随机内容(散点)使用固定 seed 和标签周围的保留矩形。> ~600 个顶点 → 将装饰性标记合并为一个 svg_layer。微小的源 → --scale 2 以使文本 ≥ 10 px。
A5 迭代:T iterate images/fig_drawio_work --note "更改了什么" = 构建 → 渲染 → 比较 →
Lint → 快照 + 历史。查看 iter_NN_side.png(所有内容:措辞、大小、粗细、换行),
iter_NN_edges.png(红色 = 仅有源,青色 = 仅有预览;移动直到对合并),
iter_NN_heat.png + 最差单元格(接下来放大这些)。分轮次工作:(1) 画布、面板、网格 →
(2) 形状对齐到检测到的边界框并标准化 → (3) 每个连接器附带端口 →
(4) 字体角色、粗细、居中、valign → (5) 边缘差异润色。每次迭代更改少数相关事物;如果分数下降,回退。停止 当:清单中的每个元素都存在(逐区域重新阅读源),lint 0 个错误且无溢出/重叠/偏离主题的警告,并排显示没有错位或错误文本,分数趋于平稳(在 2 次迭代中增益 < 0.005)。如果 draw.io CLI 存在:最后执行一次 T render_preview OUT --engine drawio。
A6 最终确定:images/fig.drawio 是最新构建;导出
T render_preview images/fig.drawio -o "images/fig#drawio.png" --dpi 300。图像文件夹仅包含源文件、.drawio、#drawio.png(+ 工作目录)。报告:路径;原生与嵌入(照片、SVG 层、emoji 占位符);使用/添加的角色;已知偏差;保留源中的拼写错误;如何重新主题化(编辑 theme.yaml → python build.py)以及如何重新缩放。
模式 B — 文字 → 三个可编辑图形原型
阅读段落,清点其主张,构建三种不同的图形系列,检查它们,呈现一张并排对比表;用户选择;您最终确定。
输出位置:文件 drafts/methods.md → drafts/methods_figures/;粘贴的文本 →
./figures/<slug>/;多节/长输入 → 每个段落一个子文件夹 p1_<slug>/ …;用
户命名的文件夹 → 完全使用该名称(--out-dir)。告知用户路径。所有段落共享一个主题(--theme 或在输出根目录编辑 theme.yaml)。
B1 创建框架 + 分析:T new_figures drafts/methods.md [--theme t.yaml] [--split|--whole] [--passages 1,3] 或 T new_figures --text "<段落>" --name rag_pipeline。打印每个段落:字数、方程式、结构线索、三个建议的系列。≤ 350 个单词且无标题 = 一个段落。没有值得制作图形的结构(纯叙述)?如实说明,而不是强行制作图形。
B2 清单(NOTES.md):实体、有序步骤(及其动词)、关系(方向 + 动词)、带单位的数量、限定词、结论 — 每个都附上简短引用。确认系列(参考 §7):默认 = 主体结构的三种布局(详细 / 紧凑概览 / 替代视角);有较强第二结构 → 两个 + 一个;有方程式 → 方程式三人组(除非是附带的)。三者必须在拓扑结构上有所不同(泳道 / 堆叠 / 径向 / 矩阵 / 图表 / 板块),而非样式。对于数学、科学、算法类的段落,优先选择主动演示(填充的矩阵计算、连续坐标/度量空间、贝叶斯板块、因果 DAG),而非静态的要点卡片。
B3 编写 build.py — 三个构建器,每个都获得一个其画布尺寸的新 Diagram:
from figlib import *
INFERRED = ["v1: 序数评分解释段落的定性措辞"] # 报告给用户
ALLOWED_TERMS = ["layer"] # 结构性词汇,非主张
LINT_IGNORE = [] # 故意接受的警告(子字符串)
def v1(d): # 泳道流水线
lane(d, 20, 20, 1060, 175, "离线索引")
chain(d, ["检查报告\n(扫描)", "OCR\n转换为文本", "语义\n分块"], 80, 78, 140, 60,
shapes=["document", None, None], ids=["rep", "ocr", "chk"])
VARIANTS = [dict(key="v1", name="swimlane_pipeline", size=(1100, 400), build=v1,
about="表格的一行描述", caption="使用段落措辞起草的标题。"), ...]
规则:仅使用主题角色,相同类别在所有三个原型中风格相同;同级元素共享尺寸和一个间隙(使用辅助函数,而非手动调整的数字);连接器已附着,反馈为虚线/次要;≤ ~25 个文本项,< ~150 个顶点(否则拆分为两个图形);图形内无标题 — 编写 caption;标签使用段落的措辞(通过删除而非同义词替换来缩短;保留限定词、数字、单位、缩写原文)。解释性装置(评分、边界线、“最佳拟合”、轴排序、文本中未提及的数学事实)仅在 INFERRED 中列出时才可使用。
B4 原型制作、检查、修复(≥ 2 轮):T prototype <段落目录> [--only v2] --note "…" 构建
所有三个原型,渲染,lint,运行忠实度检查,写入 prototypes.png/.md,记录错误/警告/遗漏。查看每个 PNG 和对比表。修复每个 lint 警告或添加一个故意的 LINT_IGNORE;解决每个非段落中的措辞(使用段落的术语重新措辞 · 如果为结构性则使用 ALLOWED_TERMS · 如果为解释性则使用 INFERRED)。检查 lint 无法检查的内容:箭头穿过标签、拥挤的角落、不清晰的层次结构、不平衡的空白、两个原型过于相似、每个箭头方向是否与清单匹配。完成于 0/0/0(或接受的警告)并在全尺寸下保持整洁。
B5 呈现:显示 prototypes.png;每个原型一行说明其重点、其草稿标题、其 INFERRED 项;指出来源的奇怪之处(拼写错误、不一致的名称),而不是默默修复。要求用户选择(或请求更改/组合 → 编辑 build.py,重新运行)。
B6 最终确定:T finalize <段落目录> --pick v2 [--name rag_pipeline] [--clean] → <名称>.drawio
(+ .png) 和 CHOSEN.md(标题,推断)。--clean 删除其他原型但保留 _work/。提供模式 C 以进行特定于出版场所的润色。
模式 C — 为出版增强 .drawio 文件
一次有意识的步骤:理解图形的作用 → 以打印尺寸测量 → 更改它以服务于该作用 → 验证 → 导出。 在问询之前绝不编辑。
C1 开始:T start_enhance path/FIG.drawio [--source-image] [--text] [--artifacts DIR] [--theme]。
它会发现上下文(模式-A 工作目录:源图像、build.py、主题、NOTES;模式-B 段落文件夹:
passage.md、build.py VARIANTS、CHOSEN.md、标题、INFERRED),检测生成后的手动编辑,
推荐一条路线,审计“之前”,写入 <stem>_enhance_work/ (context.md, before_audit.md,
brief.yaml, questions.md, theme.yaml, build_enhanced.py