chore(road-editor): plan direct-edit task tree and add client test infra

Planning: parent design.md becomes the single authoritative contract
(constraint model with 6 kinds, handle manifest, coordinate/unit
layering, preview sequencing, storage layout and lazy migration, area
config snapshot, API contract). Work is split into four independently
verifiable child tasks with per-step gates and rollback points.

Test infra: pin vitest 4.1.11, add test:client:unit for client pure
logic, extend prettier globs to root *.ts so vitest.config.ts is checked.

Add .gitignore: the repo had none, so inputs/, outputs/, workbench-data/
and the client build output were untracked rather than ignored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-26 17:33:42 +08:00
parent 7dc7ede2b8
commit 93f09e399e
41 changed files with 2001 additions and 4 deletions

View File

@@ -0,0 +1,56 @@
# 道路与路口的联合求解边界
## 当前依赖已经存在
`compileGeometry()` 目前以同一 `model``junctionPlans` 依次生成道路面、车道中心线、边缘线、控制设施、中心线/车道标线、步行带、connector 与路口面。它们不是孤立图层:
```text
道路横断面 / 进口尺寸
-> 道路面、步行带、车道偏移
-> 车道线与 connector
-> 路口边界、cutback、转弯圆角
-> 停止线、斑马线、箭头与诊断
```
普通路口目前由 `compileJunctionPlans()` 的 approach、`cutbackMeters` 和 boundary 驱动;复合路口则由 `junctionTemplates.clusters``complex-junction.js` 的 core radius、进口包络和角部岛生成。直接操纵不能在最终 `roadSurface` / `intersectionSurface` 上独立移动顶点,否则会破坏这条依赖链。
## 提议的新增阶段
```text
OSM -> compileRoadModel -> 现有 v1 参数覆盖
-> resolveDirectEditConstraints
-> editableRoadModel + editableJunctionPlans
-> 既有图层编译器(逐步接收扩展参数) -> GeoJSON
```
`resolveDirectEditConstraints` 的职责是:
1.`road-station` / `road-interval` 约束整理为道路横断面 profile
2. 将道路末端 profile 与 `junction-approach` 约束合并,构建一致的进口截面;
3.`junction-corner` / `junction-cutback` 约束生成可验证的 junction plan
4. 在违反最小车道宽、相邻道路相交、交叉口连接线包络等不变量时,返回明确冲突而非偷偷修复;
5. 输出一个 handle manifest使地图手柄能从同一语义模型读取位置、可拖动方向、受影响对象和可见的值。
道路 interval 的默认范围由求解器决定,而不是 UI 预设像素:避开两端的 junction cutback优先取拖拽站点两侧可用长度的有限比例若道路过短或与另一个约束重叠预览返回可调整范围或冲突信息。
## 交互预览
拖动不触发文件写入或完整工作台重建。地图维护 `EditSession`
```text
pointer down: 读取 handle 的 semantic anchor创建 draft command
pointer move: 投影鼠标位置 -> 立即更新 ghost 与 draft constraint -> 防抖服务端预览求解
pointer up: 验证成功则压入本地命令栈;失败保留提示并回退到上一个有效预览
save: 批量持久化 constraints + operations
compile: 用已保存文档运行权威全量编译
```
浏览器 ghost 仅包含控制柄、辅助线与半透明预估轮廓,不承担权威道路几何。服务端复用正式编译的约束求解逻辑,返回受影响的少数 OpenLayers source道路面、步行带、路口、标线、connector 和 handles客户端替换它们而非卸载 `MapCanvas` 或重新创建 `Map`。这与现有图层可见性和选择修复一致。
## 约束不变量
- 单车道最小宽度,例如 2.4m;道路横断面总宽度等于车道、边缘和步行带之和。
- 一个站点的左右外缘不得交叉;相邻 profile 之间必须有可计算的过渡。
- 进口截面必须在路口 cutback 处与路口边界连续。
- connector、停止线和人行横道必须保持在所属道路/路口可用面内,否则产生阻塞性诊断。
- 普通路口与复杂 cluster 不共用相同低层约束:前者锚定 node后者锚定 cluster 和 arm避免将 cluster 缩减为多个互相冲突的普通路口编辑。