/* ============================================================================= * common_util_mass_haul_badge.ts * 「최종 누가토량」 배지 — 종단 그래프 **좌측 상단에 겹쳐** 띄우는 한 줄. * * 왜 배지인가(2026-09-06 사용자 지시) — 유토곡선 그래프는 자리를 많이 먹는데, 계획선을 * 만지며 늘 봐야 하는 값은 **마지막 지점의 누가토량 하나**다. 그래서 그 값만 그래프 위에 * 겹쳐 두고, 곡선 자체는 B05 의 유토곡선 패널에서만 펼쳐 본다(B06 에서는 곡선을 뺐다). * * 기준은 **횡단**이다 — 종단 기준 곡선은 계획선만 보는 개략값이라 뒤에 없앨 예정이다 * (2026-09-06 사용자). 값을 넣는 쪽에서 횡단 기준 계열을 골라 넘긴다. * ========================================================================== */ /** 배지에 실리는 값 한 벌 — 유토곡선 계산 결과에서 뽑는다. */ export interface MassHaulBadgeValues { /** 마지막 측점의 누계 토량(㎥). 음수면 부족(토취). */ finalM3: number; cutNaturalM3: number; cutCompactedM3: number; fillCompactedM3: number; surplusM3: number; shortageM3: number; } /** 누가토량 표기 — 천 단위 구분 + 소수점 1자리(유토곡선 요약과 같은 규칙). */ function volume(value: number): string { return `${value.toLocaleString("ko-KR", { maximumFractionDigits: 1 })}㎥`; } export interface MassHaulBadge { /** 그래프 컨테이너(`position: relative`)에 붙일 요소. */ root: HTMLElement; /** 값을 갈아 끼운다. null 이면 배지를 숨긴다(아직 계산 못 한 상태). */ set: (values: MassHaulBadgeValues | null) => void; } /** 유토곡선 계산 결과 한 벌에서 배지 값을 뽑는다 — B05·B06 이 같은 규칙을 쓴다. */ export function badgeValuesFrom(result: { points: ReadonlyArray<{ cumulative_volume_m3: number }>; cut_natural_m3: { soil: number; ripping_rock: number; blasting_rock: number }; cut_compacted_m3: number; fill_compacted_m3: number; surplus_m3: number; shortage_m3: number; }): MassHaulBadgeValues { const cut = result.cut_natural_m3; const points = result.points; return { finalM3: points.length ? points[points.length - 1].cumulative_volume_m3 : 0, cutNaturalM3: cut.soil + cut.ripping_rock + cut.blasting_rock, cutCompactedM3: result.cut_compacted_m3, fillCompactedM3: result.fill_compacted_m3, surplusM3: result.surplus_m3, shortageM3: result.shortage_m3, }; } /** 배지를 만든다. 붙이는 것은 부르는 쪽 몫이다(그래프 컨테이너 첫 자식). */ export function createMassHaulBadge(): MassHaulBadge { const root = document.createElement("span"); root.className = "mass-haul-badge"; root.hidden = true; const caption = document.createElement("em"); caption.textContent = "누가토량"; const value = document.createElement("strong"); root.append(caption, value); return { root, set(values) { if (!values) { root.hidden = true; return; } root.hidden = false; root.classList.toggle("is-over", values.finalM3 < 0); value.textContent = volume(values.finalM3); root.title = [ `절토(자연) ${volume(values.cutNaturalM3)} · 절토(다짐) ${volume(values.cutCompactedM3)}`, `성토(다짐) ${volume(values.fillCompactedM3)}`, values.shortageM3 > 0 ? `부족 ${volume(values.shortageM3)}` : `잉여 ${volume(values.surplusM3)}`, "기준: 횡단", ].join("\n"); }, }; }