/* ============================================================================= * B06_Section_UI_Cross_Detail_Pick.ts * [상세] 수량표 칸 누름 → 그림 복제 안 그 항목 강조(150-4). * * 칸 키 → 그림 요소: 면적 띠(깍기 · 쌓기) · 사토장 선 · 중심 십자(높이 넷) · 보라 판정 선 구역 * (사면 · 노면 · 측구). 판정 선은 화면 좌표라 십자(x(0)) · `data-px-per-m` 로 m 로 되돌려 * `crossZoneAt` 으로 가름 — 측구 = 단면유형을 성토 양쪽으로 둬도 절토로 남는 자리. * 편책 · 제근 · 표토제거는 늘 빈칸 = 누름 없음(짝 표에서 뺌). * ponytail: 판정 선의 구조물 트림 여부는 복제에서 모름 → 자리로만 가름(트림 선은 놓인 구역을 따름). * ========================================================================== */ import type { CrossDesign } from "./B06_Section_Api_Fetch"; import { crossZoneAt, type CrossZone } from "./B06_Section_UI_Cross_Design_Pick"; type LineZone = CrossZone | "ditch" | "cut_slope"; /** 칸 키 → 강조 대상(선택자 = 그 요소 · zone = 그 구역 판정 선). 없는 키 = 누름 없음. */ export const DETAIL_TARGETS: Readonly> = { ground: { selector: "g.b06-chart__center" }, planned: { selector: "g.b06-chart__center" }, fill: { selector: "g.b06-chart__center" }, cut: { selector: "g.b06-chart__center" }, cut_soil: { selector: "g.b06-chart__area--cut_soil" }, cut_rock: { selector: "g.b06-chart__area--cut_rock" }, embankment: { selector: "g.b06-chart__area--fill" }, spoil_fill: { selector: ".b06-chart__spoil-fill" }, ditch_soil: { zone: "ditch" }, ditch_rock: { zone: "ditch" }, benching: { zone: "fill" }, grading_fill: { zone: "fill" }, tree_removal_fill: { zone: "fill" }, fill_seeding: { zone: "fill" }, grading_cut: { zone: "cut_slope" }, tree_removal_cut: { zone: "cut_slope" }, cut_spraying: { zone: "cut_slope" }, road_compaction: { zone: "road" }, }; /** 판정 선 하나의 구역 — 측구 · 절토 사면 · 성토 · 도로. */ export function lineZone(design: CrossDesign, offsetM: number): LineZone { const { zone } = crossZoneAt(design, offsetM); if (zone !== "cut") return zone; const ditch = crossZoneAt({ ...design, section_mode: "both_fill" }, offsetM).zone === "cut"; return ditch ? "ditch" : "cut_slope"; } /** 그림 복제 안 강조 — 묻어 온 `.is-active` 를 지우고 `key` 대상만 켬(null = 모두 끔). */ export function highlightDetail(svg: Element, design: CrossDesign | undefined, key: string | null) { svg.querySelectorAll(".is-active").forEach((el) => el.classList.remove("is-active")); const target = key ? DETAIL_TARGETS[key] : undefined; if (!target) return; if ("selector" in target) { svg.querySelectorAll(target.selector).forEach((el) => el.classList.add("is-active")); return; } const center = svg.querySelector("g.b06-chart__center line"); const ppm = Number(svg.getAttribute("data-px-per-m")); if (!design || !center || !(ppm > 0)) return; const x0 = Number(center.getAttribute("x1")); svg.querySelectorAll(".b06-chart__design-hit").forEach((hit) => { const xs = (hit.getAttribute("points") ?? "") .trim() .split(/\s+/) .map((pair) => Number(pair.split(",")[0])); const mid = (xs[0] + xs[xs.length - 1]) / 2; // x = 왼쪽 여백 + (최대 offset − offset) × ppm → offset = (x(0) − x) ÷ ppm if (lineZone(design, (x0 - mid) / ppm) === target.zone) hit.classList.add("is-active"); }); }