fix(B06·B05): 노면공 고름 세 갈래 · 형상 누름 = 목록 고름 · 카드 하나 = 그 측점(189-1)

- ② 카드 안 노면공 포장층 누름 = 목록 고름과 같은 길(범위 카드 전부 · 포장층 강조) — 카드 고름보다 먼저 가로챔
- ③ 카드 하나 누름 = 연 노면공 · 범위 강조 풂 · 그 카드 하나(165-1 노면공 지킴 · 167-3 재누름 그대로 없앰)
- 카드 고름 세션 = 그 측점(구조물 id 는 목록 · 알약 · 넘김이 고른 것만)
- 종단: 구조물 넘김이면 측점선 · 3D 띠 안 켬
- 새 시험 test_189_1_pave_pick · test_165 · test_188_1 기대값 41번 규칙으로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
This commit is contained in:
2026-10-10 13:21:23 +09:00
co-authored by Claude Opus 5.5
parent e744da8af1
commit 9e69bec512
12 changed files with 301 additions and 72 deletions
@@ -123,6 +123,8 @@ export interface B06StructuresPanel {
selectionAt: (chainageM: number | null) => DesignSelection;
/** 다음 카드 고름이 측점보다 먼저 열 구조물 — 알약 · 넘김값이 누른 것(null = 측점 규칙대로). */
prefer: (structureId: string | null) => void;
/** 지금 카드 고름이 먼저 열 구조물(목록 · 알약 · 넘김) — 없으면 null(카드만 고름 · 189-1). */
preferred: () => string | null;
/** 구조물 하나를 폼에 올리고 그 카드로 간다 — 알약을 누른 길(목록 클릭과 같은 흐름). */
selectStructure: (structureId: string) => void;
/** 지금 목록의 구조물 정본(미저장분 포함) — 카드 찾기에 쓴다. */
@@ -164,8 +166,6 @@ export function createB06StructuresPanel(deps: B06StructuresPanelDeps): B06Struc
markTypes.find((type) => type.type_id === entry.type_id)?.group !== "G";
/** 다음 카드 고름이 열 구조물 — 같은 측점에 관이 있어도 누른 것이 먼저다(52-15). */
let preferredId: string | null = null;
/** 폼에 열린 구조물 — 범위 안 빈 카드를 눌러도 연 노면공을 지킴(165-1). */
let openId: string | null = null;
/** 그 구조물로 갈 카드 측점 — 구간형은 범위 안 카드 중 기준점에 가까운 것(경계 기준점이 범위 밖 카드로 안 감 · 165-2). */
const focusOf = (structure: StructureInstance): number => {
const anchor = structureAnchorM(structure);
@@ -209,7 +209,6 @@ export function createB06StructuresPanel(deps: B06StructuresPanelDeps): B06Struc
}
},
onSelect: (structure) => {
openId = structure?.structure_id ?? null;
if (!structure) {
deps.markStructure?.(null); // 풂 = 알약도 끔(53-2)
return;
@@ -547,24 +546,15 @@ export function createB06StructuresPanel(deps: B06StructuresPanelDeps): B06Struc
prefer: (structureId) => {
preferredId = structureId;
},
preferred: () => preferredId,
selectStructure: (structureId) => section.selectById(structureId),
showAtChainage: (chainageM) => {
currentChainageM = null;
const selection: DesignSelection = { chainageM, structureId: preferredId };
// 계곡 통과 시설이 먼저다 — 같은 측점에 구조물 정본 항목이 겹쳐도 횡단도에
// 그려지는 것은 이쪽이다(순서 · 거리는 `resolveSelection` 한 곳).
// 카드 하나 고름 = 그 측점 기준 — 연 노면공은 풂(189-1 · 노면공은 덮어 열지 않음 152-5)
const hit = resolveSelection(selection, pipeFacilities, structures, coverable);
// 범위 안 빈 카드(관 · 덮는 구조물 없음) = 연 노면공 그대로 · 카드만 옮김(165-1)
const open = structures.find((entry) => entry.structure_id === openId);
if (
chainageM !== null &&
hit.pipeChainageM === null &&
hit.structureId === null &&
open &&
!coverable(open) &&
coversChainage(open, chainageM)
)
return { chainageM, structureId: openId };
// 폼은 위에서 푼 값으로 엶 — 공용 `selectAt` 은 노면공을 덮어 엶(152-5)
if (hit.pipeChainageM !== null) {
currentChainageM = hit.pipeChainageM;