Files
Aislo/B06_Section/B06_Section_UI_Cross_Detail_Pick.ts
T

69 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* 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<Record<string, { selector: string } | { zone: LineZone }>> = {
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");
});
}