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
@@ -64,26 +64,38 @@ function standardEdges(edges: { left: Edge; right: Edge }, design: CrossDesign)
};
}
type PaveMark = { type_id: string; start_m?: number | null; end_m?: number | null };
/** 포장층 범위 표식(종류 · 시작 · 끝)이 그 노면공인가 — 겹친 노면공 가름. */
function isPaveLayerOf(data: DOMStringMap, mark: PaveMark): boolean {
const [low, high] = [mark.start_m ?? 0, mark.end_m ?? 0].sort((a, b) => a - b);
return (
data.paveType === mark.type_id &&
Math.abs(Number(data.paveStart) - low) < 0.01 &&
Math.abs(Number(data.paveEnd) - high) < 0.01
);
}
/**
* 고른 노면공의 포장층만 `is-active`(152-5) — 범위 표식(종류 · 시작 · 끝)이 맞는 층. 겹친 노면공은 하나만.
* `mark` 없음 = 모두 풂.
*/
export function highlightPavement(
root: ParentNode,
mark?: { type_id: string; start_m?: number | null; end_m?: number | null },
): void {
const [low, high] = [mark?.start_m ?? 0, mark?.end_m ?? 0].sort((a, b) => a - b);
export function highlightPavement(root: ParentNode, mark?: PaveMark): void {
for (const layer of root.querySelectorAll<SVGElement>(".b06-chart__pavement")) {
const { paveType, paveStart, paveEnd } = layer.dataset;
const same =
!!mark &&
paveType === mark.type_id &&
Math.abs(Number(paveStart) - low) < 0.01 &&
Math.abs(Number(paveEnd) - high) < 0.01;
layer.classList.toggle("is-active", same);
layer.classList.toggle("is-active", !!mark && isPaveLayerOf(layer.dataset, mark));
}
}
/** 누른 자리의 포장층 → 그 노면공(189-1 · 형상 누름 = 목록 고름) · 범위 표식 없는 층 · 밖 = undefined. */
export function pavementAt<T extends PaveMark>(
target: EventTarget | null,
marks: ReadonlyArray<T>,
): T | undefined {
const layer = (target as Element | null)?.closest?.<SVGElement>(".b06-chart__pavement");
if (!layer?.dataset.paveType) return undefined;
return marks.find((mark) => isPaveLayerOf(layer.dataset, mark));
}
/**
* 포장층 — 노면선(= 포장 윗면) 아래로 두께만큼. 노면공 범위(`section.pavement`)면 그 두께 · 무늬 ·
* 확폭 켬/끔(여러 건은 아래로 차례로 쌓음) · 없으면 카드 포장 켬일 때 표준 두께 채움만.