Three things, kept in one commit because they touch overlapping hunks of the same two files and this environment has no interactive hunk staging. Splitting them by file would have drawn boundaries that misrepresent what changed. 1. Step 4 of the map editor. A native OpenLayers PointerInteraction turns a drag into a clamped constraint value, the ghost source shows it immediately, and the solver's answer replaces a parallel set of preview layers while the baseline layers are hidden rather than overwritten. Preview requests debounce at 80 ms, pointerup flushes without waiting, and a newer request aborts the one in flight; EditSession decides which answers count. Handle positions come from the clamped value, so a handle stops at its limit instead of following the cursor. Three of the four drag capabilities are live: edge offset, sidewalk width, lane divider. 2. Road edge handles were drawn on the wrong side. offsetLine() offsets counter-clockwise from the direction of travel and sidewalks use `heading + (side === 'left' ? -90 : 90)`, so left is `tangent - 90`; makeRoadHandles() placed the left handle at `tangent + 90`, over the right kerb. Dragging the visually-left handle moved the right edge. Fixed on both sides of the wire, with regression tests that name the sides geographically rather than by axis sign. 3. Roads the junctions geometrically fill are now read-only. The 0.45 cap per reserve made the existing `unavailable` branch unreachable, so a 14.5 m stub between two junctions was offered a 1.5 m editable band with no room for the transitions a road-interval constraint needs. Greying only affects the manifest: constraints already saved against such a road keep being solved, so the geometry output is unchanged and the fixture baselines do not move. Range handles are built and unit-tested but hidden behind `intervalEditingSupported`: compileGeometry() reads neither profile.interval nor profile.transitions, so every edit applies to the whole road and the control would have had no effect. Recorded in research/interval-not-applied.md, which also blocks one PRD acceptance criterion. The ol-ext probe stays in the tree as a manual harness; ol-ext is still not a dependency. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
61 lines
3.1 KiB
Markdown
61 lines
3.1 KiB
Markdown
# 几何编译器不施加 `road-interval` 区间
|
||
|
||
第 4 步手测时发现:区间范围手柄拖动有 ghost 反馈,但松手后几何毫无变化。
|
||
经排查这是服务端的能力缺口,不是客户端拖拽逻辑的问题。
|
||
|
||
## 事实
|
||
|
||
`grep "\.interval\b" src/compile/native-road.js` 返回空。
|
||
|
||
求解器写入了区间与过渡,几何阶段一个都不读:
|
||
|
||
| profile 字段 | solver 写入 | `compileGeometry()` 读取 |
|
||
| --- | --- | --- |
|
||
| `edgeOffsets.left/right` | ✅ | ✅ `centerlineShift`(native-road.js:825) |
|
||
| `widthMeters` | ✅ | ✅ native-road.js:832 |
|
||
| `sidewalkWidths.left/right` | ✅ | ✅ 真实宽度,`sidewalkRing`(native-road.js:1666-1671) |
|
||
| `laneDividerOffsets` | ✅ | ✅ native-road.js:1946 |
|
||
| **`interval`** | ✅ direct-edit-solver.js:274-277、418 | ❌ **零消费者** |
|
||
| **`transitions`** | ✅ direct-edit-solver.js:293、303、322 | ❌ **零消费者** |
|
||
|
||
## 后果
|
||
|
||
1. **每一次直接编辑都作用于整条路。** `anchor.startStation` / `endStation` 被存储、被
|
||
`validateEditDocument()` 校验、被带进 `resolveDirectEditConstraints()` 的结果,然后被忽略。
|
||
2. **区间范围手柄在几何上不可能有效果。** 客户端的 `intervalRangeHandles()`、
|
||
`projectIntervalEnd()`、`coordinateAtStation()` 都正确且有单测,但下游无人接收。
|
||
3. **`transition: 'smoothstep' | 'linear'` 是死字段。** 父任务 design.md 承诺
|
||
「作用于 interval 两端回归基线的过渡段」,实际没有任何过渡。
|
||
|
||
## 已采取的处置
|
||
|
||
`workbench/client/src/edit/flag.ts` 增加 `intervalEditingSupported = false`,
|
||
范围手柄的 UI 据此隐藏。纯逻辑与测试全部保留。
|
||
|
||
理由:一个拖起来有反馈、松手却没效果的控件比没有这个控件更糟——它会持续产生
|
||
bug 报告,并让人怀疑整个编辑器的其余部分。区间生效的那个提交把这个常量翻成 `true` 即可,
|
||
客户端不需要其他改动。
|
||
|
||
## 对验收标准的影响
|
||
|
||
父任务 prd.md 的这条**当前无法通过**:
|
||
|
||
> 区间范围手柄可修改影响区间,两端平滑过渡回基线。
|
||
|
||
`08-26-direct-edit-map-editor/prd.md` 的同名条目同理。这不是客户端欠工,
|
||
而是需要 `compileGeometry()` 具备按 station 施加横断面 profile 的能力。
|
||
|
||
## 若要实现(未排期)
|
||
|
||
用户 2026-08-27 判断影响不大,故未开任务,仅记录。真要做时的形状:
|
||
|
||
1. `native-road.js` 的横断面生成需要接受「沿中心线变化的 profile」而非单一常量宽度。
|
||
目前 `applyDirectEditProfiles()`(约 818-838 行)返回的是整条路一个 `widthMeters`
|
||
和一次 `offsetLine()` 整体平移,没有沿 station 变化的余地。
|
||
2. 区间两端按 `transitions[kind]` 做 smoothstep / linear 插值回基线值。
|
||
3. 车道线、标线、connector、步行带都由横断面派生,必须一并跟随,否则会脱节
|
||
(父任务 research/joint-solver.md 的依赖链)。
|
||
4. `test/fixtures` 基线会变化,需要显式 update-baseline 并人工核对。
|
||
|
||
这是 `compileGeometry` 的实质改动,属已归档的 `direct-edit-solver-api` 任务范围。
|