Files
Aislo/B05_Profile/logic/B05_Profile_Util_Station.ts
T
eomsangdonandClaude Opus 5.5 281c71b55b refactor(logic): 348-1 순수 계산 TS 49 파일 <페이지>/logic/ 로 옮김 — 이름 앞머리 그대로 · 동작 고침 없음
- 옮김: B04 2 · B05 23 · B06 11 · A00_Common/design 13 (9,299 줄) · 이미 logic/ 에 있던 16 파일 그대로
- 안 옮김: 묶음 입구(package.json build:*) · 파이썬 · 순수지만 계산 아닌 8 (보기 결선 · 콜백 · API 타입 · 갈아끼움 알림판)
- import 266 줄(142 파일) · 시험 · 주석 · 짝 표시 문자열 경로 고침 · B05 · B06 CLAUDE.md 짝 줄 logic/
- 시험 도우미 새 경로: 가짜 require 열쇠 · B05()/B06() 부름 · 형제 import 자리 · tsc 묶어 돌리는 시험 · 정규식 · rglob
- design_view_cross 줄 한도 시험 685 → 688 (긴 import 경로 줄바꿈 3줄)
- 확인: typecheck 0 · 서버 묶음 넷(corridor · server-calc · b07-extent · b07-note) 다시 지어 코드 같음(//#region 경로 주석만 다름) · 관련 시험 270 파일 — 남은 실패 19 는 옮기기 전과 같음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
2026-10-16 13:44:50 +09:00

71 lines
3.3 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.
/* =============================================================================
* B05_Profile_Util_Station.ts
* 측점 표기 공용 유틸 — 측점번호+잔여거리 ↔ 누가거리(chainage) 변환·서식.
*
* 구조물 위치는 저장은 누가거리(m)로 하되 **화면 입출력은 측점번호+잔여거리**로 한다
* (2026-08-17 사용자 확정). 근거: 중심선측량 측점 20m + 구조물 지점 보조말뚝
* (지식DB 설계제원_총괄 §8). 표기 예: "3+18" = 측점 3번에서 18m — 화면 표기는
* `common_util_station.stationLabel` 한 벌(거리는 정수로 보이기만 · 값은 소수).
*
* 변환식은 비정규 측점 UI(`B05_Profile_UI_IrregularStations.ts`)와 같다 —
* chainage_m = 측점번호 × 측점간격(m) + 잔여거리(m). 측점간격은 호출 시점에 받는다
* (프로젝트 설정에 따라 바뀌므로 상수로 굳히지 않는다).
* ========================================================================== */
/** 측점번호 X + 잔여거리 XX(m) → 누가거리(m). */
export function stationToChainage(station: number, remainder: number, intervalM: number): number {
return station * intervalM + remainder;
}
/** 누가거리(m) → 측점번호 + 잔여거리. 잔여거리는 [0, 간격) 범위로 정규화한다. */
export function chainageToStation(
chainageM: number,
intervalM: number,
): { station: number; remainder: number } {
if (!(intervalM > 0)) return { station: 0, remainder: Math.max(0, chainageM) };
const clamped = Math.max(0, chainageM);
let station = Math.floor(clamped / intervalM);
let remainder = clamped - station * intervalM;
// 부동소수 잔여가 간격과 사실상 같으면 다음 측점의 0으로 올린다 (19.999… → 1+0.0).
if (intervalM - remainder < 0.005) {
station += 1;
remainder = 0;
}
return { station, remainder };
}
/** "3+18.0" · "3 + 18" · "76.5"(누가거리 직접) 입력을 누가거리로 해석한다.
* 해석 불가하면 null — 호출부가 칸을 붉히고 진행을 멈춘다. */
export function parseStationText(text: string, intervalM: number): number | null {
const trimmed = text.trim();
if (!trimmed) return null;
const match = /^(\d+)\s*\+\s*(\d+(?:\.\d+)?)$/.exec(trimmed);
if (match) {
const chainage = stationToChainage(Number(match[1]), Number(match[2]), intervalM);
return Number.isFinite(chainage) ? chainage : null;
}
const direct = Number(trimmed);
return Number.isFinite(direct) && direct >= 0 ? direct : null;
}
/** 구조물 측점과 겹쳤다고 볼 거리(m) — 이보다 가까우면 규칙 측점을 지운다. */
export const STATION_MERGE_TOLERANCE_M = 0.1;
/**
* 구조물 측점과 **겹치는 규칙 측점을 지운다**(2026-09-06).
*
* 구조물은 보조말뚝을 박아 그 자리가 정본 측점이 된다. 규칙 격자(20m)와 0.1m 안에서
* 만나면 세로선·라벨이 두 겹으로 겹쳐 읽히지 않으므로 구조물 쪽만 남긴다.
*/
export function dropStationsNear<T extends { chainage_m: number }>(
stations: readonly T[],
anchors: ReadonlyArray<{ chainage_m: number }>,
toleranceM: number = STATION_MERGE_TOLERANCE_M,
): T[] {
if (anchors.length === 0) return [...stations];
return stations.filter(
(station) =>
!anchors.some((anchor) => Math.abs(anchor.chainage_m - station.chainage_m) <= toleranceM),
);
}