主题
build · 生成场景
把几何、画法、主题合成场景:零件的 key 在这里规划,样式在这里解析成具体值。
导出
buildScene、BuildOptions、ROOT_KEY。
签名
ts
interface BuildOptions {
registry: DrawRegistry;
theme: ResolvedTheme;
overlay?: StyleOverlay;
measurer?: TextMeasurer; // 传 layout 用的同一个,避免「量的盒子」与「画的文字」两套口径
}
function buildScene(
input: DrawDocument | BoundDocument,
layout: LayoutResult,
options: BuildOptions,
): Result<Scene>;新代码应传入 BoundDocument。为兼容 v1,也可以传原始文档,build 内部会先 bind。
这一步做四件事
| 事 | 口径 |
|---|---|
| key 规划 | sink.key(suffix) 在固定前缀下分配稳定 key,重名才追加 #n |
| 样式解析 | 逐字段覆盖:默认画法 → typeStyles → 排版量 → document.styles[...] → overlay |
| 派发画法 | registry.shapeSpec(type).render(ctx) / edgeSpec(type).render(ctx),形状自己摆零件 |
| 收集诊断 | SceneSink.warn 是 render 期诊断的唯一出口,和 build 自己的诊断汇到同一个数组 |
绘制顺序由结构推导
容器背景(树先序)→ 边(含端点 marker)→ 叶子节点 → 边标签。
这条顺序让「线在节点下面、标签在节点上面」自动成立,不需要 z-index;host 只按 Scene 顺序输出。 marker 的大小固定 8,落点在 route 两端并自动转向(角度由首末折段推)。
样式合流
两类元素各有底座,之后被更靠后的声明逐字段覆盖:
| 元素 | 底座 | 覆盖顺序 |
|---|---|---|
| 节点 | fill=surface / stroke=border / strokeWidth=1 / color=text | typeStyles → 排版量 → 文档样式 → overlay |
| 边 | fill=none / stroke=edge / strokeWidth=1 / color=text | typeStyles → 排版量 → 文档样式 → overlay |
{ token: "name" } 查 ResolvedTheme.tokens;查不到 → 退回到更早那一层的同名字段(最外层就是上面那两份底座)+ UNKNOWN_TOKEN(不抛)。 回落必须仍然给出一个可用值:把槽位丢掉会让形状掉到 SVG 的隐式默认(fill 黑),比不回落更难查。
只做 / 绝不做
只做:把「绝对几何 + 画法 + 主题」翻译成场景树;入参只读。 绝不做:算坐标(几何来自 LayoutResult)、读宿主、吞掉未知类型。
- 未知 shape / edge / marker 的回落已在 bind 处理,build 不重复发同类诊断。
- shape / edge 使用
BoundDocument中已经归一化的 props,不再重复执行 schema。 EdgeSpec.labels缺省为 auto:build 读取LayoutResult.labels[edge:id]并统一绘制。node/edge在LayoutResult里没有几何 →LAYOUT_FAILED(error),整段ok: false(这是唯一会让 build 失败的情况)。- 形状 / 边的 render 拿不到
document、LayoutResult、别的元素(L2 不变量,见 六段管线)。
相关 BDD
C11(未知 shape / port 回落 + 诊断)· C12(最小文档 → golden)· C13(两次 build 相等)· C14(复合形状子 key)· C23(不改入参)。