B05 계획 유토곡선과 B06 정식 유토곡선이 같은 엔진을 쓰도록 모듈을 옮겼다.
- B06_wf3_ProfileCross_UI_MassHaul{,_Balance,_Balance_View,_Balloon,_Curve,
_Settle,_View}.ts + Style_MassHaul.css → common_util/common_util_mass_haul*.
- common_util_mass_haul_types.ts 신설: GroundType/EarthworkConversion/
HaulEquipmentLimit/BalloonOffsets 정의처를 한 곳으로 모으고 B06 Api_Fetch가
재수출. 엔진 입력은 페이지 API 타입 대신 구조적 부분집합으로 받는다.
- common_util_svg.ts 신설: svgElement/svgText/L/stationLabel/
inferStationInterval 이관, B06 _UI_Section_Common은 재수출로 경로 유지.
- createMassHaulChart에 MassHaulAxis 인자 추가 — 종단 렌더러 상수 의존을
걷어내고 호출한 쪽이 X축(누가거리 최댓값·좌우 여백)을 주입한다.
동작 무변경. npm run typecheck 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
64 lines
2.6 KiB
TypeScript
64 lines
2.6 KiB
TypeScript
/* =============================================================================
|
|
* common_util_svg.ts
|
|
* SVG 도면 렌더러가 공유하는 최소 유틸리티 — 요소 생성·텍스트·측점 표기.
|
|
*
|
|
* B06 종·횡단 렌더러가 쓰던 것을 유토곡선 모듈 공용화(2026-08-03)에 맞춰 옮겼다.
|
|
* B05 계획 유토곡선과 B06 정식 유토곡선이 **같은 표기 규칙**을 쓰게 하는 것이 목적이며,
|
|
* B06 쪽은 `_UI_Section_Common`이 그대로 재수출해 기존 임포트 경로를 유지한다.
|
|
* ========================================================================== */
|
|
|
|
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
|
|
|
|
export const SVG_NS = "http://www.w3.org/2000/svg";
|
|
|
|
export function L(key: keyof typeof ui_locales): string {
|
|
return ui_locales[key][currentLanguageIndex];
|
|
}
|
|
|
|
export function svgElement<K extends keyof SVGElementTagNameMap>(
|
|
tag: K,
|
|
attributes: Record<string, string | number> = {},
|
|
): SVGElementTagNameMap[K] {
|
|
const element = document.createElementNS(SVG_NS, tag);
|
|
Object.entries(attributes).forEach(([key, value]) => element.setAttribute(key, String(value)));
|
|
return element;
|
|
}
|
|
|
|
export function svgText(
|
|
value: string,
|
|
attributes: Record<string, string | number>,
|
|
): SVGTextElement {
|
|
const text = svgElement("text", attributes);
|
|
text.textContent = value;
|
|
return text;
|
|
}
|
|
|
|
/** 측점 간격을 실제 측점 배열에서 되짚는다(최빈 간격). 라벨 표기의 분모다. */
|
|
export function inferStationInterval(stations: Array<{ chainage_m: number }>): number {
|
|
const counts = new Map<number, number>();
|
|
for (let index = 1; index < stations.length; index += 1) {
|
|
const difference = stations[index].chainage_m - stations[index - 1].chainage_m;
|
|
if (difference <= 0) continue;
|
|
const rounded = Math.round(difference * 10) / 10;
|
|
counts.set(rounded, (counts.get(rounded) ?? 0) + 1);
|
|
}
|
|
return (
|
|
[...counts.entries()].sort(
|
|
([intervalA, countA], [intervalB, countB]) => countB - countA || intervalB - intervalA,
|
|
)[0]?.[0] ?? 1
|
|
);
|
|
}
|
|
|
|
/** 측점 라벨 — "STA" 접두사 없이 `측점번호+잔여거리`(예: `4+0.0`). */
|
|
export function stationLabel(chainage: number, interval: number): string {
|
|
const safeInterval = interval > 0 ? interval : 1;
|
|
let stationNumber = Math.floor((chainage + 1e-6) / safeInterval);
|
|
let remainder = chainage - stationNumber * safeInterval;
|
|
if (Math.abs(remainder) < 0.05) remainder = 0;
|
|
if (remainder >= safeInterval - 0.05) {
|
|
stationNumber += 1;
|
|
remainder = 0;
|
|
}
|
|
return `${stationNumber}+${remainder.toFixed(1)}`;
|
|
}
|